Account interface function reference

Overview

The functions in the table Starknet account interface functions are part of account contracts. Where required, you must include these functions within your contract. The logic of these functions can be mostly arbitrary, with a few limitations. For information on these limitations, see Invalid transactions.

Table 1. Starknet account interface functions
Function name When required

__validate__

Always required

__execute__

Always required

__validate_declare__

Required for a contract to be able to send a DECLARE transaction.

__validate_deploy__

Required when deploying an instance of an account contract with a DEPLOY_ACCOUNT transaction.

You can only use the __validate_deploy__ function in an account contract to validate the DEPLOY_ACCOUNT transaction for that same contract.

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.

When the sequencer receives a transaction request, it calls the corresponding validation function with the fields of the transaction request, as follows:

  • For an INVOKE transaction, the sequencer calls the __validate__ function. After successfully completing validation, the sequencer calls the __execute__ function with the fields of the transaction request.

  • For a DEPLOY_ACCOUNT transaction, the sequencer calls the constructor function with the fields of the transaction request, then validates the transaction by calling the __validate_deploy__ function.

  • For a DECLARE transaction, the sequencer validates the transaction by calling the __validate_declare__ function.

After successfully completing validation, the deployment is finalized.

Separating the validation and execution stages guarantees payment to sequencers for work completed and protects them from Denial of Service (DoS) attacks.

Potential attacks

The validation functions have limitations, described below, that are designed to prevent the following attacks:

  • Denial of Service (DoS) attacks. Without these limitations, an attack could cause the sequencer to perform a large amount of work before a transaction fails validation, such as by sending multiple DEPLOY_ACCOUNT transactions that are invalid as a result of the constructor or __validate_deploy__ failing. This work would not be eligible for fee payment.

  • Even if the validation is simple, the following attack could still be possible:

    1. An attacker fills the mempool with transactions that are valid at the time they are sent.

    2. A sequencer starts executing them, thinking that by the time it produces a block, they will still be valid.

    3. However, shortly after the transactions are sent, the attacker sends one transaction that somehow invalidates all the previous ones and makes sure it’s included in the block, by offering higher fees for this one transaction, before the sequencer can publish the block.

  • Consider many validation functions checking 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.

Limitations on validation that prevent attacks

The limitations listed here apply to the following validation functions:

  • __validate__, __validate_deploy__, and __validate_declare__.

  • A constructor, when run in a DEPLOY_ACCOUNT transaction. That is, if an account is deployed from an existing class via the deploy syscall, these limitations do not apply.

The validation functions have the following limitations:

  • You cannot call functions in external contracts, only in your account contract.

    This restriction enforces a single storage update being able to invalidate only transactions from a single account. However, be aware that an account can always invalidate its own past transactions by changing its keys.

    So the fees you need to pay to invalidate transactions in the mempool are directly proportional to the number of unique accounts. For example, if the fee you need to pay to invalidate a transaction from one account is \(x\), then the price of invalidating ten transactions from ten different accounts is \(10x\).

  • The maximum number of computational steps, measured in Cairo steps, for a validation function is 1,000,000.

  • A builtin can be applied a limited number of times. For specific limits for each builtin, see Current limits.

  • Access is restricted to sequencer_address in the get_execution_info syscall. The syscall returns zero values for sequencer_address.

  • The values of block_number and block_timestamp in the get_execution_info syscall are modified as follows:

    • 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.

  • The following syscalls cannot be called:

    • get_block_hash

    • get_sequencer_address get_sequencer_address is only supported for Cairo 0 contracts.

Invalid transactions

When the __validate__, __validate_deploy__, or __validate_declare__, function fails, the account in question does not pay any fee, and the transaction’s status is REJECTED.

Reverted transactions

A transaction has the status REVERTED when the __execute__ function fails. 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, similar to Ethereum.

Function reference

The functions in this section must be present in account contracts, as noted, with __execute__ and __validate__ required in all account contracts. However, you define the logic in the these functions as needed, unless noted otherwise, while adhering to the limitations specified in Limitations on validation that prevent attacks. As a result, the function descriptions below leave you room to define your own functionality.

For examples of account contracts that implement these functions, see Account Contracts in the Starknet Book.

