Learn about transactions on the Stacks blockchain.
Transactions are the fundamental unit of execution in the Stacks blockchain. Each transaction is originated from a Stacks account, and is retained in the Stacks blockchain. This guide helps you understand Stacks transactions.
Transactions go through phases before being finally confirmed and propagated on the Stacks network.
Generate: Transactions are assembled according to the encoding specification.
Validate and sign: Transactions are validated to confirm they are well-formed. Required signatures are filled in.
Broadcast: Transactions are sent to a node.
Register: A miner receives transactions, verifies, and adds them to the "mempool", a holding area for all the pending transactions.
Process: Miners review the mempool and select transactions to be included in the next block. Depending on the transaction type, different actions can happen during this step. For example, post-conditions could be verified for a token transfer, tokens defined in a smart contract could be minted, or an attempt to call an existing smart contract method could be made.
Confirm: Miners successfully mine blocks, with each block containing a set of transactions. The transactions inside each new block are successfully propagated to the network.
A transaction can have one of three states once it is registered: pending, success, or failed.
Microblocks are being deprecated with the Nakamoto upgrade.
Transactions can be mined either in an anchor block or in a microblock. If microblocks
are selected, the transaction can be confirmed with a lower latency than the anchor block time.
The anchor mode enum has three options:
OnChainOnly The transaction must be included in an anchored block.
OffChainOnly: The transaction must be included in a microblock.
Any: The leader can choose where to include the transaction.
Here is an example where the transaction must be included in a microblock:
Transaction post-conditions are a feature meant to limit the damage malicious smart contract developers and smart contract bugs can do in terms of destroying (or taking) a user's assets. Post-conditions are executed whenever a contract is deployed or a public method of an existing contract is executed. Whenever a post-condition fails, the transaction is forced to abort.
Post-conditions are meant to be added by the user (or by the user's wallet software) at the moment they sign a transaction. For example, a user may append a post-condition saying that upon successful execution, their account's Stacks (STX) balance should have decreased by no more than 1 STX. If this is not the case, then the transaction would abort, and the account would only pay the transaction fee of processing it.
Each transaction includes a field that describes zero or more post-conditions that must all be true when the transaction finishes running. The post-condition describes only properties of the owner of the asset before the transaction happened. For a transfer transaction, the post-condition is about the sender, for a burn transaction, the post-condition is about the previous owner.
A post-condition includes the following information:
Transactions can be authorized in two ways: standard and sponsored. Authorization determines whether or not the originating account is also the paying account. In a transaction with a standard authorization, the origin and paying accounts are the same. In a transaction with a sponsored authorization, the origin and paying accounts are distinct, and both accounts must sign the transaction for it to be valid (first the origin, then the spender).
Sponsored transactions enable developers and/or infrastructure operators to pay for users to call into their smart contracts, even if users do not have the Stacks (STX) to do so.
In a sponsored transaction, the user first signs the transaction with their origin account and with the intent of it being sponsored (that is, the user must explicitly allow a sponsor to sign), and then the sponsor signs the transaction with their paying account to pay for the user's transaction fee.
The easiest way to construct well-formed transactions is by using the Stacks.js Transactions library. With that library, you can construct the following transaction types:
Stacks token transfer
Smart contract deploy
Smart contract function call
When constructing transactions, it is required to set the network the transaction is intended for. This can be either mainnet or testnet.
Note
Transactions can be constructed and serialized offline. However, it is required to know the nonce and estimated fees ahead of time. Once internet access is available, the transaction can be broadcast to the network. Keep in mind that the nonce and fee might change during offline activity, making the transaction invalid.
Building transactions that call functions in deployed Clarity contracts requires you to construct valid Clarity values to pass to the function as arguments. The Clarity type system contains the following types:
Type
Declaration
Description
Tuple
(tuple (key-name-0 key-type-0) ...)
Typed tuple with named fields
List
(list max-len entry-type)
List of maximum length max-len, with entries of type entry-type
Response
(response ok-type err-type)
Object used by public functions to commit their changes or abort. May be returned or used by other functions as well, however, only public functions have the commit/abort behavior
Optional
(optional some-type)
Option type for objects that can either be (some value) or none
Buffer
(buff max-len)
Byte buffer with maximum length max-len
Principal
principal
Object representing a principal (whether a contract principal or standard principal)
The Stacks.js Transactions library contains TypeScript types and classes that map to the Clarity types, in order to make it easy to construct well-typed Clarity values in JavaScript. These types all extend the abstract class ClarityValue.
Here are samples for Clarity value constructions using this library:
If you develop in TypeScript, the type checker can help prevent you from creating wrongly-typed Clarity values. For example, the following code won't compile since lists are homogeneous in Clarity (meaning they can only contain values of a single type). It is important to include the type variable BooleanCV in this example; otherwise the TypeScript type checker won't know which type the list is of and won't enforce homogeneity.
Broadcasting transactions directly to the Stacks Blockchain API or Node RPC API requires the transaction to be serialized and in hexadecimal representation.
The preceding method returns the following string:
Transaction IDs are generated by hashing the raw transaction with sha512/256
When called the Stacks Blockchain API or Node RPC API, transactions returned will be serialized in a JSON format. Here is a token transfer transaction:
Every transaction contains verifiable signatures that certify its authenticity. These signatures are generated by signing the transaction hash with the origin's private key. The Elliptic Curve Digital Signature Algorithm (ECDSA) is used for signing, with the curve set to secp256k1. The internal structure that encapsulates the signature is the spending condition. Spending conditions include several parameters, such as the public key hash, nonce, fee rate and the recoverable ECDSA signature.
When constructing a transaction using Stacks.js, you can supply the private key and signing will be completed automatically. If you would like to sign the transaction manually, use the TransactionSigner class.
Below are the steps taken to generate the signature internal to the transaction library.
As the name implies, a single signature transaction contains 1 signature from the origin account that authorizes a token spend or smart contract deploy/execution.
A sponsored transaction is one where a second signer sets and pays the transaction fees. The origin must sign the transaction first before the sponsor signs.
With a serialized transaction in the raw format, it can be broadcast to the network using the POST /v2/transactions endpoint:
The API will respond with a HTTP 200 - OK if the transaction was successfully added to the mempool.
There is no explicit time constraint between the construction of a valid signed transaction and when it can be broadcast. There are, however, some constraints to be aware of. The following reasons can deem a transaction invalid after some period:
Token transfer: Nonce changed in-between construction and broadcast
Contract call or deploy: Block height is evaluated (with at-block) and changed in-between construction and broadcast
Once a transaction has been successfully broadcast to the network, the transaction is added to the mempool of the node
that received the broadcast. From the [Bitcoin wiki][]: "a node's memory pool contains all 0-confirmation transactions
across the entire network that that particular node knows about."
So, the set of transactions in the mempool might be
different for each node in the network. For example, when you query the mempool endpoints on
api.mainnet.hiro.so, the response reflects the set of unconfirmed transactions known to the nodes that
service that API.
Miners can employ different heuristics and strategies for deciding which transactions to admit into the mempool and
which transactions to include from the mempool when mining a block. Some transactions may be rejected outright (for
example, if there are insufficient funds at an address) while others might be accepted into the mempool, but not mined
into a block indefinitely (for example, if the transaction fees are too low).
Transactions that are admitted in the mempool but not yet
mined are said to be "pending." The current implementation of stacks-blockchain discards pending mempool
transactions after [256 blocks][].
Nonce: It's crucial that transactions use the correct nonce. Using an incorrect nonce makes it less likely that
the transaction is mined in a timely manner. To determine the correct nonce, query the [accounts][] endpoint of
the node you intend to broadcast your transaction to. The value of the nonce field in the response is the next nonce
that the node expects to consume for that account. Nonce starts at 0, so the first transaction from an account should
be set to nonce=0.
Transaction chaining: Even when using the correct nonce, transactions might arrive at a node out-of-order. For
instance, a transaction with nonce=1 may arrive in the mempool before a nonce=0 transaction. Stacks nodes admit
such out-of-order transactions in the mempool, but only up to a limit ([25 in the current implementation][]). So, you
should limit any chain of unconfirmed transactions from a single account to less than 25. Making this limit higher has
downsides, discussed in this issue. If you need to send
more than 25 transactions per block, consider using multiple accounts or a smart-contract based approach. See
this tool, for example, that allows up to 200 token
transfers in a single transaction.
Transactions on the Stacks network can be queried using the Stacks Blockchain API. The API exposes two interfaces, a RESTful JSON API and a WebSockets API.
Note
The API can be easily consumed using a generated JS client library. The generator uses an OpenAPI specification and supports other languages and frameworks.
Broadcast transactions stay in the mempool for 256 blocks (~42 hours). If a transaction is not confirmed within that time, it is removed from the mempool.
Most transactions stay in the mempool due to nonce issues. If you see a transaction pending for an unusual time, review the nonce of the account and the transaction.
If a transaction is removed from the mempool, the transaction was not processed, and no changes were made to the blockchain state.