Transactions are constructed by users to express the intent of performing actions in the network. Once in the network, transactions are converted into Receipts, which are messages exchanged between network nodes.
On this page, we will explore the lifecycle of a transaction, from its creation to its final status.
Receipts & Finality
Let’s walk through the lifecycle of a complex transaction using blocks as time units. The block numbers below illustrate execution stages; transaction-pool delays, receipt dependencies, and congestion can spread them over additional blocks. Execution and consensus finality progress independently.Block #1: The Transaction Arrives
Once a validated transaction is included in a chunk, processing it creates a single actionReceipt containing all the actions to be executed.
While creating the Receipt, the signer gets $NEAR deducted from its balance to pay for the gas and any attached NEAR.
If the signer and receiver coincide - e.g. the signer is adding a Key - the local Receipt can execute in the same chunk. This completes those actions, but consensus finality and any remaining receipts must still be checked.
Block #2: The Receipt is Processed
If thesigner and receiver differ - e.g. the signer transfers NEAR to the receiver - the Receipt is routed to the receiver’s shard for execution, which can occur in a subsequent block.
During this process a FunctionCall could span a cross-contract call, creating one or multiple new Receipts.
Block #3…: Function Calls
Receipts created by a function call can execute in later blocks. Function-call receipts can spawn additional receipts; callbacks wait for their dependencies before executing.Final Block: Gas is Refunded
A finalReceipt is processed in a new block, refunding any extra gas paid by the user. Almost every transaction ends with one of these, since the buy price is usually higher than the burn price.
Waiting for a Transaction
When using our libraries (or directly contacting the RPC), you can specify how long you want to wait for a transaction to be processed using await_until parameter.
After a transaction is submitted, it first gets validated by the RPC node. If the transaction fails structural checks it will be immediately rejected. Otherwise, two things progress independently:
- Execution: the receipts spawned by the transaction are executed across shards
- Finality: the blocks containing the transaction and its receipts are finalized by consensus
wait_until levels:
Notice that
ExecutedOptimistic and IncludedFinal are not ordered relative to each other — they represent progress on different axes. ExecutedOptimistic guarantees all non-refund receipt outcomes are available but the blocks aren’t final yet. IncludedFinal guarantees the transaction’s block is final but some receipts may still be pending, so you may only get a partial set of receipt outcomes.
Refund receipts
When a receipt executes, NEAR can generate refund receipts for unused gas and the difference between the gas buy and burn prices, subject to the protocol’s refund rules. Failed actions can also generate refunds of attached NEAR.ExecutedOptimistic and Executed don’t wait for refund receipts; Final does. If a system refund’s receiving account has been deleted, the undeliverable amount is burned; see refunding deleted accounts.
Transaction Status
As theReceipts of a Transaction are processed, they get a status:
Success: the actions on the receipt were executed successfullyFailed: an action on the receipt failedUnknown: the receipt is not known by the network
Receipt fails, all the actions in that Receipt are rolled back. Notice that we are talking about the Receipt status, and not the Transaction status.
The status of a transaction is determined by its first receipt, which contains all its actions. If any of the actions in the first receipt fail, the transaction is marked as failed.
Notice that, it could happen that a transaction is marked as successful, but some of its receipt fails. This happens when a FunctionCall successfully spawns a new receipt, but the consequent function call fails. In this case, the transaction is marked as successful because the original function call was successful.
See the examples below for more details.
Status Examples
Status Examples
Example: Transaction with Transfer
bob.nearcreates a transaction to transfer 10 NEAR toalice.near- The transaction is converted into a receipt
- The conversion fails because
bob.neardoes not have enough balance - The transaction is marked as failed ⛔
Example: Deploying a Contract
bob.nearcreates a transaction to:- create the account
contract.bob.near - transfer 5 NEAR to
contract.bob.near - deploy a contract in
contract.bob.near
- create the account
- The transaction is transformed into one receipt
- The account is created, the money transfer and the contract deployed
- The transaction is marked as successful ✅
Example: Deploying a Contract Fails
bob.nearcreates a transaction to:- create the account
contract.bob.near - transfer 5 NEAR to
contract.bob.near - deploy a contract in
contract.bob.near
- create the account
- The transaction is transformed into one receipt
- The account is created, but the transfer fails because
bob.neardoes not have enough balance - The whole process is reverted (i.e. no account is created)
- The transaction is marked as failed ⛔
Example: Calling a Function
bob.nearcreates a transaction to call the functioncross-callincontract.near- The transaction is transformed into one receipt
- The function
cross-callcreates a promise to call the functionexternal-callinexternal.near - The function finishes correctly and the transaction is marked as successful ✅
- A new receipt is created to call the function
external-callinexternal.near - The function
external-callfails - The original transaction is still marked as successful ✅ because the first receipt was successful
Nonce values
Thenonce is a number that is incremented for each transaction sent by the transaction’s Signer.
On a valid transaction, the nonce value should follow these rules:
- When adding a key, the
nonceis automatically assigned - particularly, it is given the valueblock height * 10^6- so the value in theADD_KEYaction is ignored - A transaction is accepted only if the
noncevalue is in the range of:[(current_nonce_of_access_key + 1) .. (block_height * 10^6)]
- Once a transaction is accepted, the access key’s
nonceis set to thenoncevalue of the included transaction - Gas keys hold one nonce per lane (up to 1,024 lanes); a transaction signed with a gas key selects a lane with its
nonce_index, and the rules above apply to that lane’s nonce independently of all other lanes