# Reference

## `withYodl7702(provider, options)`

Returns an EIP-1193-compatible provider wrapper.

| Option | Type | Required | Description |
| --- | --- | --- | --- |
| `chains` | `{ id: number; bundlerUrl: string; rpcUrl?: string }[]` | No | Chains where the wrapper serves the fallback. Defaults to [`yodlHostedChains()`](#yodlhostedchainschainids). An explicitly empty array is rejected. See [using Yodl's bundler](/sdk/eip-7702-provider/setup#using-yodls-bundler) |
| `yodl` | `YodlLike` | No | A `Yodl` from `@yodlpay/sdk-core`. Defaults `chains` to the chains the instance reads on that Yodl sponsors (never mainnet), routed to its `baseUrl`'s bundler with its RPC proxy as `rpcUrl` where it names one, and `getAuthHeaders` to `config.getAuthHeaders`, sent only to `baseUrl`'s origin. Explicit `chains` / `getAuthHeaders` win. See [using Yodl's bundler](/sdk/eip-7702-provider/setup#using-yodls-bundler) |
| `estimateUserOperationFees` | `(chainId: number) => Promise<{ maxFeePerGas: bigint; maxPriorityFeePerGas: bigint }>` | No | Overrides the built-in pricing, which asks the configured bundler. See [gas pricing](/sdk/eip-7702-provider/setup#gas-pricing) |
| `signAuthorization` | `(request: { account; chainId; contractAddress; nonce }) => Promise<SignedAuthorization>` | Yes | Signs the EIP-7702 authorization. No universal wallet method exists, so this one has no default. See [authorization signing](/sdk/eip-7702-provider/setup#authorization-signing) |
| `foreignDelegations` | `Record<Address, 'repoint' \| EIP1193Provider>` | No | What to do with an account delegated to another implementation, per delegate: hand its bundles, capabilities and status to an EIP-5792 provider of your own, or re-point it to Yodl's. Unnamed delegates are refused with `5760`. When set, the delegation is read on every send. See [a wallet delegated elsewhere](/sdk/eip-7702-provider/setup#a-wallet-delegated-elsewhere) |

`YodlLike` is structural — `{ config: { baseUrl?; chainIds?; rpcUrls?; getAuthHeaders? } }` — so passing a `Yodl` needs no type import and the package takes no dependency on `@yodlpay/sdk-core`.

`chains[].rpcUrl` is optional. Omit it and the wrapper's chain reads go over the wrapped wallet's own transport — see [chain reads](/sdk/eip-7702-provider/setup#chain-reads).

## `yodlHostedChains(chainIds?)`

Returns `chains` entries pointing at Yodl's own bundler — what `withYodl7702` falls back to when `chains` is omitted. Called with no argument it covers every chain Yodl sponsors gas on; pass ids to narrow it.

```tsx
yodlHostedChains();          // every sponsored chain
yodlHostedChains([8453]);    // Base only
```

A chain outside `YODL_SPONSORED_CHAIN_IDS` throws rather than being handed an invented endpoint, and an empty list is rejected. Mainnet is excluded because Yodl does not sponsor it: payers there pay their own gas, in ETH or in the payment's token (see [gas paid in an ERC-20 token](#gas-paid-in-an-erc-20-token)), and serving it takes a `chains` entry for chain 1 of your own.

| Export | What it is |
| --- | --- |
| `yodlHostedChains(chainIds?)` | `chains` entries for Yodl's bundler, with no `rpcUrl` so reads ride the wallet transport |
| `YODL_SPONSORED_CHAIN_IDS` | The chain ids Yodl sponsors gas on, read off the chain table in `@yodlpay/tokenlists` |

## Gas paid in an ERC-20 token

Where Yodl does not sponsor gas, a `wallet_sendCalls` bundle or a `yodl_prewarmAccount` request can pay it in an ERC-20 token through Pimlico's ERC-20 paymaster. Both name the token in the [ERC-7677](https://eips.ethereum.org/EIPS/eip-7677) `context`:

```ts
capabilities: {
  paymasterService: {
    url: paymasterUrl, // serves Pimlico's ERC-20 paymaster methods for EntryPoint 0.8
    context: { token: '0xdAC17F958D2ee523a2206206994597C13D831ec7' }, // USDT
  },
}
```

| Item | Behaviour |
| --- | --- |
| `paymasterService.erc20Gas` | Capability marker. `wallet_getCapabilities` sets `paymasterService: { supported: true, erc20Gas: true }` on every configured chain that is not in `boostedChainIds`. It means the wrapper adds the paymaster's token approval in front of the calls itself, so send a token only to a chain that carries it. Today only `withYodl7702` advertises it |
| `paymasterService.context.token` | The token gas is paid in, one the paymaster accepts: its list is `pimlico_getSupportedTokens`, managed on Yodl's Pimlico dashboard. Without `token`, the wrapper ignores `context` and the bundle is sponsored through `url`. The paymaster URL must serve `pimlico_getTokenQuotes`, `pm_getPaymasterStubData` and `pm_getPaymasterData` for Pimlico's EntryPoint 0.8 ERC-20 paymaster, `0x888888888888Ec68A58AB8094Cc1AD20Ba3D2402`, and answer with that paymaster |
| `GasTokenUnsupportedError` (`-32602`) | The paymaster quotes no rate for the token on that chain. Thrown before anything is signed |
| `GasTokenPaymasterMismatchError` (`-32602`) | The quote, stub data or final data named a paymaster other than Pimlico's EntryPoint 0.8 ERC-20 paymaster. Thrown before the operation is signed, so no allowance goes to that paymaster |
| Other `-32602` refusals | The token is not an address (a mixed-case address must carry a valid checksum), the bundle has no `paymasterService.url` to quote it, or the chain is in `boostedChainIds`, where the payer pays no gas |

`@yodlpay/sdk-core` names the token only for a wallet that advertises `erc20Gas`. Other wallets keep paying in ETH. See [setup](/sdk/eip-7702-provider/setup#gas-paid-in-an-erc-20-token) for the full sequence.

## How it works

The wrapper is **native-first**. Every request is tried against the original provider, and the EIP-7702 path is used only as a fallback. The exception is a `wallet_sendCalls` bundle that names a gas token in `paymasterService.context.token`: it always takes the EIP-7702 path, whatever `nativeSendCalls` says, because a native wallet would not put the paymaster's token approval in front of the calls.

```mermaid
flowchart TD
    UI["Yodl UI calls wallet_sendCalls"] --> TRY[Try the wallet natively]
    TRY -->|Succeeds| NATIVE[Native path wins]
    TRY -->|"Unsupported, 5700 or 5760"| FB[EIP-7702 fallback]
    TRY -->|"User rejected or any other error"| ERR[Surfaced as-is, never masked]
    FB --> SIGN["signAuthorization — you supply this"]
    SIGN --> FEES["estimateUserOperationFees — defaults to the bundler's oracle"]
    FEES --> SUB[Submit through the configured bundler]
    SUB --> ID[Return an EIP-5792 id and track it]
```

`wallet_sendCalls` falls back when native support is unavailable, when the wallet rejects a required capability with EIP-5792 code `5700`, or when it reports atomicity unsupported with code `5760`. User rejection and unrelated wallet errors surface as-is and are never hidden by the fallback.

When it does fall back, the wrapper sends the calls through the Yodl EIP-7702 smart-account path, returns an EIP-5792 `{ id }`, and tracks that id for later status checks.

## Per-method behaviour

| Method | Behaviour |
| --- | --- |
| `wallet_getCapabilities` | Advertises fallback capabilities for the configured `chains` (the hosted default when none were passed), per account, with `paymasterService.erc20Gas: true` wherever it answers for itself on a chain not in `boostedChainIds`: `atomic: supported` and `paymasterService` available for an EOA that is ours or undelegated; `atomic: ready` (an upgrade, the authorization signature) for a delegate the wrapper may re-point; the plugged-in provider's own answer for a delegate one of them drives, with a chain that answer leaves out counting as unsupported; `atomic: unsupported` for the rest. With no account, or no answer from the chain, it advertises `atomic: ready` and `paymasterService` available and leaves the verdict to the send |
| `wallet_sendCalls` | Tries native first, except for a bundle with `paymasterService.context.token`; falls back only on `5700` / `5760` / unsupported. A `capabilities.paymasterService.url` is used via the standard ERC-7677 paymaster client; with `paymasterService.context.token` the gas is paid in that ERC-20 token instead, see [gas paid in an ERC-20 token](#gas-paid-in-an-erc-20-token) |
| `wallet_getCallsStatus` | EIP-5792-shaped status for wrapper-tracked ids: pending, confirmed, reverted, partially reverted, not included. A bundle a plugged-in provider sent is asked of that provider |
| `wallet_showCallsStatus` | Resolves with no result for wrapper-tracked ids, since fallback bundles have no native status screen; a bundle a plugged-in provider sent is forwarded to it. Unknown ids delegate to the original provider, then report the EIP-5792 unknown-bundle error |
| everything else | Passed straight through to the original provider |

## Troubleshooting

| Symptom | Cause and fix |
| --- | --- |
| `crypto.getRandomValues ... not supported` | The `react-native-get-random-values` native module isn't linked. Reinstall pods and rebuild — not Expo Go |
| `Can't find variable: BigInt` | Hermes is disabled — see [requirements](/sdk/react-native#requirements) |
| `Unsupported chain id` (EIP-5792 `5710`) | The bundle's chain isn't in `chains`. Add a `{ id, bundlerUrl }` entry for it. On the hosted default this means Yodl doesn't sponsor that chain — mainnet included |
| `already delegates to 0x… on chain …` (`5760`) | The payer's EOA is delegated to an implementation nobody here drives. The Yodl UI falls back to one transaction at a time. Pay through the wallet that set it, or name it in [`foreignDelegations`](/sdk/eip-7702-provider/setup#a-wallet-delegated-elsewhere) with a provider of your own or `'repoint'` |
| `holds contract code … rather than an EIP-7702 delegation` (`5760`) | A contract is deployed at the payer's address. No authorization can replace it, so this account can't use the fallback |
| `answered wallet_sendCalls with no bundle id` (`-32603`) | A provider in `foreignDelegations` resolved `wallet_sendCalls` with neither `{ id }` nor a bare id string. Return the EIP-5792 shape |
| `an id already tracked` (`-32603`) | A provider in `foreignDelegations` answered `wallet_sendCalls` with an id another bundle already holds, or the bundler answered with one a plugged bundle was tracked under meanwhile. The send went out, but it is not tracked: its status questions would otherwise reach the wrong stack. Return an id unique to each bundle |
| Wallet chain mismatch (`-32602`) | Chain reads are riding the wallet transport and the wallet is on a different chain than the bundle. Switch the wallet before sending, or give that chain its own `rpcUrl`. See [chain reads](/sdk/eip-7702-provider/setup#chain-reads) |
| Error naming `rpcUrl` and a `'pending'` nonce | The wallet's node proxy method-whitelists and refuses `eth_getTransactionCount` at `blockTag: 'pending'`. Configure `rpcUrl` for that chain |
| `The paymaster quotes no rate for gas token 0x… on chain …` (`GasTokenUnsupportedError`, `-32602`) | The paymaster takes no payment in that token on that chain. Pay gas in a token it quotes, or in ETH by leaving `context.token` out. Nothing was signed |
| `The paymaster service named 0x… where Pimlico's ERC-20 paymaster 0x8888…2402 was expected` (`GasTokenPaymasterMismatchError`, `-32602`) | The paymaster URL's quote, stub data or final data named a paymaster other than Pimlico's EntryPoint 0.8 ERC-20 paymaster, so the wrapper did not approve it as a spender. Point `paymasterService.url` at a service for Pimlico's ERC-20 paymaster. No gas-token approval was signed |
| `Chain … is boosted: its gas is not paid by the payer` (`-32602`) | The bundle names a gas token on a chain in `boostedChainIds`. Leave `context.token` out there: gas on that chain is billed off-chain |
| `paymasterService.context.token must be a token address` (`-32602`) | The gas token is not an address. A mixed-case address must carry a valid checksum |
| `paymasterService.context.token needs a paymasterService.url` (`-32602`) | The bundle names a gas token but no paymaster to quote and sign it. Add `paymasterService.url` to the bundle |
| `Invalid request` (`-32602`) on mainnet through Yodl's proxy, before the payer signs | The operation's `maxFeePerGas` is over 500 gwei, which Yodl's proxy refuses on mainnet. Nothing was signed; retry once gas falls |
| Fallback never triggers | The wallet supports `wallet_sendCalls` natively. That's expected — native always wins, except for a bundle that pays gas in a token |
| `maxFeePerGas` / `maxPriorityFeePerGas` too low | An `estimateUserOperationFees` override returned a value below the bundler's floor, e.g. `0n` or a stale cached price. Drop the override to use the bundler's own oracle, or return real fees from it |
| Fallback fails at submission | Check that chain's `bundlerUrl` (and `rpcUrl` if you set one), including API keys |

:::tip
"Fallback never triggers" is the most-reported non-issue. If the wallet has native EIP-5792, this package is correctly doing nothing — verify with `wallet_getCapabilities` before debugging further.
:::
