KIP 228: SetCode for EOA
Author | Ollie, Garry, Shiki |
---|---|
Discussions-To | TBD |
Status | Draft |
Type | |
Category | Core |
Created | 2024-11-15 |
Requires | 223 |
Abstract
Add a new transaction type to assign a code to an EOA. This KIP focuses on describing the difference from EIP-7702.
Motivation
Improve user experience by allowing account abstraction features to already existing EOAs. Previously, for EOA owners to benefit from ERC-4337 interface, they had to create a new smart account and migrate all assets and privileges. With EIP-7702, users can attach code to an existing EOA in-place.
Specification
Parameters
Parameter | Value |
---|---|
EMPTY_ROOT_HASH |
0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421 |
EMPTY_CODE_HASH |
0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 |
SET_CODE_TX_TYPE |
0x04 |
ENVELOPED_SET_CODE_TX_TYPE |
0x7804 |
SetCode Transaction
Introduce the SetCode transaction type where its RLP encoding is defined below.
0x7804 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination, value, data, access_list, authorization_list, signature_y_parity, signature_r, signature_s])
authorization_list = [[chain_id, address, nonce, y_parity, r, s], ...]
The fields follow the same semantics as EIP-7702.
Its consensus type prefix (that affects block.transactionRoot
) is ENVELOPED_SET_CODE_TX_TYPE (0x7804)
and its eth namespace API type prefix is SET_CODE_TX_TYPE (0x04)
. See “Changes to API” for clarification.
Behavior
The transaction’s behavior follows that of EIP-7702 except for the following:
For each authorization tuple [chain_id, address, nonce, y_parity, r, s]
,
- After recovering the authority,
- Fetch the AccountKey field of the recovered authority account.
- Verify that the AccountKey is of type
AccountKeyTypeLegacy
. If verification fails, immediately stop processing the tuple and continue to the next tuple in the list.
- When setting the code of the authority,
- Also set its codeInfo to
0x11 (VmVersion1 & EVM)
- As a special case, if address is
0x0000000000000000000000000000000000000000
, reset the authority account’s codeInfo to0x00
.
- Also set its codeInfo to
Delegation Designation
Semantics of EXTCODESIZE
, EXTCODECOPY
, EXTCODEHASH
, CALL
, CALLCODE
, STATICCALL
, DELEGATECALL
opcodes follows EIP-7702.
Gas costs
The intrinsic gas cost of a SetCode transaction follows the EIP-7702, meaning it inherits EIP-2930 and EIP-2028. Note that the EIP-2028 rule is realized by KIP-223.
Transaction Origination
Since Kaia hasn’t been explicitly enforcing EIP-3607, there is no need to modify transaction origin restriction.
Do follow the EIP-7702’s behavior where if a transaction’s destination has a delegation, also warm its target
Account State
The account RLP encoding for the world state trie needs to change.
EOA's compact encoding: 0x01 || rlp([nonce, balance, humanReadable, accountKey])
EOA's full encoding: 0x01 || rlp([[nonce, balance, humanReadable, accountKey], storageRoot, codeHash, codeInfo])
SCA's encoding: 0x02 || rlp([[nonce, balance, humanReadable, accountKey], storageRoot, codeHash, codeInfo])
Now both EOAs and SCAs hold the same set of 7 fields: nonce
, balance
, humanReadable
, accountKey
, storageRoot
, codeHash
, and codeInfo
.
When an EOA is RLP encoded, the compact encoding is used when the account has storageRoot = EMPTY_ROOT_HASH, codeHash = EMPTY_CODE_HASH, and codeInfo = 0x0. Conversely, when decoding an RLP-encoded EOA, the compact encoding implies that the account has storageRoot = EMPTY_ROOT_HASH, codeHash = EMPTY_CODE_HASH, and codeInfo = 0x0.
If an EOA’s delegation was cleared by a SetCode transaction with authorization.address = 0x0, set its CodeHash to EMPTY_CODE_HASH as per EIP-7702, and also set its CodeInfo to 0x0. Note that the account does not necessarily return to compact encoding. It will be compactly encoded only if storageRoot is EMPTY_ROOT_HASH.
An EOA with codeHash = EMPTY_CODE_HASH can be called “EOA without code” and an EOA with codeHash != EMPTY_CODE_HASH can be called “EOA with code”. Note that EOA without code doesn’t always mean it will be compactly encoded.
Interaction with other transaction types
The to
address of the following transaction types MUST be an EOA without code:
TxTypeValueTransfer
TxTypeFeeDelegatedValueTransfer
TxTypeFeeDelegatedValueTransferWithRatio
TxTypeValueTransferMemo
TxTypeFeeDelegatedValueTransferMemo
TxTypeFeeDelegatedValueTransferMemoWithRatio
The from
address of the following transaction types MUST be an EOA without code:
TxTypeAccountUpdate
TxTypeFeeDelegatedAccountUpdate
TxTypeFeeDelegatedAccountUpdateWithRatio
The to
address of the following transaction types MUST be an EOA with code or an SCA:
TxTypeSmartContractExecution
TxTypeFeeDelegatedSmartContractExecution
TxTypeFeeDelegatedSmartContractExecutionWithRatio
Note that whether the emptiness of storageRoot does not matter in transaction type restrictions.
Changes to APIs
eth_getRawTransaction, eth_sendRawTransaction
eth_getRawTransaction returns the raw transaction RLP prefixed with SET_CODE_TX_TYPE (0x04)
and eth_sendRawTransaction accepts such raw transaction.
kaia_getRawTransaction, kaia_sendRawTransaction
kaia_getRawTransaction returns the raw transaction RLP prefixed with ENVELOPED_SET_CODE_TX_TYPE (0x7804)
and kaia_sendRawTransaction accepts such raw transaction.
eth_getBlock, eth_getTransaction, eth_getTransactionReceipt
For SetCode transaction type, eth namespace APIs that return the transaction fields MUST show the authorizationList
field and its type: "0x04"
.
kaia_getBlock, kaia_getTransaction, kaia_getTransactionReceipt
For SetCode transaction type, kaia namespace APIs that return the transaction fields MUST show the authorizationList
field and its typeInt: 30724
, and type: "TxTypeEthereumSetCode"
.
eth_getCode, eth_getStorageAt, kaia_getCode, kaia_getStorageAt
APIs querying the code and storage of an account MUST work correctly for EOAs.
kaia_getAccount, kaia_isContractAccount
For both EOAs and SCAs, its kaia_getAccount result SHOULD include storageRoot, codeHash, codeFormat, vmVersion fields.
For both EOAs and SCAs, kaia_isContractAccount SHOULD return true if its codeHash is not EMPTY_CODE_HASH.
Changes to SDK
Before an SDK signs an authorization tuple, the SDK MAY verify that the authority’s AccountKey is of type AccountKeyTypeLegacy. If the SDK does not verify, it is the responsibility of the SDK user (e.g. Wallet) to make sure the authority is eligible to use the SetCode transaction.
Rationale
Extending the EOA instead of converting to SCA
Because the feature is called “Set EOA account code”, there could be a need to distinguish between EOA with code and an SCA.
EOAs with updated AccountKeys cannot sign authorization
In Kaia, EOAs may change the transaction signing permissions by sending an AccountUpdate transaction. But in this KIP, we strictly let the original private key (i.e. the one that AccountKeyLegacy accepts) to sign the SetCode authorization. Decoupling the two permissions will be confusing, so EOAs with code must have AccountKeyLegacy at all times.
EOA RLP formats depend on field values
For backward compatibility, we have to retain the compact encoding.
If we choose to select the encoding depending on hardfork instead, then all trie handling code must be fork-aware, and coding in that is non-trivial.
Kaia transaction type restrictions
ValueTransfer transaction types cannot be sent to EOAs with code. Kaia’s ValueTransfer transaction types have the semantics of simply increasing the balance of the destination EOA. If ValueTransfer can target EOA with code, the balance increase would bypass the EOA’s fallback() or receive() function. Opening a way to bypass the fallback may have an unknown security implication, thus prohibiting such a case.
AccountUpdate transaction types cannot be sent to EOAs with code. The reason is the same as why EOAs with updated AccountKeys cannot sign authorization.
SmartContractExecution transaction types can be sent to EOAs with code. Kaia’s SmartContractExecution transaction types had the semantics of executing the code of the destination SCA. Now that EOAs with code has a code, SmartContractExecution transactions indeed can invoke them.
Backwards Compatibility
The stateRoot calculation is backward compatible because we retain the EOA’s compact encoding.
The semantics of Ethereum typed transactions are backward compatible. If the target account has nonempty code, the code is executed.
Implementation
References
Copyright
Copyright and related rights waived via CC0.