Transaction Construction¶
Introduction¶
This page will discuss the transaction format in Polkadot and how to create, sign, and broadcast transactions, as well as highlight some of the commands and tools available for integrators.
Always refer to each tool's documentation when integrating.
For further reading, refer to blocks, transactions, and fees to learn more about the basics.
Transaction Format¶
Polkadot has some basic transaction information that is common to all transactions.
- Address: The SS58-encoded address of the sending account.
- Block hash: The hash of the checkpoint block.
- Block number: The number of the checkpoint block.
- Genesis hash: The genesis hash of the chain.
- Metadata: The SCALE-encoded metadata for the runtime when submitted.
- Nonce: The nonce for this transaction.
- Spec version: The current spec version for the runtime.
- Transaction version: The current version of the transaction format.
- Tip: The tip to increase transaction priority. This is optional when constructing the transaction.
- Mode: The flag indicating whether to verify the metadata hash or not.
- Era period: The number of blocks after the checkpoint for which a transaction is valid. If zero, the transaction is immortal. This is optional when constructing the transaction.
- Metadata hash: The metadata hash which should match the
RUNTIME_METADATA_HASH
environment variable. This is optional when constructing the transaction.
Warning
There are risks to making a transaction immortal. If an account is reaped and a user refunds the account, then they could replay an immortal transaction. Always default to using a mortal extrinsic.
The nonce queried from the System module does not account for pending transactions. You must manually track and increment the nonce if you want to submit multiple valid transactions simultaneously.
Each transaction will have its own parameters, or it may have none to add. For example, the transferKeepAlive
function from the Balances pallet will take:
dest
: Destination address#[compact] value
: Number of tokens (compact encoding)
Refer to the protocol specifications for the concrete specifications and types required to build a transaction.
Mode and Metadata Hash¶
The mode
and metadata hash
fields were introduced in transaction construction to support the optional CheckMetadataHash
Signed Extension. This enables trustless metadata verification by allowing the chain to verify the correctness of the metadata used without the need of a trusted party. This functionality was included in v1.2.5 runtime release by the Fellowship. A user may opt out of this functionality by setting the mode
to 0
. When the mode is 0
, the metadata hash
field is empty/None
.
Serialized Transactions and Metadata¶
Before being submitted, transactions are serialized. Serialized transactions are hex encoded SCALE-encoded bytes. The relay chain runtimes are upgradable, and therefore, any interfaces are subject to change. The metadata allows developers to structure any extrinsics or storage entries accordingly and provides you with all of the information required to construct the serialized call data specific to your transaction. You can read more about the metadata, its format and how to get it in the Subxt documentation.
Transaction Flow¶
The typical transaction workflow is as follows:
- Construct an unsigned transaction.
- Create a signing payload.
- Sign the payload.
- Serialize the signed payload into a transaction.
- Submit the serialized transaction.
There are several tools to help perform these steps.
Polkadot-JS Tools¶
Polkadot-JS Tools contains a set of command-line tools for interacting with a Polkadot SDK client, including one called "Signer CLI" to create, sign, and broadcast transactions.
This example will use the signer submit
command, which creates and submits the transaction. The signer sendOffline
command has the same API, but will not broadcast the transaction. The submit
and sendOffline
must be connected to a node to fetch the current metadata and construct a valid transaction.
Start by installing the Signer CLI.
To create a transaction, you need to connect to a chain, enabling the creation of a transaction using the chain's metadata.
Here is the format for submit
or sendOffline
:
polkadot-js-signer <submit|sendOffline> --account <from-account-ss58> --ws <endpoint> <module.method> [param1] [...] [paramX]
And for signing a transaction:
polkadot-js-signer sign --account <from-account-ss58> --seed <seed> --type <sr25519|ed25519> <payload>
Creating a Transaction, Signing, and Submitting¶
For the sake of this example, create two accounts using the Subkey CLI tool.
Let's say you want to send 1 WND from 5F4c8mNz6schf2WMXQZiz1eyR1GGxrMf2coXpAn8mNjxyzp2
to 5FnudgwK8xJvmujsXXP35pF2xwskhHQzBSRM8KZhXjnEz5gD
on Westend's Asset Hub using polkadot-js-signer
.
First, fund the sending account. You can use the Westend Faucet to do so.
Request some tokens for 5F4c8mNz6schf2WMXQZiz1eyR1GGxrMf2coXpAn8mNjxyzp2
.
Next, call submit
to create the transaction, which will give you the payload to sign.
polkadot-js-signer submit --account 5F4c8mNz6schf2WMXQZiz1eyR1GGxrMf2coXpAn8mNjxyzp2 --ws wss://asset-hub-westend-rpc.n.dwellir.com balances.transferKeepAlive 5FnudgwK8xJvmujsXXP35pF2xwskhHQzBSRM8KZhXjnEz5gD 1000000000000
This will return a payload to sign and an input waiting for a signature.
Take this payload and use your normal signing environment (e.g., air-gapped machine, VM, etc.). In a separate tab of your terminal, sign the payload.
polkadot-js-signer sign --account 5F4c8mNz6schf2WMXQZiz1eyR1GGxrMf2coXpAn8mNjxyzp2 --seed "south ladder exile ... grape rival settle coil" --type sr25519 0x040300ff4a83f1...a8239139ff3ff7c3f6
This will output the transaction's signature.
Paste this signature into the submit
signature field, and send the transaction (or just return the serialized transaction if using sendOffline
).
By default, submit will create a mortal extrinsic with a lifetime of 50 blocks.
Assuming a six-second block time, you will have five minutes to go offline, sign the transaction, paste the signature, and submit the signed transaction.
You will get useful output in the terminal with details like the events that were fired off, as well as the block in which the extrinsic is in.
Full example output
Submitting Pre-Signed Transaction
You can also submit pre-signed transactions, e.g., generated using the sendOffline
command.
Txwrapper¶
If you do not want to use the CLI for signing operations, Parity provides an SDK called txwrapper-core to generate and sign transactions offline. For Polkadot, Kusama, and select parachains, use the txwrapper-polkadot
package. Other Polkadot SDK-based chains will have their own txwrapper-{chain}
implementations. See the examples for a guide.
Creating a Transaction, Signing, and Submitting¶
You will need a network to test the transaction. Let's use chopsticks for this:
You should get a Polkadot network running on port 9944.
The txwrapper
example script will then be used to create and sign transactions.
For this, you will need the txwrapper
library. Let's clone txwrapper
:
git clone https://github.com/paritytech/txwrapper-core
cd txwrapper-core
yarn install && yarn build
cd packages/txwrapper-examples
Build and run the Polkadot txwrapper
example script:
The txwrapper
example script includes several reference examples.
Additional Libraries for Submitting a Transaction¶
Other than Polkadot JS Tools and txwrapper, there are several other libraries that can also be used to submit a signed payload, such as the Sidecar API or using RPC calls with author_submitExtrinsic
or author_submitAndWatchExtrinsic
, the latter of which will subscribe you to events to be notified as a transaction gets validated and included in the chain. You can see all the available libraries in the API Libraries section of the Polkadot Docs.
| Created: August 5, 2025