# Setup

Wrap your wallet provider with `withYodl7702` and pass the result wherever you'd pass the wallet provider.

```tsx
import { signAuthorizationViaProvider, withYodl7702 } from '@yodlpay/react-native-eip-7702-provider';
import { YodlProvider, YodlPayment } from '@yodlpay/react-native';

const provider = withYodl7702(walletProvider, {
  // The only thing you must supply — no wallet signs 7702 authorizations the same way.
  signAuthorization: signAuthorizationViaProvider(walletProvider),
});

<YodlProvider sdk={sdk} provider={provider} address={address} chainId={chainId}>
  <YodlPayment qrData={qrData} />
</YodlProvider>;
```

That is the whole configuration for an integration against Yodl's hosted UI. `chains` defaults to Yodl's own bundler on the chains Yodl sponsors gas for, gas pricing defaults to asking that bundler, and chain reads go over the wallet you already passed in.

The wrapper reads the account and chain id from each `wallet_sendCalls` bundle, both supplied by the Yodl UI. The account implementation is the canonical SimpleAccount on EntryPoint 0.8 — used as both the delegated account logic and the authorization's delegate target, so the two can't drift. Sponsorship uses the standard [ERC-7677](https://eips.ethereum.org/EIPS/eip-7677) paymaster carried in the bundle's capabilities.

## Using Yodl's bundler

The default is `yodlHostedChains()`, which names the host that already sponsors the payment — the same URL the Yodl UI attaches to every bundle as `capabilities.paymasterService.url`. There is no bundler account, no API key and no per-op cost, because Yodl's pricing sponsors L2 gas.

Pass it explicitly when you'd rather see the choice at the call site, or narrow it:

```tsx
import { withYodl7702, yodlHostedChains } from '@yodlpay/react-native-eip-7702-provider';

const provider = withYodl7702(walletProvider, {
  chains: yodlHostedChains([8453]),   // Base only; omit the argument for every sponsored chain
  signAuthorization: signAuthorizationViaProvider(walletProvider),
});
```

If you already have a `createYodlClient(...)` instance, pass it as `yodl` and the wrapper follows it — its `baseUrl` (so a staging build submits to staging's bundler, where `yodlHostedChains()` is production-only), its chains (the ones Yodl sponsors gas on — a default instance also reads on mainnet, which stays out so a mainnet bundle fails with `5710` rather than building an operation the user would have to fund), its RPC proxy as `rpcUrl` where it names one, and its `getAuthHeaders`:

```tsx
const provider = withYodl7702(walletProvider, {
  yodl,
  signAuthorization: signAuthorizationViaProvider(walletProvider),
});
```

Explicit `chains` or `getAuthHeaders` beside `yodl` win over what the instance supplies. The `getAuthHeaders` inherited from the instance keeps sdk-core's promise: it is only sent to requests on `baseUrl`'s origin, so pairing `yodl` with your own bundler, RPC or paymaster never hands the host session token to a third party. Pass `getAuthHeaders` explicitly when you want credentials on your own endpoints.

A chain Yodl does not sponsor throws rather than being handed an invented endpoint. Mainnet is deliberately excluded — on L1 gas is bundled into the payment rather than sponsored, so a mainnet bundle fails loudly with EIP-5792 `5710` instead of quietly building a UserOperation the user has to fund.

:::note
**What the default delegates.** The bundler receives the signed UserOperation and the signed EIP-7702 authorization, decides whether and when to submit, and is the only source of the settlement status reported back through `wallet_getCallsStatus`. On the default, that host is Yodl's. The sponsored-chain list also ships inside the package, so Yodl adding a corridor reaches you as a package upgrade rather than a config edit.
:::

## Bringing your own bundler

Pass `chains` entries directly and none of your traffic touches Yodl:

```tsx
const provider = withYodl7702(walletProvider, {
  // List only the chains you can actually serve the fallback on.
  chains: [
    {
      id: 8453,
      bundlerUrl: 'https://api.pimlico.io/v2/8453/rpc?apikey=YOUR_KEY',
      // rpcUrl is optional — see "Chain reads" below.
    },
  ],
  signAuthorization: signAuthorizationViaProvider(walletProvider),
});
```

:::note
`chains` here is independent of the SDK's `supportedChainIds`. This list is where the *fallback* can run — it should cover only chains you have a bundler for. A bundle on a chain outside this list fails with EIP-5792 code `5710`. An explicitly empty array is rejected rather than treated as an omission.
:::

## Gas pricing

[ERC-7769](https://eips.ethereum.org/EIPS/eip-7769) standardises the ERC-4337 JSON-RPC surface and deliberately leaves gas price out of it, so the right value is **bundler policy, not chain state** — and there is no vendor-neutral way to compute it. Pimlico serves a tiered oracle; Alchemy's Rundler expects a hand-assembled base fee plus a per-chain-buffered priority fee.

**You usually need to do nothing.** By default the wrapper asks the bundler you configured:

1. `pimlico_getUserOperationGasPrice` against that chain's `bundlerUrl`, taking the `fast` tier.
2. If the bundler doesn't implement that method, an EIP-1559 estimate from the chain, **doubled** — the same buffer viem applies for a bundler it knows nothing about.

Only an *unsupported-method* reply falls through to step 2. A real failure — auth, rate limit, network — propagates instead, because pricing against a bundler that is refusing to talk produces a submission rejected for an unactionable reason.

**Override it when your bundler prescribes something else**, notably Rundler, whose prechecks the generic estimate does not satisfy. The hook receives the bundle's chain id:

```tsx
estimateUserOperationFees: async (chainId) =>
  // e.g. Pimlico: (await pimlicoClient.getUserOperationGasPrice()).fast
  getYourBundlerGasPrice(chainId),

// @error: Never return 0n. Sponsorship covers the cost, not the gas-price fields.
```

`maxFeePerGas` is a **refundable ceiling** — a paymaster or refund covers the difference — so returning the fast tier never overcharges.

:::warning
Even paymaster-sponsored operations need **real, non-zero** fees. Sponsorship covers the cost, not the gas-price fields, so never return `0n`. A stale cached price below the bundler's floor fails the same way.
:::

## Chain reads

The wrapper makes exactly three chain reads: the EOA's **pending nonce** for the authorization, **`eth_getCode`** for delegation status, and the EntryPoint's **`getNonce`** per UserOperation. All three are ordinary node methods.

Omit `rpcUrl` and they go over the wrapped wallet's own EIP-1193 transport. The wallet is already connected to a node, so there is no second endpoint to configure or to drift out of step with the wallet mid-payment. Supply `rpcUrl` when you'd rather not depend on the wallet vendor's node — it always takes precedence.

Two things to know if you omit it:

* **EIP-1193 carries no per-request chain.** Reads land on whichever chain the wallet is currently on, so the wrapper checks the wallet's `eth_chainId` against the bundle's chain once per bundle and refuses on a mismatch with `-32602`. Switch the wallet before sending — which any EIP-5792 caller already does.
* **`blockTag: 'pending'` is the one read a wallet may refuse.** A wallet that method-whitelists its node proxy can reject it. That fails loudly and names `rpcUrl`, rather than retrying at `latest` — which could sign an authorization for an already-used nonce.

## Authorization signing

There is **no universal** way to sign an EIP-7702 authorization across wallets. Some expose `wallet_signAuthorization` over EIP-1193, some only a raw signer, and embedded wallets sign through their own SDK. The wrapper doesn't guess — you supply `signAuthorization`, receive the unsigned request, and return the signed authorization however your wallet does it.

**Try the built-in helper first.** For wallets that sign over EIP-1193:

```tsx
import { signAuthorizationViaProvider, withYodl7702 } from '@yodlpay/react-native-eip-7702-provider';

const provider = withYodl7702(walletProvider, {
  signAuthorization: signAuthorizationViaProvider(walletProvider),
});
```

**If the wallet doesn't sign over EIP-1193**, sign through its SDK instead:

```tsx
signAuthorization: ({ account, chainId, contractAddress, nonce }) =>
  yourWallet.signAuthorization({ address: account, chainId, contractAddress, nonce }),
```

The wrapper always fetches the `nonce` itself — over the chain's `rpcUrl` if one is configured, otherwise over the wallet's own transport — so the authorization can't go stale. You only sign.

### Picking a signing helper

`signAuthorizationViaProvider` is a **discovery convenience**. It tries `wallet_signAuthorization`, then falls back to raw `secp256k1_sign` — but only when the first is genuinely unsupported. Real errors such as user rejection surface as-is and are never masked.

Once you know which method your wallet uses, prefer the single-strategy primitive so failures stay precise:

| Helper | Method |
| --- | --- |
| `signAuthorizationViaWalletRpc(provider)` | `wallet_signAuthorization` only |
| `signAuthorizationViaSecp256k1(provider)` | raw `secp256k1_sign` only |
| `signAuthorizationViaProvider(provider)` | the former, falling back to the latter on unsupported-method |
