Near Learn

Transactions, receipts and blocks

How a NEAR transaction becomes receipts: action vs data receipts, which block each one runs in, execution outcomes, and how gas is prepaid, burnt and refunded.

Intermediate6 min read3-question check

On Ethereum a transaction is one atomic state transition: every nested call runs inside it, and a revert anywhere can undo everything. On NEAR a signed transaction is only an envelope. The network validates it, buys its gas, and turns it into a receipt. Receipts are the real unit of execution: each one runs on the shard that holds its receiver account, commits its own state changes, and can emit new receipts that run in later blocks.

Every async pattern in this module follows from that one fact. Once you can picture a call as a little tree of receipts spread over several blocks, callbacks, joins, gas budgets and rollback design stop being surprising.

From transaction to receipts#

  1. Inclusion. The transaction lands in a chunk on the signer’s shard. The signer pays up front: attached deposits plus the prepaid gas at the current gas price. The transaction is converted into a single action receipt addressed to the receiver, carrying all of its actions.
  2. Execution. If signer and receiver are the same account, that receipt is executed in the same block (a “local” receipt). Otherwise it is executed in a later block, on the receiver’s shard.
  3. Fan-out. A FunctionCall action can create promises. Each promise becomes a new action receipt, which needs at least one more block to execute.
  4. Results. When a receipt that someone is waiting on finishes, the runtime sends its result to the waiting receipt as a data receipt.
  5. Refunds. Unused gas and the deposits of failed receipts come back as refund receipts, which are ordinary transfer receipts sent by the special account system.
ReceiptKey fieldsWhat it does
Action receiptactions, input_data_ids, output_data_receivers, signer_id, gas_priceRuns actions (create account, transfer, deploy, function call…) on its receiver. If input_data_ids is non-empty it is postponed until every matching data receipt has arrived.
Data receiptdata_id, data: Option<Vec<u8>>Carries one result to a waiting action receipt. data is the callee’s return value, or None if that receipt failed.
The two receipt kinds, as specified in the Nomicon

A callback is simply an action receipt with an input_data_ids list. It is created at the same time as the call it waits for, parked on your contract’s shard, and released when the data arrives. That is why a callback always runs, even when the call failed: a failure still produces a data receipt, just one with None in it.

A timeline in blocks#

alice.near calls vault.near withdraw, which calls token.near ft_transfer with a callback (best case, no congestion)
Shell
block N    tx signed by alice.near        -> converted to action receipt R1 for vault.near
block N+1  R1  vault.near::withdraw       debit committed; emits R2 (call) and R3 (callback,
                                          postponed: waits for data D1)
block N+2  R2  token.near::ft_transfer    succeeds or panics; emits data receipt D1 -> vault.near
block N+3  R3  vault.near::on_withdraw    runs as soon as D1 is there; reads the result
block N+4  refund receipts                unused gas -> alice.near (sender: "system")

# each receipt that left gas unused emits its own refund receipt one block later;
# a busy shard can push any step back by one or more blocks

With mainnet blocks at roughly 600 ms, a call plus callback is typically done in two to three seconds. But note the shape: withdraw committed in block N+1, two blocks before anyone knew whether the transfer would work. Everything in the rollback lessons follows from that gap.

Outcomes: what explorers and clients see#

The runtime records an execution outcome for the transaction and for every receipt: its status, logs, gas burnt, tokens burnt, the executor account, and the ids of receipts it created. Wallets and RPC clients then derive one “final” status for the whole transaction by following the return chain: if a method returns a promise, the transaction’s result is whatever the last receipt of that chain returns.

StatusMeaning
SuccessValue(bytes)The receipt finished and returned a value (often JSON; empty for ()).
SuccessReceiptId(id)The receipt finished by returning a promise. The real result is in receipt id; follow it.
Failure(error)The receipt panicked, ran out of gas, or an action failed. Its own state changes were discarded.
Execution outcome statuses
See every receipt of a transaction with near-cli-rs
Shell
near transaction view-status 4Hc9...yourTxHash network-config testnet

RPC send_tx lets the client choose how long to wait with wait_until. The default, EXECUTED_OPTIMISTIC, returns once the transaction is included and all non-refund receipts have executed; FINAL also waits for refunds and finality. A front end that waits only for INCLUDED has not seen any of your contract’s logic run yet.

Gas: prepaid, burnt, refunded#

  • Prepaid. The signer buys all attached gas when the transaction is converted, at a pessimistic “buy” price that is at least the current gas price. A single transaction can attach at most 1,000 Tgas (1 PGas). For years the cap was 300 Tgas, so older tutorials still quote that number.
  • Split. A receipt burns gas for its own execution. When it creates promises, the gas you give them (static gas plus a share of leftovers) is moved into the new receipts. The tree of receipts shares one budget: the original prepaid gas.
  • Burnt. Each receipt is charged at the gas price of the block in which the transaction was converted; part of the burnt fee goes to the contract that burnt it.
  • Refunded. Whatever a receipt did not burn or pass on goes back to the signer in a refund receipt, together with the price difference. Since protocol version 78 the protocol defines a refund fee of max(1 Tgas, 5% of the unspent gas), currently set to zero during an adaptation period; still, attach what you need rather than the maximum.

Check yourself

3 questions · progress saved in this browser

  1. 1.What makes a callback receipt wait until the call it depends on has finished?
  2. 2.A transaction calls contract A, which calls B with a callback on A. B succeeds, then A’s callback panics. What is committed?
  3. 3.A user signs a transaction to app.near, and app.near makes one cross-contract call with a callback. In the best case, when does the callback execute relative to the block that included the transaction?