__execute__

Description

Always required

Initiates the execution stage in the sequencer. The sequencer calls this function upon receiving an INVOKE transaction, after the __validate__ function successfully completes.

In most implementations, __execute__ initiates a sequence of calls from the account.

The purpose of the __execute__ function is to abstract away the remaining actions performed by a transaction.

In Ethereum, a transaction is necessarily a call to a specific function in a smart contract. With the __execute__ abstraction, the account designer controls the flow of the transaction. For example, you can natively support multicalls in your account, saving the need to send multiple transactions. In practice, however, sending multiple transactions is even harder to manage without multicalls due to nonces.

Function signature

fn __execute__(
   self: @ContractState,
   <__arguments__>
) -> felt252

Parameters

self: @ContractState

The contract’s state. If you reference a component in a separate file, use @ComponentState<TContractState>.

<arguments>

Any arguments that you add.

Returns

The list of each call’s serialized return value.


__validate__

Description

Always required

Initiates the validation stage in the sequencer. Validates the sender’s address. The sequencer calls this function upon receiving an INVOKE transaction.

In most implementations, __validate__ ensures that only the account owner can initiate transactions.

The __validate__ function typically ensures that any transaction submitted was indeed initiated by the account owner and therefore does not take up unjustified resources during the execution process.

Without this mechanism, a forged transaction could result in the sequencer stealing the user’s funds. So the __validate__ function ensures that the sequencer can only include transactions that were approved by the account owner.

The arbitrary logic allowed in the __validate__ function gives the account’s designer the ability to determine what it means for a transaction to be valid, enabling different signature schemes and other exotic accounts.

Function signature

fn __validate__(
   self: @ContractState,
   <__execute_arguments__>
) -> felt252

Parameters

self: @ContractState

The contract’s state. If you reference a component in a separate file, use @ComponentState<TContractState>.

<execute_arguments>

The same arguments used in the __execute__ function.

Returns

If the signature is verified, the function should return the string VALID as felt252 value. If not, it should return any other value, such as 0.


__validate_declare__

Description

Required for a contract to be able to send a DECLARE transaction.

The sequencer calls this function upon receiving a DECLARE transaction.

If the contract declares other contracts and handles the corresponding gas fees, this function authenticates the contract declaration.

Function signature

fn __validate_declare__(
    self: @ContractState,
    class_hash: felt252
) -> felt252

Parameters

self: @ContractState

The contract’s state. If you reference a component in a separate file, use @ComponentState<TContractState>.

class_hash: felt252

The class hash.

Returns

If the signature is verified, the function should return the string VALID as felt252 value. If not, it should return any other value, such as 0.


__validate_deploy__

Description

Required when deploying an account with a DEPLOY_ACCOUNT transaction.

The sequencer calls this function upon receiving a DEPLOY_ACCOUNT transaction. Validates the deployment of the class referred to by the class_hash parameter in the transaction.

You can use this function to set up an account contract without linking it to the address that deploys it or depending on another account contract for gas fees. When determining the contract’s address, use the deployer address 0x0.

Function signature

fn __validate_deploy__(
    self: @ContractState,
    class_hash: felt252,
    contract_address_salt: felt252,
    <__constructor_arguments__>
) -> felt252

Parameters

self: @ContractState

The contract’s state. If you reference a component in a separate file, use @ComponentState<TContractState>.

class_hash: felt252

The class hash.

contract_address_salt: felt252

The contract address salt.

<constructor_arguments>

The arguments expected by the contract’s constructor. The inputs to the constructor must be identical to the rest of the inputs for validate_deploy. The compiler enforces this requirement.

In determining the contract address, the deployer address 0x0 is used.

Returns

If the signature is verified, the function should return the string VALID as a felt252 value. If not, it should return any other value, such as 0.

Example

Notice how the signature of __validate_deploy__ is structured to consider the signature of the constructor:

fn __validate_deploy__(
    self: @ContractState,
    class_hash: felt252,
    salt: felt252,
    public_key: felt252
) -> felt252

#[constructor]
fn constructor(ref self: ContractState, public_key: felt252)

You can access the transaction hash and value for max_fee by getting transaction information with the get_execution_info system call.