Function name | When required |
---|---|
__validate__ | Always required |
__execute__ | Always required. The signatures of __validate__ and __execute__ must be identical. See critical validation requirements below. |
__validate_declare__ | Required for the account to be able to send a DECLARE transaction. This function must receive exactly one argument, which is the class hash of the declared class. |
__validate_deploy__ | Required to allow deploying an instance of the account contract with a DEPLOY_ACCOUNT transaction. The arguments of __validate_deploy__ must be the class hash of the account to be deployed and the salt used for computing the account’s contract address, followed by the constructor arguments. |
constructor | All contracts have a constructor function. It can be explicitly defined in the contract, or if not explicitly defined, the sequencer uses a default constructor function, which is empty. |
__execute__
, and their absence can lead to draining of the account’s funds:(1) assert!(get_caller_address().is_zero())
This asserts that the account’s __execute__
is not called from another contract, thus skipping validations (in later versions we may disallow calling __execute__
from another contract at the protocol level)(2) assert!(get_tx_info().unbox().version.into() >= 1_u32)
This asserts that the transaction’s version is at least 1, preventing the account from accepting INVOKE v0 transactions. It is critical to explicitly disallow the deprecated v0 transaction type, as v0 transactions assume that the signature verification happens in __execute__
, and are thus skipping __validate__
entirely.__validate_deploy__
function in an account contract to validate the DEPLOY_ACCOUNT transaction for that same contract. That is, this function runs at most once throughout the lifecycle of the account.DECLARE
transaction, the sequencer validates the transaction by calling the __validate_declare__
function.
INVOKE
transaction, the sequencer calls the __validate__
function with the transaction’s calldata as input, after being deserialized to the arguments in the __validate__
function’s signature. After successfully completing validation, the sequencer calls the __execute__
function with the same arguments.
DEPLOY_ACCOUNT
transaction, the sequencer calls the constructor
function with the transaction’s constructor_calldata
as input, after being deserialized to the arguments in the constructor signature. After the successful execution of the constructor, the sequencer validates the transaction by calling the __validate_deploy__
function.
INVOKE
and DEPLOY_ACCOUNT
transactions, it is up to the sender to make sure that the calldata is serializied correctly according to either the __validate__
or constructor
function’s signature.__validate__
, __validate_deploy__
, __validate_declare__
, and constructor
functions can be mostly arbitrary, except for the following limitations:
constructor
function, limitations apply only when run in a DEPLOY_ACCOUNT
transaction (in particular, not when an account is deployed from an existing class via the deploy
syscall)get_class_hash_at
get_sequencer_address
(this syscall is only supported for Cairo 0 contracts)
deploy
syscall from the __validate__
, __validate_deploy__
, __validate_declare__
, and constructor
functions will also not be possible.get_execution_info
syscall:
sequencer_address
is set to zero
block_timestamp
returns the time (in UTC), rounded to the most recent hour
block_number
returns the block number, rounded down to the nearest multiple of 100
INVOKE
transactions whose __validate__
requires many steps, but eventually fails
DEPLOY_ACCOUNT
transactions that are invalid as a result of the constructor or __validate_deploy__
failing.
__validate__
checks that the value of a storage slot is 1
, and the attacker’s transaction later sets it to 0
. Restricting validation functions from calling external contracts prevents this attack.
__validate__
, __validate_deploy__
, or __validate_declare__
functions fail, the account in question does not pay any fee, and the transaction’s status is REJECTED
.
When the __execute__
function fails, the transaction’s status is REVERTED
. Similar to Ethereum, a reverted transaction is included in a block, and the sequencer is eligible to charge a fee for the work done up to the point of failure.
__execute__
and the different validation functions is up to the party implementing the account. To review a concrete implementation, you can check out OpenZeppelin’s account component, which also adheres to SNIP6.
CREATE
and CREATE2
opcodes).
DEPLOY_ACCOUNT
transaction, which does not require a preexisting account.
INVOKE
transaction
DEPLOY_ACCOUNT
transaction type, the sequencer executes the __validate_deploy__
function in the deployed contract.
__validate__
function in the contract of the sender’s address.
DEPLOY_ACCOUNT
transaction, the fees are paid from the address of the deployed account. If you use the UDC, which requires an INVOKE
transaction, the fees are paid from the sender’s account. For information on the differences between V1 and V3 INVOKE
transactions, see INVOKE
transaction in Transaction types.1
, when deployed with a DEPLOY_ACCOUNT
transaction
0
, when deployed with the UDC