EIP-7702 is a standard slated to be released with the Pectra hardfork in Q2 2025 that allows EOAs to become smart contracts by setting code at their address. This will allow wallet providers to offer users the option to upgrade their EOA to a smart contract wallet. Many smart contract wallet implementations have initializer functions that are designed to be called atomically upon deployment by a factory. Due to the nature of how code is set at an account via EIP-7702, the same guarantee of atomic deployment and initialization cannot be made in this case. Without additional protection, such account implementations that were designed for factory deployment would be vulnerable to arbitrary initialization.
The EIP7702Proxy
is a lightweight, ERC-1967 proxy with additional behavior that protects an implementation from being frontrun with arbitrary initialization. The EIP7702 proxy achieves this protection by atomically enforcing that the proxy will only set its pointer and begin delegating calls to the implementation if the criteria for safety according to the implementation are simultaneously met. Implementations are free to define modular validator contracts that will be used to evaluate whether a given implementation has achieved a safe state. This allows existing smart contract wallet implementations with vulnerable initialization patterns to be used without modification as implementations for EIP-7702-created smart contract accounts.
Prize distribution and scoring
-
Total Prize Pool: $20,000
-
Scoring described in the competition scoring page.
-
Findings Severities described in detail on our docs page.
Documentation
- Documentation can be found in the README and directly inline in the EIP7702Proxy.
- Note that the EIP7702Proxy, while implementation agnostic, is designed to work with the CoinbaseSmartWallet.
- CoinbaseSmartWallet is out of scope for this audit, but interactions between the proxy and an implementation like the CoinbaseSmartWallet should be carefully considered.
- Blog
Scope
- Repository: https://github.com/base/eip-7702-proxy
- Commit:
371a141ed738a0328b356092a8df1bed0e1d9856
- Total LOC: 237
- Files:
- EIP7702Proxy.sol
- DefaultReceiver.sol
- NonceTracker.sol
- IAccountStateValidator.sol
- CoinbaseSmartWalletValidator.sol
Build Instructions
git clone [email protected]:base/eip-7702-proxy.git
forge update && forge install
forge build
forge test
POC Rule
- Mandatory POC rule applies for this competition.
- See the first test “test_succeeds_whenImplementationSlotIsEmpty” in setImplementation.t.sol for a simple POC test and necessary boilerplate.
- Note that coinbaseImplementation.t.sol is a good example of tests that specifically use a CoinbaseSmartWallet implementation v.s. a simple mock implementation. There are test examples available for all functions in all contracts.
Out of scope
- 7702 code and storage mutability: EIP-7702 enables EOAs to delegate, re-delegate, or un-delegate the logic at their account an indefinite number of times, but all delegates operating at the account share the same storage root. This fact loosens guarantees around logic/storage relationship held by “normal” smart contracts. This is a universal property of 7702 accounts and may lead to new patterns or offchain decisions made to protect users, for example zeroing-out crucial storage slots with a dedicated eraser contract or constructor logic upon initialization of of a newly established delegate.
- a. There is nothing a given smart contract can do to protect against the activity of other delegates if the EOA is willing to delegate to them.
- b. There are essentially infinite vectors for malicious or destructive delegates dangerously manipulating storage state in a way that causes problems or unexpected behavior for subsequent delegates.
- c. We are not aiming to solve for all possible complications of this fact, but rather to defend against insecurities that may arise even in good-faith operation of the EOA.
- The EOA is always a super-owner/super-administrator of any delegated logic at its address because the EOA can set/unset the logic at its address an unlimited number of times, and can therefore also arbitrarily manipulate any storage at its address with a dedicated contract (intentionally or unintentionally). We consider the entire family of potential vulnerabilities, unpredictable behavior or outright attacks associated with this fact to apply to ALL potential 7702’ed EOAs and to be out of the scope of this audit.
- We’re aware that the EOA will be treated as an owner in many (but not all) calls to a
CoinbaseSmartWallet
implementation. For example, the EOA (by way of being equal to address(this) of the smart wallet) will pass the _onlyOwner modifier and be able to directly callexecute
andaddOwner
etc., however it will not pass the logic of_isValidSignature
which is used invalidateUserOp
and therefore EOAs will not able to sign userOps for their CoinbaseSmartWallet unless explicitly configured as an owner. We understand and accept this seeming inconsistency in privilege, as it does not introduce any security vulnerabilities that aren’t already a subset of the total power held by the EOA private key. - We implement an ecrecover check on signatures by overriding
isValidSignature
in the proxy. Therefore all existing signatures that were ever created by the EOA private key are potentially valid against a call toisValidSignature
. This is by design and the responsibility of the EOA/user to determine whether their history of signatures may be problematic if upgrading to this contract.- a. This override of isValidSignature implemented on the proxy means that from the perspective of an implementation contract, using “isValidSignature” will invoke the implementation’s implementation of isValidSignature, whereas using “this.isValidSignature” will invoke the proxy’s implementation of isValidSignature. This is a known complexity and will be handled with documentation.
- The initialization payload signature can be chain-agnostic, but the dependence on both a nonce series and a specific current → new implementation transition in the signed message means a chain agnostic signature is not actually guaranteed to work on all chains unless they also share these states. This is a known potential usability issue for cross-chain signatures, and we do not attempt to guarantee that all cross-chain signatures will always work but rather preserve this option as the only way to sign initializations that may be playable on future, nonexistent chains in the case that a private key is intentionally destroyed to lock the account in smart-wallet mode.
- Account state validation is only as good as the accuracy of the supplied validator address. If
setImplementation
is called with a validator implementation that does not correctly assess the state of the new implementation, the new implementation could become vulnerable. We attempt to mitigate the likelihood of this by requiring a nonzero validator address, requiring validators to return a specific magic value indicating success (mere non-reverting is not sufficient success criteria), and to pass the address of the implementation tothe validateAccountState
(with the hope this will encourage validator logic to evaluate whether this implementation matches an implementation they know how to support). However, validator correctness cannot be enforced at the level of theEIP7702Proxy
and is out of the scope of this audit. The correctness of the specificCoinbaseSmartWalletValidator
is in scope. - During the first call to
setImplementation
on a given chain, the signed value of ERC1967Utils.getImplementation() will be 0, while the actual implementation (_implementation()) will be set to the receiver address. This discrepancy may cause confusion when verifying the initial implementation state, but is a known issue that will be solved with documenting the fact that the “current implementation” should always be evaluated by looking at the ERC-1967 storage slot directly._implementation()
is a private function anyway, and can’t be called publicly. - 7702 accounts may have their ERC-1967 storage slot established, only to be delegated to a different piece of logic that may not be a proxy at all. Block explorers will need to adapt to this and a variety of other potential issues with a 7702 account to avoid making assumptions about the nature of an account (such as displaying an account as a proxy simply due to the presence of a value in the ERC-1967 storage location). This is a challenge for block explorers and out of scope for this audit.
- The EOA can arbitrarily increase its nonces in the NonceTracker sequence. This would potentially invalidate a signature for setImplementation. We are accept this possibility given that 1) The EOA has no incentive to disrupt intended operations for now reason 2) the worst case scenario is simply that a new signature would need to be generated for setImplementation and 3) this does technically offer a way for EOAs to invalidate signatures before they are applied if so desired.
- Lightchaser report:
Contact Us
For any issues or concerns regarding this competition, please reach out to the Cantina core team through the Cantina Discord.
Summary
Status
CompletedTotal reward:
$20,000
Findings submitted:
120
Start date:
20 Mar 2025 8:00pm (local time)
End date:
25 Mar 2025 8:00pm (local time)