主页EIPs周刊
EIPsEIP-7701
EIP-7701

Native Account Abstraction

Native Account Abstraction protocol, relying on a new transaction type and a family of opcodes
DraftStandards Track: Core
创建时间: 2024-05-01
Vitalik Buterin (@vbuterin), Yoav Weiss (@yoavw), Alex Forshtat (@forshtat), Dror Tirosh (@drortirosh), Shahaf Nacson (@shahafn)
社区讨论原文链接编辑
1 分钟了解
欢迎补充好内容
去提交
相关视频
欢迎补充好内容
去提交
正文

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.

Specification

Constants

NameValue
AA_TX_TYPETBD
AA_ENTRY_POINTaddress(0x7701)
AA_BASE_GAS_COST15000
ROLE_SENDER_DEPLOYMENT0xA0
ROLE_SENDER_VALIDATION0xA1
ROLE_PAYMASTER_VALIDATION0xA2
ROLE_SENDER_EXECUTION0xA3
ROLE_PAYMASTER_POST_OP0xA4

New Transaction Type

A new 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:

nReturn valueData sizeDefaultComment
0x00current transaction type32
0x01nonce32
0x02sender32
0x03sender_validation_datadynamic
0x04deployer0 or 32address(0)
0x05deployer_datadynamicempty array
0x06paymaster0 or 32address(0)
0x07paymaster_datadynamicempty array
0x08sender_execution_datadynamic
0x0Bmax_priority_fee_per_gas32
0x0Cmax_fee_per_gas32
0x0Dsender_validation_gas32
0x0Epaymaster_validation_gas320
0x0Fsender_execution_gas32
0x10paymaster_post_op_gas320
0x11access_list hash32
0x12authorization_list hash32
0xf1execution_status32See transaction scoped vars in processing flow
0xf2execution_gas_used32See transaction scoped vars in processing flow
0xfftx_hash_for_signature32Hash of the transaction without the signature

Affected opcodes

In all top-level frames, the global variables have the following meaning:

Opcode NameSolidity EquivalentValue
CALLERmsg.senderThe AA_ENTRY_POINT address
ORIGINtx.originThe transaction sender address
CALLDATA*msg.dataEmpty 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 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.

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 and related rights waived via CC0.

扩展阅读
欢迎补充好内容
去提交

不想错过最新的 EIP 动态?

订阅 EIPs Fun 周刊以跟进相关更新,建⽴你与 EIP 之间的连接 ,更好地建设以太坊。

详情
支持以太坊贡献者,推动生态建设
资源
GitHub