--- eip: 7701 title: Native Account Abstraction description: Native Account Abstraction protocol, relying on a new transaction type and a family of opcodes author: Vitalik Buterin (@vbuterin), Yoav Weiss (@yoavw), Alex Forshtat (@forshtat), Dror Tirosh (@drortirosh), Shahaf Nacson (@shahafn) discussions-to: https://ethereum-magicians.org/t/eip-7701-native-account-abstraction/19893 status: Stagnant type: Standards Track category: Core created: 2024-05-01 --- ## Abstract We propose splitting the Ethereum transaction scope into multiple steps: validations, execution, and post-operation logic. Transaction validity is determined by the result of the validation steps of a transaction. We further separate transaction validation for the purposes of authorization and the gas fee payment, allowing one contract to pay gas for a transaction that will be executed from another contract. ## Motivation Native Account Abstraction allows custom validation logic of a transaction and custom gas payment logic, opening new use-cases and features for wallets and dApps. A more detailed motivation for this proposal can be found in the [README document](../assets/eip-7701/README.md). ## Specification ### Constants | Name | Value | |-----------------------------|-------------------| | `AA_TX_TYPE` | TBD | | `AA_ENTRY_POINT` | `address(0x7701)` | | `AA_BASE_GAS_COST` | 15000 | | `ROLE_SENDER_DEPLOYMENT` | `0xA0` | | `ROLE_SENDER_VALIDATION` | `0xA1` | | `ROLE_PAYMASTER_VALIDATION` | `0xA2` | | `ROLE_SENDER_EXECUTION` | `0xA3` | | `ROLE_PAYMASTER_POST_OP` | `0xA4` | ### New Transaction Type A new [EIP-2718](./eip-2718) transaction with type `AA_TX_TYPE` is introduced. Transactions of this type are referred to as "AA transactions". Their payload should be interpreted as: ``` AA_TX_TYPE || rlp([ chain_id, nonce, sender, sender_validation_data, deployer, deployer_data, paymaster, paymaster_data, sender_execution_data, max_priority_fee_per_gas, max_fee_per_gas, sender_validation_gas, paymaster_validation_gas, sender_execution_gas, paymaster_post_op_gas, access_list, authorization_list ]) ``` ### `CURRENT_ROLE` and `ACCEPT_ROLE` opcodes `current_context_role` is a context variable set by the AA transaction to the current role. During AA transactions, it is set to the current role in the transaction's lifecycle for each top level call. During non-AA transactions it is always set to `ROLE_SENDER_EXECUTION`. It remains unchanged on `DELEGATECALL` but is reset to `ROLE_SENDER_EXECUTION` on `CALL` / `STATICCALL` / `CALLCODE`. This behavior resembles `msg.sender`. The `CURRENT_ROLE` opcode returns the `current_context_role` value. The `ACCEPT_ROLE` opcode is equivalent to `RETURN` in the sense that it copies a memory slice, ends execution, and pastes the memory slice onto parent returndata, with a single modification: * It accepts the `frame_role` as the additional input parameter, and reverts if it differs from the `current_context_role` For each `role` in the transaction's lifecycle, a successful `ACCEPT_ROLE` is expected with the `frame_role == role`. If any validation frame failed to perform an `ACCEPT_ROLE` matching its role, the transaction fails the validity checks and cannot be included. ### `TXPARAM*` opcodes The `TXPARAMDLOAD`, `TXPARAMSIZE`, `TXPARAMCOPY` opcodes follow the pattern of `CALLDATA*` / `RETURNDATA*` opcode families. Each `TXPARAM*` opcode takes an extra stack input value as a first input compared to its `CALLDATA*` equivalent. The values of this input are as follows: | `n` | Return value | Data size | Default | Comment | |------|----------------------------|-----------|--------------|-----------------------------------------------------------------------------------| | 0x00 | current transaction type | 32 | | | | 0x01 | `nonce` | 32 | | | | 0x02 | `sender` | 32 | | | | 0x03 | `sender_validation_data` | dynamic | | | | 0x04 | `deployer` | 0 or 32 | `address(0)` | | | 0x05 | `deployer_data` | dynamic | empty array | | | 0x06 | `paymaster` | 0 or 32 | `address(0)` | | | 0x07 | `paymaster_data` | dynamic | empty array | | | 0x08 | `sender_execution_data` | dynamic | | | | 0x0B | `max_priority_fee_per_gas` | 32 | | | | 0x0C | `max_fee_per_gas` | 32 | | | | 0x0D | `sender_validation_gas` | 32 | | | | 0x0E | `paymaster_validation_gas` | 32 | `0` | | | 0x0F | `sender_execution_gas` | 32 | | | | 0x10 | `paymaster_post_op_gas` | 32 | `0` | | | 0x11 | `access_list` hash | 32 | | | | 0x12 | `authorization_list` hash | 32 | | | | 0xf1 | `execution_status` | 32 | | See transaction scoped vars in [processing flow](#aa-transaction-processing-flow) | | 0xf2 | `execution_gas_used` | 32 | | See transaction scoped vars in [processing flow](#aa-transaction-processing-flow) | | 0xff | `tx_hash_for_signature` | 32 | | Hash of the transaction without the signature | ### Affected opcodes In all top-level frames, the global variables have the following meaning: | Opcode Name | Solidity Equivalent | Value | |-------------|---------------------|-----------------------------------------------------------------------------------------------------------------| | `CALLER` | `msg.sender` | The `AA_ENTRY_POINT` address | | `ORIGIN` | `tx.origin` | The transaction `sender` address | | `CALLDATA*` | `msg.data` | Empty for all call frames except for the sender execution frame, for which it is set to `sender_execution_data` | ### Costs of accessing cold addresses for Sender, Paymaster, and Deployer The Sender address is pre-warmed as part of the `AA_BASE_GAS_COST`. When a non-zero address that is not equal to the `Sender` address, is provided for a `Paymaster` or a `Deployer` contract, an additional [EIP-2930](./eip-2930) `ACCESS_LIST_ADDRESS_COST` cost of **2400 gas** is charged and the address is added to `accessed_addresses`. ### AA transaction processing flow We define processing flow for an AA transaction as follows: ``` def state_transition_function(tx, block, state): # Empty refunds, warm list, execution status and gas used (new), etc state.transaction_scoped_vars = {} max_gas = tx.sender_validation_gas + tx.paymaster_validation_gas + tx.sender_execution_gas + tx.paymaster_post_op_gas gas_price = min(tx.max_fee_per_gas, block.base_fee_per_gas + tx.max_priority_fee_per_gas) payer = tx.sender if tx.paymaster is None else tx.paymaster total_max_cost = max_gas * gas_price balances[payer] -= total_max_cost gas_used = 0 if get_code(tx.sender) is None: deployer_result = call(tx.deployer, [], tx.sender_validation_gas_limit, ROLE_SENDER_DEPLOYMENT) assert deployer_result.accepted_role == ROLE_SENDER_DEPLOYMENT gas_used += deployer_result.gas_used sender_result = call(tx.sender, [], tx.sender_validation_gas_limit - gas_used, ROLE_SENDER_VALIDATION) assert sender_result.accepted_role == ROLE_SENDER_VALIDATION gas_used += sender_result.gas_used if tx.paymaster: paymaster_result = call(tx.paymaster, [], tx.paymaster_validation_gas, ROLE_PAYMASTER_VALIDATION) assert paymaster_result.accepted_role == ROLE_PAYMASTER_VALIDATION gas_used += paymaster_result.gas_used checkpoint = state.take_snapshot() sender_execution_result = call(tx.sender, [], tx.sender_execution_gas, ROLE_SENDER_EXECUTION) gas_used += sender_execution_result.gas_used state.transaction_scoped_vars[execution_status] = sender_execution_result.output_code state.transaction_scoped_vars[execution_gas_used] = gas_used if tx.paymaster: postop_result = call(tx.paymaster, [], tx.paymaster_post_op_gas, ROLE_PAYMASTER_POST_OP) gas_used += postop_result.gas_used if postop_result.accepted_role != ROLE_PAYMASTER_POST_OP: state.revert_snapshot(checkpoint) balances[payer] += gas_price * (max_gas - gas_used) ``` ## Rationale A full list of rationales for the decisions made in this proposal can be found in the [README document](../assets/eip-7701/README.md). ## Backwards Compatibility ## Security Considerations As the `ACCEPT_ROLE` opcode represent a generic way to authorize any action on behalf of the contract, correct and secure implementation of this code is critical. We expect that compilers targeting EVM will play a major role in enabling and ensuring Smart Contract Accounts' security. For smart contract security auditors and security-oriented developer tools it is crucial to ensure that contracts not meant to have roles in AA transactions do not have unexpected `ACCEPT_ROLE` opcode. Otherwise, these contracts may present an immediate security threat. As an example, block explorers should tag contracts as "user accounts" or "paymasters" if they have the `ACCEPT_ROLE` opcode used in their source code. ## Copyright Copyright and related rights waived via [CC0](../LICENSE.md).