Transaction Construction and Signing
This page will discuss the transaction format in Polkadot and how to create, sign, and broadcast transactions. Like the other pages in this guide, this page demonstrates some of the available tools. Always refer to each tool's documentation when integrating.
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 for transaction format.
Tip: Optional, the tip to increase transaction priority.
Era Period: Optional, the number of blocks after the checkpoint for which a transaction is valid. If zero, the transaction is immortal
CAUTION
There are risks to making a transaction immortal. If an account is reaped and a user re-funds 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 track and increment the nonce manually if you want to submit multiple valid transactions at the same time.
Each transaction will have its own (or no) parameters to add. For example, the transferKeepAlive
function from the Balances pallet will take:
dest
: Destination address#[compact] value
: Number of tokens (compact encoding)
Serialized transaction format
Before being submitted, transactions are serialized. Serialized transactions are hex encoded SCALE-encoded bytes. The specific serialization is defined in the runtime and can change if the runtime is upgraded, but in general the serialization format can be described as follows:
Compact encoded number of SCALE encoded bytes following this.
1 bit: it is a 0 if no signature is present, or a 1 if it is.
7 bits: the extrinsic version, it is equal to 4 in decimal.- 4 bytes: Spec version of the runtime.
4 bytes: Transaction version of the runtime.
32 bytes: Genesis hash of the chain.
32 bytes: Block hash serving as the era reference. If the transaction is immortal, then this would be the genesis hash.
If there is a signature:
a SCALE encoded
sp_runtime::MultiAddress::Id<AccountId32, u32>
indicating the signer(s) of the transaction.a SCALE encoded
sp_runtime::MultiSignature::{SigningScheme}
with the signature*.a SCALE encoded
sp_runtime::generic::Era
indicating for how long this transaction is valid:If the transaction is immortal, the Era would be simply 0.
Otherwise, it would be a
Vec[u64, u64]
comprising the period and the phase.
Compact encoded
u32
with the nonce.Compact encoded
u128
with the tip paid to the block producer.a SCALE encoded
sp_runtime::traits::SignedExtension<Vec<Text>>
with the additional data and logic associated with this transaction.
The specific transaction parameters or call data, which consists of:
1 byte: the pallet index the transaction is calling into.
1 byte: the function in the pallet the transaction is calling.
variable: the SCALE-encoded parameters required by the function being called.
The metadata provides you with all of the information required to know how 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 Substrate documentation.
* Polkadot supports sr25519, ed25519, and ECDSA as signing schemes.
Summary
Once you have all the necessary information, you will need to:
Construct an unsigned transaction.
Create a signing payload.
Sign the payload.
Serialize the signed payload into a transaction.
Submit the serialized transaction.
Parity provides the following tools to help perform these steps.
Polkadot-JS Tools
Polkadot-JS Tools contains a set of command line tools for interacting with a Substrate client, including one called "Signer CLI" to create, sign, and broadcast transactions.
This example will use the signer submit
command, which will create and submit the transaction. The signer sendOffline
command has the exact same API, but will not broadcast the transaction. submit
and sendOffline
must be connected to a node to fetch the current metadata and construct a valid transaction. Their API has the format:
Signing:
For example, let's send 0.5 DOT from 121X5bEgTZcGQx5NZjwuTjqqKoiG8B2wEAvrUFjuw24ZGZf2
to 15vrtLsCQFG3qRYUcaEeeEih4JwepocNJHkpsrqojqnZPc2y
.
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.). Sign the payload:
Save the output and bring it to the machine that you will broadcast from, enter it into submit
's signature field, and send the transaction (or just return the serialized transaction if using sendOffline
).
Tx Wrapper Polkadot
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 Substrate-based chains will have their own txwrapper-{chain}
implementations. See the examples for a guide.
Import a private key
Derive an address from a public key
Construct a transaction offline
Construct a signing payload
Serialize a signed transaction
Decode payload types
You may want to decode payloads to verify their contents prior to submission.
Check a transaction's hash
Submitting a Signed Payload
There are several ways to submit a signed payload:
Signer CLI (
yarn run:signer submit --tx <signed-transaction> --ws <endpoint>
)RPC with
author_submitExtrinsic
orauthor_submitAndWatchExtrinsic
, the latter of which will subscribe you to events to be notified as a transaction gets validated and included in the chain.
Notes
Some addresses to use in the examples. See Subkey documentation.