> For the complete documentation index, see [llms.txt](https://docs.railgun.org/wiki/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.railgun.org/wiki/learn/integrating-railgun/eip-7702-integration.md).

# EIP-7702 Integration

## EIP-7702 Integration

> **Status: Released.** The 7702-capable SDK modules are on npm under the `latest` tag. The delegate contract is deployed and listed in the on-chain registry on each supported network (see "Registry" below).

EIP-7702 lets an externally owned account (EOA) delegate its execution to a smart contract, for the duration of one signed Type 4 authorization. Railgun uses this to replace Adapt Relay. Adapt Relay is the shared, communal contract that Railgun transactions have used for external DeFi and swap calls (cross-contract calls). In its place, each user's own EOA points to a single whitelisted delegate contract.

This page describes what changes for a wallet that integrates Railgun. It covers why the change exists and what to implement.

#### Why this exists

Adapt Relay is a shared contract. Every wallet's swap and DeFi transactions execute as the same communal contract instance. This works for a simple swap-and-shield: the output is an ordinary, transferable ERC-20 that Railgun can route back to the user. It does not work for protocols that return a position or receipt token that is non-transferable, or otherwise bound to the depositing address.

Aave is the clearest case. Its obligation tokens (aTokens) are non-transferable. A position opened by a shared relay contract has no way to be attributed, or moved back out, to the individual user who funded it. This puts an entire category of DeFi out of reach of the old shared-contract model: lending and borrowing positions, and non-transferable or account-bound receipt tokens generally.

With EIP-7702, each user delegates their own EOA to the delegate contract instead of routing through a shared one. The DeFi call executes as the user's own account. Whatever position comes back, transferable or not, is already in the address that opened it. It was never held by a shared intermediary. This is what makes protocols like Aave integrable at all, not just safer to integrate.

#### Reference implementation

Terminal Wallet has a working 7702 integration on the `feat/7702-integration-base` branch: [github.com/Terminal-Wallet/terminal-wallet-cli/compare/master...feat/7702-integration-base](https://github.com/Terminal-Wallet/terminal-wallet-cli/compare/master...feat/7702-integration-base). It is a full, working reference for everything below: package upgrade, base-token shield and unshield, private swaps, gas plumbing, broadcaster wiring, and an ephemeral-account admin and recovery UI. Wallet devs who integrate 7702 should expect to make substantially the same changes. This doc walks through what they are. That branch uses the release-candidate packages, so use the stable versions below.

The underlying on-chain mechanics live in `@railgun-community/engine` (stable release `9.8.0`). The public GitHub `release` branch is older: [github.com/Railgun-Community/engine/compare/main...release](https://github.com/Railgun-Community/engine/compare/main...release) (engine `9.7.0-rc.0`). Wallet devs who use the SDK (`@railgun-community/wallet`, which wraps this engine) will not call most of this directly. It is the source of truth for what the SDK does under the hood. This doc references it below, where it clarifies or corrects something Terminal Wallet's app-level code does not show.

#### What changes for a wallet integration

**Upgrade to 7702-capable SDK versions.** The 7702 code paths exist from these versions forward: `@railgun-community/wallet` ≥11.2.0, `@railgun-community/cookbook` ≥3.0.2, `@railgun-community/shared-models` ≥8.2.1, `@railgun-community/waku-broadcaster-client-node` ≥10.0.1, `ethers` 6.14.3. These are on npm under the `latest` dist-tag, so a plain `npm install @railgun-community/wallet` installs them. Do not use the earlier `-rc` versions or the `@dev` tag. They use the earlier development delegate addresses. These are major-version upgrades, so check the release notes of each package for other breaking changes. `@railgun-community/waku-broadcaster-client-node` 10.0.1 declares a peer dependency of `@railgun-community/wallet` `~11.1.0`, so you can see a peer-dependency warning with wallet 11.2.0.

**Base-token (ETH) shield.** Derive the wallet's current `EphemeralAccount` (via the SDK's `getCurrentEphemeralWallet`), and pass it into `gasEstimateForShieldBaseToken` and `populateShieldBaseToken`. The SDK takes the 7702 path only when it receives an `EphemeralAccount` signer. The delegate contract wraps ETH to WETH atomically as part of the shield, so there is no separate manual wrap step. Before you derive the account, realign the wallet's local ephemeral index with on-chain history once per session (`EphemeralKeyManager.scanHistoryForEphemeralIndex`). This protects an imported or restored wallet whose local index counter reset to 0 while indices were already spent on-chain.

**Base-token (ETH) unshield also routes through 7702** in the Terminal Wallet reference implementation. `getUnshieldBaseTokenGasEstimate` flags it explicitly as a 7702 (type-4) relay-adapt transaction, so the same Type4 gas handling below applies. Private ERC-20 transfers and unshields that do not touch the base token are not affected.

**DeFi and swap (cross-contract call) flows.** Switch from `gasEstimateForUnprovenCrossContractCalls` and `generateCrossContractCallsProof` to the 7702 variants: `gasEstimateForUnprovenCrossContractCalls7702` and `generateCrossContractCallsProof7702`. If you use a cookbook swap recipe (for example the 0x recipe), watch for this gotcha: the stock recipe defaults its quote's taker and recipient to the network's `relayAdaptContract` address. Under 7702, the delegate code executes as the user's own ephemeral EOA, not as a separate contract instance. A quote built against the old relay-adapt address routes the bought tokens to a non-executing address, and the downstream shield then operates on the wrong account. Override the quote's taker and recipient to the ephemeral EOA's own address instead. Also use the recipe's own `minGasLimit` (its floor, for example around 2.7M for swap-and-shield) rather than a hardcoded value. A swap's variable external call under-estimates gas and needs a real floor, or it reverts out of gas. Deterministic operations (shield, unshield, recovery, with no variable external call) can safely use `0` instead.

**Gas typing, common to all 7702 flows.** A 7702 (type-4) transaction must keep its EIP-1559 fee fields (`maxFeePerGas` and `maxPriorityFeePerGas`) through the whole pipeline. Tag these flows explicitly, with an `is7702Transaction` flag through your gas-details plumbing, so a broadcaster-routing branch does not downgrade them to Type1 or legacy pricing. That downgrade would zero out the priority fee on the populated transaction. Separately, pin `overallBatchMinGasPrice` to `0` for 7702 batches. That field is a legacy gas-price commitment check on the Railgun smart contract. A non-zero value reverts as "Gas price too low" once pricing is governed by the type-4 `maxFeePerGas` instead.

**Broadcaster wiring.** 7702 flows need a 7702-capable broadcaster. Filter broadcaster discovery to broadcasters that advertise 7702 support, using the `use7702Only` flag on the find-broadcaster calls. When you submit, pass the signed authorization tuple and explicit type-4 fee overrides (`gasLimit`, `maxFeePerGas`, `maxPriorityFeePerGas`) through to the broadcaster call. Without them, the broadcaster silently falls back to a legacy submission and drops the authorization, so the bundle can never broadcast. Pass your own already-computed gas limit rather than relying on the broadcaster's own `estimateGas` on the type-4 transaction. That estimate under-counts 7702 execution and can submit below the delegate contract's minimum-gas check, which fails with "Not enough gas supplied".

**7702-delegated (ephemeral) EOA management.** A delegated EOA is a real on-chain account, not a disposable signer. It can end up holding a balance the wallet needs to know about. If a DeFi or swap call fails partway through (a slippage revert, or an out-of-gas failure before the re-shield step), the leftover value lands in that EOA rather than in a shared contract. It sits there until the wallet notices and acts. This applies beyond DeFi and swap flows too. Any delegated EOA, including one touched only by a shield or base-token unshield flow, can end up holding native gas token or leftover balances the user did not intend to leave there.

These delegated EOAs are ephemeral. A wallet derives them from a sequential local index per wallet and chain, rather than reusing the user's single long-lived address, for privacy. Use the current index for a given operation, so the gas estimate, proof, and submission all agree on the same address. Only advance, or "ratchet," to the next index after that operation has actually broadcast. Never ratchet speculatively. Keep a durable local record of the index, not just an in-memory value. Realign it against on-chain history on session start and on demand: check which addresses show up as unshield recipients in the wallet's local transaction history. A restored or imported wallet's local counter will not know what has already been used on-chain.

Ship tooling for this. Do not leave it to the user to notice a stray balance. Build an admin view that shows the current ephemeral index and address, and lets the user scan for balances at any given index. Scan for both the native token and ERC-20s: combine a curated token list with a recent-block log scan for the recipient, since an arbitrary swap output can be missed by the curated list alone. Let the user recover stranded funds by reshielding them directly back into their own RAILGUN balance. Recovery targets a specific, possibly prior, ephemeral index. It must not disturb the wallet's live, current index, and must never ratchet the live index afterward.

#### Architecture notes

* The delegate contract is a single, general-purpose, whitelisted contract. No wallet should deploy its own version. All wallets that integrate this flow should point at the same canonical delegate contract, rather than build a bespoke implementation. A wallet-specific contract creates a distinguishable on-chain fingerprint: which contract a user's transactions route through becomes metadata that narrows down which wallet app they use. That works against Railgun's anonymity set, not for it.
* First-time use requires the user to sign the Type 4 delegation authorization before their EOA can use the delegate contract. Handle this as an explicit first-use step in your flow. Do not assume delegation is already in place. Check on-chain whether the account is already delegated before you prompt the user to sign again.
* EIP-7702 (native account delegation) and ERC-4337 (UserOperations, bundlers, paymasters) are not the same thing, and they are not compatible with each other. If your wallet has existing 4337 smart-account infrastructure, do not assume it interoperates with this flow. This integration is built on native 7702 authorizations, not UserOperations.
* **Two separate signatures are involved, not one.** First, the EIP-7702 `Authorization` tuple itself: the native account-delegation signature that points the ephemeral EOA's code at the delegate contract address. Second, a separate EIP-712 execution signature over the specific batch of calls being executed (`{payloadHash}`, domain `{name: 'RelayAdapt7702', version: '1', chainId, verifyingContract: <the ephemeral EOA's own address>}`). The second signature is what lets a broadcaster, or anyone, submit the transaction on the user's behalf without holding the ephemeral's private key. The delegate contract verifies the execution signature on-chain, rather than trusting `msg.sender`. If you integrate below the level the SDK wraps, you must produce both signatures and keep them in sync (see the nonce note below). If you call the SDK's own shield, swap, and unshield functions, this is handled for you.
* **Ephemeral EOA derivation uses a fully hardened BIP-32 path**: `m/44'/60'/0'/7702'/<railgunIndex>'/<chainId>'/<index>'`. It derives from the wallet's own mnemonic, and mnemonic password if any. Chain ID is baked into the path, so ephemeral addresses are chain-specific by construction. This matches the "per wallet and chain index" model described above.
* **The engine supports pluggable ephemeral signers** (`setEphemeralSignerProvider`) for wallets that do not want mnemonic-derived ephemeral keys, for example a hardware wallet or MPC signer. A custom provider owns its own key derivation and must manage its own index (`setEphemeralKeyIndex`). The engine's automatic index-ratcheting and on-chain-history resync (`incrementEphemeralKeyIndex`, `scanHistoryForEphemeralIndex`) work only for the default HD-derived provider. This matters if your wallet does not do mnemonic-based key management.
* **The delegate contract itself has at least two execution ABIs**: a current nonce-aware `execute()`, and a `LegacyPreExecuteNonce` variant without it. The engine tries both when it parses or validates a 7702 transaction it did not construct itself. Wallet devs do not need to branch on this directly; the SDK's execution-type default handles it. It is worth knowing if you debug a transaction that fails to parse. All current deployments use the nonce-aware ABI.

#### Registry

The on-chain registry holds the contract addresses that make up the protocol, as well as the supporting non-protocol contracts, such as the 7702 delegate contract. Read the delegate address with `getContractAddress('RelayAdapt7702')`. On each network in the table below, the registry returns the same delegate address that `@railgun-community/shared-models` 8.2.1 has hardcoded.

The SDK does not resolve the delegate address from the registry every time. At the engine level, network setup takes an explicit delegate contract address (`relayAdapt7702ContractAddress`) as a parameter. There is no code path where the engine calls the registry and substitutes whatever it returns in place of your configured value. If you also supply the registry's address (`railgunRegistryContractAddress`), the engine does a one-time check on load. It calls the registry's `getContractAddress('RelayAdapt7702')` and logs a **warning** if that disagrees with the address you configured. It does not block, and it does not switch to the registry's value. The engine keeps using whatever address you configured either way.

In practice, this means **your app, or its remote-configuration mechanism, is responsible for keeping the delegate address current**. If you use the SDK, the addresses come from `@railgun-community/shared-models`, so keep that package up to date.

If your wallet needs to override or pin a specific delegate address, for example ahead of an SDK release, check whether `@railgun-community/wallet`'s network-loading function exposes an equivalent parameter.

The earlier release-candidate addresses (`@railgun-community/shared-models` 8.1.0-rc.1) are superseded. Do not use them.

| Network                    | Delegate contract (`RelayAdapt7702`)         | Registry                                     |
| -------------------------- | -------------------------------------------- | -------------------------------------------- |
| Ethereum                   | `0x1F74b4339990Db761bC0168F20D294A3a141497e` | `0xF361d49b40E44CB23e6138B4cB32607322d9B595` |
| BNB Chain                  | `0xa2BFd382c54bb33DF6A13f21acd8770dc1e67D4D` | `0xDdE5c5a39945803Aa45f738688b319cFB172738c` |
| Polygon                    | `0xa2BFd382c54bb33DF6A13f21acd8770dc1e67D4D` | `0xDdE5c5a39945803Aa45f738688b319cFB172738c` |
| Arbitrum                   | `0xa2BFd382c54bb33DF6A13f21acd8770dc1e67D4D` | `0xDdE5c5a39945803Aa45f738688b319cFB172738c` |
| Base                       | `0xAcBC3De27E7d9750a7F26b2eCCE9c89C24Cc8691` | `0xFe276aD6a9Be967292D7fcb74E5510a1A6796bFb` |
| Ethereum Sepolia (testnet) | `0x05ae73c5925d843864ae6F261f3175De2ebCd963` | `0xb0FBBC5ba90ef3e565f162f8873F0dFF85dC9A0e` |

#### Rescuing funds from a delegated EOA while preserving privacy and saving gas

The core Railgun-orchestrated unshield-swap-reshield flow does not require the delegated EOA to hold its own gas. A broadcaster can submit that transaction and pay gas itself. The signed Type 4 authorization only has to come from the user's EOA, not from the gas payer. The broadcaster sponsors the whole thing the same way it does today.

**Recovering a stray balance does not require funding the EOA either.** This corrects an earlier assumption in this doc. In Terminal Wallet's recovery flow, the funds move by building a 7702 relay-adapt batch signed by the target ephemeral EOA. The EOA authorizes; it does not need to hold gas. The wallet submits the batch either through a broadcaster, with the fee paid in a token, or by self-broadcasting from the user's own already-funded public wallet as the transaction sender. Either way, the stranded balance gets reshielded straight back into the user's RAILGUN balance, with no gas top-up needed at the ephemeral address.

#### Privacy note for implementers

If you build sponsor or paymaster-style gas UX on top of this, do not require the user to reveal their shielded note in plaintext to a third party to get a gas quote or sponsorship. That defeats the privacy guarantee Railgun exists to provide. If you need a gas-sponsorship model, design it so the sponsor learns only what it needs to validate and price the transaction, not the user's private balance contents.

#### Integration checklist

* Upgrade to the stable 7702-capable SDK versions: `@railgun-community/wallet` 11.2.0, `@railgun-community/cookbook` 3.0.2, `@railgun-community/shared-models` 8.2.1, `@railgun-community/waku-broadcaster-client-node` 10.0.1, `ethers` 6.14.3. Do not use the old rc versions or the `dev` tag.
* Review the release notes for non-7702 breaking changes in the major version upgrades (wallet 11, cookbook 3, waku-broadcaster-client-node 10). Check the peer-dependency warning between waku-broadcaster-client-node 10.0.1 (`@railgun-community/wallet` `~11.1.0`) and wallet 11.2.0.
* Use the delegate addresses from the table above (they come with `shared-models` 8.2.1). Remove any hardcoded rc-era addresses from your app or remote config.
* Keep your SDK current. The SDK does not resolve the delegate address from the registry for you. It only warns on a mismatch.
* Add Base to your 7702 network list if you support it. Base has a delegate deployment.
* Base-token shield: derive an `EphemeralAccount` (via `getCurrentEphemeralWallet`) and pass it to `gasEstimateForShieldBaseToken` and `populateShieldBaseToken`.
* Base-token unshield: mark it as a 7702 (type-4) relay-adapt transaction in your gas-details plumbing. Do not assume it is unaffected.
* Private swaps and cross-contract calls: switch to `gasEstimateForUnprovenCrossContractCalls7702` and `generateCrossContractCallsProof7702`.
* If you use a swap recipe: override the quote's taker and recipient to the ephemeral EOA's own address, not the network's relay-adapt contract address.
* Use the swap recipe's own `minGasLimit` for swaps, not a hardcoded floor. Deterministic operations (shield, unshield, recovery) can use `0`.
* Keep `EVMGasType.Type4` through the whole pipeline for 7702 flows. Do not let a broadcaster-routing branch downgrade it to Type1.
* Pin `overallBatchMinGasPrice` to `0` for 7702 batches.
* Filter broadcaster discovery to 7702-capable broadcasters for 7702 flows.
* Pass the signed authorization tuple and explicit type-4 fee and gas-limit overrides through to the broadcaster submission call.
* Sync the local ephemeral index from on-chain history once per session. Keep a durable local record of the index, not just an in-memory, per-session value.
* Ratchet the ephemeral index forward only after a relay-adapt transaction has actually broadcast successfully. Never ratchet speculatively, and never after a recovery build, which targets a specific, possibly prior, index.
* Ship ephemeral-account admin and recovery tooling: show the current index and address, scan for stranded balances at a given index, and let the user reshield stranded funds back into RAILGUN.
* Recovery does not require funding the ephemeral EOA with gas: a broadcaster or the user's own public wallet pays. Reserve the unshield-to-address gas top-up pattern for direct third-party DApp interaction only.
* Test on mainnet. Testnets do not have the real DeFi liquidity and interactions needed to validate swap flows end to end. Use Ethereum Sepolia for basic shield and unshield checks.
* If you have existing 4337 smart-account infrastructure, treat this as a separate, non-interoperable code path.
