A secure ERC-1967 proxy implementation for EIP-7702 smart accounts.
The EIP-7702 Proxy provides a secure way to upgrade EOAs to smart contract wallets through EIP-7702 delegation. It solves critical security challenges in the EIP-7702 design space while allowing the use of existing smart account implementations.
- Signature-based authorization from the EOA for initial implementation setting and initialization
- Atomic implementation setting + initialization to prevent front-running
- Account state validation through implementation-specific configurable validator
- Reliable protection against signature replay through external nonce tracking
- ERC-1967 compliant implementation storage
- Ability to set the ERC-1967 storage slot via the proxy itself
- Built-in token receiver for uninitialized state
- Safe handling of A β B β A delegation patterns
- Implementation-agnostic design
- Compatible with any ERC-1967 implementation
Deployed on all networks supported by Coinbase Smart Wallet
| Contract | Address |
|---|---|
| EIP7702Proxy | 0x7702cb554e6bFb442cb743A7dF23154544a7176C |
| NonceTracker | 0xD0Ff13c28679FDd75Bc09c0a430a0089bf8b95a8 |
| DefaultReceiver | 0x2a8010A9D71D2a5AEA19D040F8b4797789A194a9 |
| CoinbaseSmartWalletValidator | 0x79A33f950b90C7d07E66950daedf868BD0cDcF96 |
All contracts are deployed deterministically via CREATE2, so they share the same addresses on every chain (see Deployment Addresses above). Deployment is handled by script/Deploy.s.sol, which deploys the full cluster and asserts that each resulting address matches its canonical value. A misconfigured deploy therefore reverts instead of silently landing at the wrong address.
Deployments go through the canonical CREATE2 factory at 0x4e59b44847b379578588920cA78FbF26c0B4956C, which is already present on most chains. A CREATE2 address depends only on the factory, the salt, and the contract init code (creation bytecode plus constructor args), and never on the deployer's account. The salts are fixed in Deploy.s.sol:
| Salt | Value | Used for |
|---|---|---|
DEPENDENCY_DEPLOYMENT_SALT |
7702 |
CoinbaseSmartWalletValidator, NonceTracker, DefaultReceiver |
PROXY_DEPLOYMENT_SALT |
23662924 |
EIP7702Proxy (gives the 0x7702cb... vanity prefix) |
Because the address is a function of the init code, the compiler settings must match exactly or the address changes. They are pinned in foundry.toml (via_ir = true, optimizer = true, optimizer_runs = 20000, solc 0.8.23). Do not override them when deploying.
- Confirm the CREATE2 factory exists on the target chain:
If this returns
cast code 0x4e59b44847b379578588920cA78FbF26c0B4956C --rpc-url <RPC_URL>
0x, deploy Arachnid's deterministic deployment proxy first, then continue. - Fund the deployer account on the target chain. The full cluster is roughly 1.6M gas total.
- Optionally simulate first by running the command below without
--broadcast. - Deploy and broadcast:
forge script Deploy \ --account <your-keystore-account> \ --sender <deployer-address> \ --rpc-url <RPC_URL> \ --broadcast -vvvv
The asserts in the script guarantee the contracts either land at the canonical addresses or the transaction reverts.
For Etherscan-family explorers:
# CoinbaseSmartWalletValidator (constructor arg: CoinbaseSmartWallet implementation)
forge verify-contract --watch --chain-id <CHAIN_ID> \
--constructor-args $(cast abi-encode "constructor(address)" 0x000100abaad02f1cfC8Bbe32bD5a564817339E72) \
--etherscan-api-key <API_KEY> \
0x79A33f950b90C7d07E66950daedf868BD0cDcF96 \
src/validators/CoinbaseSmartWalletValidator.sol:CoinbaseSmartWalletValidator
# NonceTracker (no constructor args)
forge verify-contract --watch --chain-id <CHAIN_ID> --etherscan-api-key <API_KEY> \
0xD0Ff13c28679FDd75Bc09c0a430a0089bf8b95a8 src/NonceTracker.sol:NonceTracker
# DefaultReceiver (no constructor args)
forge verify-contract --watch --chain-id <CHAIN_ID> --etherscan-api-key <API_KEY> \
0x2a8010A9D71D2a5AEA19D040F8b4797789A194a9 src/DefaultReceiver.sol:DefaultReceiver
# EIP7702Proxy (constructor args: NonceTracker, DefaultReceiver)
forge verify-contract --watch --chain-id <CHAIN_ID> \
--constructor-args $(cast abi-encode "constructor(address,address)" 0xD0Ff13c28679FDd75Bc09c0a430a0089bf8b95a8 0x2a8010A9D71D2a5AEA19D040F8b4797789A194a9) \
--etherscan-api-key <API_KEY> \
0x7702cb554e6bFb442cb743A7dF23154544a7176C src/EIP7702Proxy.sol:EIP7702ProxyFor Blockscout explorers, add --verifier blockscout --verifier-url https://<explorer-host>/api? to each command (no API key required).
Because CREATE2 ignores the sender, anyone can reproduce the same addresses on a new chain by replaying the original deployment calldata against the factory, with no source checkout or compiler setup required. Each original deployment is a transaction sent to 0x4e59b44847b379578588920cA78FbF26c0B4956C whose calldata is salt (32 bytes) ++ init code. Copy that calldata from a chain where the contracts already exist and resend it:
# Read the calldata from an existing deployment transaction
cast tx <DEPLOY_TX_HASH> input --rpc-url <SOURCE_RPC>
# Replay it on the new chain (deploy the dependencies before the proxy)
cast send 0x4e59b44847b379578588920cA78FbF26c0B4956C <CALLDATA> \
--rpc-url <NEW_RPC> --account <your-keystore-account>This reuses the exact init code byte-for-byte, so it produces identical addresses and avoids compiler-setting mismatches entirely.
CoinbaseSmartWalletValidatortakes the Coinbase Smart Wallet implementation (0x000100abaad02f1cfC8Bbe32bD5a564817339E72) as a constructor argument. This is baked into its init code and does not need to exist for deployment to succeed, but the validator is only usable on chains where that implementation is also deployed.
- Manages safe implementation upgrades through
setImplementation - Validates EOA signatures for all state changes
- Provides fallback to
DefaultReceiverwhen uninitialized - Overrides
isValidSignatureto provide a final fallbackecrecovercheck
- External nonce management for signature validation in storage-safe location
- Prevents signature replay attacks
- Maintains nonce integrity across delegations
- Interface for implementation-specific state validation
- Called to ensure correct initialization or other account state
- Reverts invalid state transitions
- Inherits from Solady's
Receiver - Provides a default implementation for token compatibility
-
Deploy singleton instance of
EIP7702Proxywith immutable parameters:NonceTrackerfor signature securityDefaultReceiverfor token compatibility
-
Sign an EIP-7702 authorization with the EOA to delegate to the
EIP7702Proxy -
Sign a payload for
setImplementationwith the EOA, which includes the new implementation address, initialization calldata, and the address of an account state validator -
Submit transaction with EIP-7702 authorization and call to
setImplementation(bytes args, bytes signature)with:address newImplementation: address of the new implementationbytes calldata callData: initialization calldataaddress validator: address of the account state validatorbytes calldata signature: ECDSA signature over the initialization hash from the EOAbool allowCrossChainReplay: whether to allow cross-chain replay
Now the EOA has been upgraded to the smart account implementation and had its state initialized.
If the smart account implementation supports UUPS upgradeability, it will work as designed by submitting upgrade calls to the account.
Audited by Spearbit.
| Audit | Date | Report |
|---|---|---|
| First private audit | 02/03/2025 | Report |
| Second private audit | 03/05/2025 | Report |
| Public competition | 04/13/2025 | Report |