The following is an example of an invoke transaction in its current structure (version 0):
{
"contract_address": "0x53877052f990547ec7ca9dedec3a33ca321d3d45d2ca0790a1bb34bae52956e",
"entry_point_selector": "0x15d40a3d6ca2ac30f4031e42be28da9b056fef9bb7357ac5e85627ee876e5ad",
"calldata": [],
"signature": [
"0x2053a06585efce1c6e6e5cd9bbba774b78d872e727c310d972c7d885378d4fc",
"0x53e0e3c6a515c1334d199a5a55fb8b5e0a1b5f678c36587e43c8f939f8ac99d"
],
"max_fee": "0xe0fbdace1bb7a",
"type": "INVOKE_FUNCTION",
"version": 0
}
In StarkNet 0.10 we Introduce a new transaction version that includes the following changes:
entry_point_selector
is removednonce
field is addedThe following is an example of a version 1 transaction:
{
"contract_address": "0x53877052f990547ec7ca9dedec3a33ca321d3d45d2ca0790a1bb34bae52956e",
"calldata": [],
"signature": [
"0x2053a06585efce1c6e6e5cd9bbba774b78d872e727c310d972c7d885378d4fc",
"0x53e0e3c6a515c1334d199a5a55fb8b5e0a1b5f678c36587e43c8f939f8ac99d"
],
"max_fee": "0xe0fbdace1bb7a",
"type": "INVOKE_FUNCTION",
"nonce": "0xa1",
"version": 1
}
The motivation for the above change is detailed in the next section. Essentially, since transactions must go through an account contract in a flow that is known in advance (first validate and then execute), there is no need to specify the fixed __execute__
selector.
In this version, we move forward with our account abstraction design and add the __validate__
entrypoint to accounts. Recall that StarkNet generalizes that notion of Ethereum’s EOAs with account abstraction. With account abstraction, accounts are contracts like any other (up to some caveats which we’ll cover here), which may contain arbitrary logic (this is particularly useful for custom verification logic).
Accounts have a special entrypoint named __execute__
****(see e.g. the OpenZeppelin account implementation). Up until now, to send a transaction from your account, you would call the __execute__
****function in your account contract, which was also responsible for the verification. In 0.10 we will break this coupling and introduce the __validate__
****entrypoint into accounts. Upon receiving a transaction, your account contract will first call __validate__
, and only if it succeeded, __execute__
will be called.
With this separation in place, we can distinguish between invalid transactions (validate failing, analogous to an invalid signature in Ethereum) and transactions that failed in the execution phase. In future versions, we will make use of this separation in order to include reverted transactions in the block, and potentially charge a fee for a transaction that passed validation.
This new flow will only apply to transactions of version 1. This means that old account contracts, who do not contain the __validate__
entrypoint, will continue to operate as usual upon receiving transactions of version 0. Note that this is a temporary measure to smooth the transition, and version 0 transactions will be deprecated in the future.
So far, nonces were handled at the account contract level as a measure of replay protection. However, this does not handle hash uniqueness. Depending on the account’s validation logic, one may be able to include two transactions with identical hashes. To avoid this, we decide to include nonces at the protocol level, similarly to Ethereum. This means that every contract now includes a nonce, which counts the number of transactions that were accepted by it. In particular, account contracts will only accept transactions with a matching nonce, i.e. if the current nonce of the account is X, then it will only accept transactions with nonce X.
Note that the nonce validation will happen at the protocol level, independently from the account’s __validate__
functionality. Currently, if a transaction passes __validate__
but fails in __execute__
, the nonce will NOT be bumped, however, this behavior is expected to change in future versions.
The introduction of nonces has implications on different components in StarkNet, we attempt to summarize these below: