# Abstract Wallet Core introduces a design for 7702-enabled accounts that keeps the core execution logic of the EOA minimal while enabling extensibility. By delegating storage management to external contracts, it ensures that the EOA’s storage remains clean. This will avoid the need for additional checks for the status of storage slots as the delegated code will not need to read or write from storage. # Motivation ## Storage EIP-7702 introduces code logic into EOAs, enabling them to write to their own storage. However, the presence of storage variables within the EOA can create interoperability issues, particularly when a wallet redelegates between different code implementations that may access the same storage slots. This poses significant risks, especially if critical storage used for validation or ownership verification is altered. Currently, developers are responsible for preventing storage slot collisions when upgrading EOAs to smart accounts. While Storage Namespace solutions can reduce the likelihood of such conflicts, they do not fully eliminate the risk of EOA storage being overwritten. To address this, we propose a design that ensures EOA storage remains clean when upgraded to a smart account while also supporting extensibility. Our alternative account model enforces essential features while allowing users to incorporate additional functionalities as needed. ## Execution Wallet Core is designed to maintain a lean execution flow. With proper validation data, any entity—including the wallet owner—can submit transactions for execution. This approach aligns with an executor flow design, ensuring flexibility while maintaining security and efficiency. Our initial findings of gas costs using this lean execution flow is found to be reasonably low. The amount of gas needed to transfer ETH using our execution flow is at **33,541** gas. In comparison, the cost of sending ETH from an EOA to another is only slightly lower at about **21,000++** gas. > ![20250220-161642 (1)](https://hackmd.io/_uploads/S1FUHP4qyg.png) The value obtained assumes that the nonce of the transaction sponsor, nonce in Core Storage as well as receiver's ETH balance are non-zero to avoid cold storage writes (20,000 gas each) # Specification ![image](https://hackmd.io/_uploads/HJ6mIMQqkg.png) ## IWalletCore The Wallet Core is the contract that the EOA delegates to via an EIP-7702 transaction and it implements IWalletCore. This is a stateless orchestrator that has the following features: - Factory to create global storage through CREATE2 if it does not exist - Minimal proxy factory to create sub-accounts through CREATE2 - Batch execution - Perform validation if sender is not owner - Perform batch calls with try catch, to ensure that calls do not revert the transaction even if they fail ```solidity= interface IWalletCore { struct Call { address target; uint256 value; bytes data; } function executeWithValidation( Call[] calldata calls, bytes32 creationSalt, bytes calldata validationData, uint256 nonce, ) external; function executeFromSelf(Call[] calldata calls) external; function createSubAccount( address implementation, bytes32 uniqueBytes, bytes calldata initData ) external; } ``` ## ICoreStorage The core storage contract stores EOA-wide state that is critical to the operability of the account. As such, these contracts should be deployed through CREATE2 during initialisation and non-upgradeable to ensure resilience towards upgrade attacks during redelegation. A sample interface for core storage is as follows. ``` solidity interface IStorage { // should be protected, only allowing EOA to update function readAndUpdateNonce(address validator) external returns (uint256); function getNonce() external view returns (uint256); } ``` In the event that new variables need to be introduced to the core storage, we utilise a versioned namespaced salt, for each version of core storage, allowing the account to toggle between various versions as and when needed. ```solidity= // Version 0 Storage bytes32 private constant V0_STORAGE_SALT = keccak256(abi.encodePacked("core.storage.v0"));bytes32 private constant V0_STORAGE_SALT = keccak256(abi.encodePacked("okx.storage.v0")); // Version 1 Storage bytes32 private constant V1_STORAGE_SALT = keccak256(abi.encodePacked("core.storage.v1"));bytes32 private constant V1_STORAGE_SALT = keccak256(abi.encodePacked("okx.storage.v1")); function _getV0Storage() private view returns (address) { bytes memory code = abi.encodePacked( bytes1(0xff), address(this), V0_STORAGE_SALT, keccak256(type(Storage).creationCode) ); return address(uint160(uint(keccak256(code)))); } function _getV1Storage() private view returns (address) { bytes memory code = abi.encodePacked( bytes1(0xff), address(this), V1_STORAGE_SALT, keccak256(type(Storage).creationCode) ); return address(uint160(uint(keccak256(code)))); } ``` ## ISubAccount Since we envision sub-accounts' implementations to be provided by the downstream dapp developers, the suggested interface only contains an executeFromOwner function, which allows the rightful owner to make any outbound call assuming the identity of the sub-account. ```solidity interface ISubAccount { function executeFromOwner( address to, bytes calldata data, uint256 value ) external; } ``` # Rationale ## Execution As mentioned in the motivation, we aim to keep the execution flow lean. This is to lower the cost of executing transactions, which would be one of the main considerations of users migrating their EOAs to a smart account. Transactions can be executed in two ways. 1. Users can submit their own transactions with `executeFromAddressThis`, the only check needed will be that `msg.sender` is the same as `address(this)` 2. Transactions may also be submitted by other entities on behalf of the user with `executeWithValidation`. The transactions will be validated accordingly and the cost of submitting the transaction can be covered by the entity. This design opens up more possibilities of gas sponsorship, which is out of the scope of this discussion. ## Core Storage To prevent writing directly to an EOA’s storage, we utilize external contracts to manage storage variables required at the EOA level. This approach ensures that critical data, such as nonces and other essential variables, is stored externally, maintaining the integrity of the EOA's storage. Currently, the Core Storage only contains the wallet’s nonce, which is essential for validating transactions and preventing transaction replay using the execution flow. ## Sub Accounts The motivation behind sub-accounts is to create an isolated environment that remains ultimately owned by an EOA. These accounts should maintain storage specific to their use case while being managed by the corresponding application, with the EOA only responsible for deploying proxies. The use cases of sub-accounts are up for discussion. ### Use Cases The sub-account design enables a wide range of use cases. A straightforward example is allowing an EOA to control multiple smart accounts, thereby enabling interoperability between smart account implementations from different wallet providers without needing the EOA to perform redelegation between wallet providers' implementations. However, this is not the only potential use case. #### Deployer-bound Sub-accounts could also be deployer-bound, allowing them to store and manage critical data related to the EOA. These possibilities include: - Executor on EOA - DCA Bot - Soulbound NFTs - Proof of identity - Proof of KYC #### Transferable sub-accounts Another possible use case of sub-accounts can be transferable sub-accounts which open up following possibilities: - Trading of sub accounts with airdrop allocation - Trading of sub accounts with soul bound tokens - Trading of sub accounts with staked assets - Rental of sub accounts # Reference Implementation Our reference implementation remains in development. https://github.com/okx/wallet-core