Skip to content

Commit 79950e2

Browse files
committed
add transaction construction guide for integrators
1 parent 81ab749 commit 79950e2

File tree

3 files changed

+515
-1
lines changed

3 files changed

+515
-1
lines changed

develop/toolkit/integrations/.pages

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ nav:
33
- index.md
44
- 'Wallets': wallets.md
55
- 'Indexers': indexers.md
6-
- 'Oracles': oracles.md
6+
- 'Oracles': oracles.md
7+
- 'Transaction Construction': transaction-construction.md
Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
---
2+
title: Transaction Construction
3+
description: Understand how to construct, sign, and broadcast transactions in the Polkadot ecosystem using various tools and libraries.
4+
---
5+
6+
# Transaction Construction
7+
8+
## Introduction
9+
10+
This page will discuss the transaction format in Polkadot and how to create, sign, and broadcast
11+
transactions. Like the other pages in this guide, this page demonstrates some of the available
12+
tools.
13+
14+
**Always refer to each tool's documentation when integrating.**
15+
16+
!!!note "Complementary Reading"
17+
Learn about the basics of [blocks, transactions, and fees](/polkadot-protocol/parachain-basics/blocks-transactions-fees/).
18+
19+
## Transaction Format
20+
21+
Polkadot has some basic transaction information that is common to all transactions.
22+
23+
- Address: The SS58-encoded address of the sending account.
24+
- Block Hash: The hash of the [checkpoint](/polkadot-protocol/parachain-basics/blocks-transactions-fees/transactions/#transaction-mortality) block.
25+
- Block Number: The number of the checkpoint block.
26+
- Genesis Hash: The genesis hash of the chain.
27+
- Metadata: The SCALE-encoded metadata for the runtime when submitted.
28+
- Nonce: The nonce for this transaction.\*
29+
- Spec Version: The current spec version for the runtime.
30+
- Transaction Version: The current version for transaction format.
31+
- Tip: Optional, the [tip](https://docs.polkadot.com/polkadot-protocol/parachain-basics/blocks-transactions-fees/fees/#how-fees-are-calculated) to increase transaction priority.
32+
- Mode: The flag indicating whether to verify the metadata hash or not.
33+
- Era Period: Optional, the number of blocks after the checkpoint for which a transaction is valid.
34+
If zero, the transaction is [immortal](/polkadot-protocol/parachain-basics/blocks-transactions-fees/transactions/#transaction-mortality)
35+
- MetadataHash: Optional, the metadata hash which should match the RUNTIME_METADATA_HASH environment
36+
variable.
37+
38+
!!!warning
39+
There are risks to making a transaction immortal. If an account is reaped and a user re-funds the
40+
account, then they could replay an immortal transaction. Always default to using a mortal extrinsic.
41+
42+
The nonce queried from the System module does not account for pending transactions. You must track
43+
and increment the nonce manually if you want to submit multiple valid transactions at the same time.
44+
45+
Each transaction will have its own (or no) parameters to add. For example, the `transferKeepAlive`
46+
function from the Balances pallet will take:
47+
48+
- `dest`: Destination address
49+
- `#[compact] value`: Number of tokens (compact encoding)
50+
51+
Refer to [the protocol specifications](https://spec.polkadot.network/id-extrinsics), for the
52+
concrete specifications and types to build a transaction.
53+
54+
**Mode and MetadataHash**
55+
56+
The mode and metadataHash fields were introduced in transaction construction to support the optional
57+
[`CheckMetadataHash` Signed Extension](https://github.com/polkadot-fellows/RFCs/blob/main/text/0078-merkleized-metadata.md).
58+
This enables trustless metadata verification by allowing the chain to verify the correctness of the
59+
metadata used without the need of a trusted party. This functionality was included in
60+
[v1.2.5](https://github.com/polkadot-fellows/runtimes/releases/tag/v1.2.5) runtime release by the
61+
Fellowship. A user may up out of this functionality by setting the mode to `0`. When the mode is 00,
62+
the `metadataHash` field is empty/None.
63+
64+
**Serialized transactions and metadata**
65+
66+
Before being submitted, transactions are serialized. Serialized transactions are hex encoded
67+
SCALE-encoded bytes. The relay chain runtimes are upgradable and therefore any interfaces are
68+
subject to change, the metadata allows developers to structure any extrinsics or storage entries
69+
accordingly. The metadata provides you with all of the information required to know how to construct
70+
the serialized call data specific to your transaction. You can read more about the metadata, its
71+
format and how to get it in the [Subxt documentation](/polkadot-protocol/parachain-basics/chain-data/#use-subxt).
72+
73+
**Transaction Flow**
74+
75+
The typical transaction workflow is as follows:
76+
77+
1. Construct an unsigned transaction.
78+
2. Create a signing payload.
79+
3. Sign the payload.
80+
4. Serialize the signed payload into a transaction.
81+
5. Submit the serialized transaction.
82+
83+
Parity provides the following tools to help perform these steps.
84+
85+
## Polkadot-JS Tools
86+
87+
[Polkadot-JS Tools](https://github.com/polkadot-js/tools) contains a set of command line tools for
88+
interacting with a Substrate client, including one called "Signer CLI" to create, sign, and
89+
broadcast transactions.
90+
91+
This example will use the `signer submit` command, which will create and submit the transaction. The
92+
`signer sendOffline` command has the exact same API, but will not broadcast the transaction.
93+
`submit` and `sendOffline` must be connected to a node to fetch the current metadata and construct a
94+
valid transaction. Their API has the format:
95+
96+
```bash
97+
yarn run:signer <submit|sendOffline> --account <from-account-ss58> --ws <endpoint> <module.method> [param1] [...] [paramX]
98+
```
99+
100+
Signing:
101+
102+
```bash
103+
yarn run:signer sign --account <from-account-ss58> --seed <seed> --type <sr25519|ed25519> <payload>
104+
```
105+
106+
For example, let's send 0.5 DOT from `121X5bEgTZcGQx5NZjwuTjqqKoiG8B2wEAvrUFjuw24ZGZf2` to
107+
`15vrtLsCQFG3qRYUcaEeeEih4JwepocNJHkpsrqojqnZPc2y`.
108+
109+
```bash
110+
yarn run:signer submit --account 121X5bEgTZcGQx5NZjwuTjqqKoiG8B2wEAvrUFjuw24ZGZf2 --ws ws://127.0.0.1:9944 balances.transferKeepAlive 15vrtLsCQFG3qRYUcaEeeEih4JwepocNJHkpsrqojqnZPc2y 5000000000
111+
```
112+
113+
This will return a payload to sign and an input waiting for a signature. Take this payload and use
114+
your normal signing environment (e.g. air gapped machine, VM, etc.). Sign the payload:
115+
116+
```bash
117+
yarn run:signer sign --account 121X5bEgTZcGQx5NZjwuTjqqKoiG8B2wEAvrUFjuw24ZGZf2 --seed "pulp gaze fuel ... mercy inherit equal" --type sr25519 0x040300ff4a83f1...a8239139ff3ff7c3f6
118+
```
119+
120+
Save the output and bring it to the machine that you will broadcast from, enter it into `submit`'s
121+
signature field, and send the transaction (or just return the serialized transaction if using `sendOffline`).
122+
123+
## TxWrapper
124+
125+
If you do not want to use the CLI for signing operations, Parity provides an SDK called
126+
[TxWrapper Core](https://github.com/paritytech/txwrapper-core) to generate and sign transactions
127+
offline. For Polkadot, Kusama, and select parachains, use the `txwrapper-polkadot` package. Other
128+
Substrate-based chains will have their own `txwrapper-{chain}` implementations. See the
129+
[examples](https://github.com/paritytech/txwrapper-core/blob/main/packages/txwrapper-examples/README.md)
130+
for a guide.
131+
132+
**Import a private key**
133+
134+
```ts
135+
import { importPrivateKey } from '@substrate/txwrapper-polkadot';
136+
137+
const keypair = importPrivateKey(“pulp gaze fuel ... mercy inherit equal”);
138+
```
139+
140+
**Derive an address from a public key**
141+
142+
```ts
143+
import { deriveAddress } from '@substrate/txwrapper-polkadot';
144+
145+
// Public key, can be either hex string, or Uint8Array
146+
const publicKey =0x2ca17d26ca376087dc30ed52deb74bf0f64aca96fe78b05ec3e720a72adb1235”;
147+
const address = deriveAddress(publicKey);
148+
```
149+
150+
**Construct a transaction offline**
151+
152+
```ts
153+
import { methods } from "@substrate/txwrapper-polkadot";
154+
155+
const unsigned = methods.balances.transferKeepAlive(
156+
{
157+
dest: "15vrtLsCQFG3qRYUcaEeeEih4JwepocNJHkpsrqojqnZPc2y",
158+
value: 5000000000,
159+
},
160+
{
161+
address: "121X5bEgTZcGQx5NZjwuTjqqKoiG8B2wEAvrUFjuw24ZGZf2",
162+
blockHash: "0x1fc7493f3c1e9ac758a183839906475f8363aafb1b1d3e910fe16fab4ae1b582",
163+
blockNumber: 4302222,
164+
genesisHash: "0xe3777fa922cafbff200cadeaea1a76bd7898ad5b89f7848999058b50e715f636",
165+
metadataRpc, // must import from client RPC call state_getMetadata
166+
nonce: 2,
167+
specVersion: 1019,
168+
tip: 0,
169+
eraPeriod: 64, // number of blocks from checkpoint that transaction is valid
170+
transactionVersion: 1,
171+
},
172+
{
173+
metadataRpc,
174+
registry, // Type registry
175+
}
176+
);
177+
```
178+
179+
**Construct a signing payload**
180+
181+
```ts
182+
import { methods, createSigningPayload } from '@substrate/txwrapper-polkadot';
183+
184+
// See "Construct a transaction offline" for "{...}"
185+
const unsigned = methods.balances.transferKeepAlive({...}, {...}, {...});
186+
const signingPayload = createSigningPayload(unsigned, { registry });
187+
```
188+
189+
**Serialize a signed transaction**
190+
191+
```ts
192+
import { createSignedTx } from "@substrate/txwrapper-polkadot";
193+
194+
// Example code, replace `signWithAlice` with actual remote signer.
195+
// An example is given here:
196+
// https://github.com/paritytech/txwrapper-core/blob/b213cabf50f18f0fe710817072a81596e1a53cae/packages/txwrapper-core/src/test-helpers/signWithAlice.ts
197+
const signature = await signWithAlice(signingPayload);
198+
const signedTx = createSignedTx(unsigned, signature, { metadataRpc, registry });
199+
```
200+
201+
**Decode payload types**
202+
203+
You may want to decode payloads to verify their contents prior to submission.
204+
205+
```ts
206+
import { decode } from "@substrate/txwrapper-polkadot";
207+
208+
// Decode an unsigned tx
209+
const txInfo = decode(unsigned, { metadataRpc, registry });
210+
211+
// Decode a signing payload
212+
const txInfo = decode(signingPayload, { metadataRpc, registry });
213+
214+
// Decode a signed tx
215+
const txInfo = decode(signedTx, { metadataRpc, registry });
216+
```
217+
218+
**Check a transaction's hash**
219+
220+
```ts
221+
import { getTxHash } from ‘@substrate/txwrapper-polkadot’;
222+
const txHash = getTxHash(signedTx);
223+
```
224+
225+
## Submitting a Signed Payload
226+
227+
There are several ways to submit a signed payload:
228+
229+
1. Signer CLI (`yarn run:signer submit --tx <signed-transaction> --ws <endpoint>`)
230+
1. [Substrate API Sidecar](https://docs.polkadot.com/develop/toolkit/api-libraries/sidecar/#sidecar-api)
231+
1. [RPC](https://docs.polkadot.com/develop/toolkit/api-libraries/) with `author_submitExtrinsic` or
232+
`author_submitAndWatchExtrinsic`, the latter of which will subscribe you to events to be notified
233+
as a transaction gets validated and included in the chain.
234+
235+
## Example Addresses
236+
237+
Some addresses to use in the examples. See
238+
[Subkey documentation](https://docs.polkadot.com/polkadot-protocol/basics/accounts/#using-subkey).
239+
240+
```bash
241+
$ subkey --network polkadot generate
242+
Secret phrase `pulp gaze fuel ... mercy inherit equal` is account:
243+
Secret seed: 0x57450b3e09ba4598 ... ... ... ... ... ... ... .. 219756eeba80bb16
244+
Public key (hex): 0x2ca17d26ca376087dc30ed52deb74bf0f64aca96fe78b05ec3e720a72adb1235
245+
Account ID: 0x2ca17d26ca376087dc30ed52deb74bf0f64aca96fe78b05ec3e720a72adb1235
246+
SS58 Address: 121X5bEgTZcGQx5NZjwuTjqqKoiG8B2wEAvrUFjuw24ZGZf2
247+
248+
$ subkey --network polkadot generate
249+
Secret phrase `exercise auction soft ... obey control easily` is account:
250+
Secret seed: 0x5f4bbb9fbb69261a ... ... ... ... ... ... ... .. 4691ed7d1130fbbd
251+
Public key (hex): 0xda04de6cd781c98acf0693dfb97c11011938ad22fcc476ed0089ac5aec3fe243
252+
Account ID: 0xda04de6cd781c98acf0693dfb97c11011938ad22fcc476ed0089ac5aec3fe243
253+
SS58 Address: 15vrtLsCQFG3qRYUcaEeeEih4JwepocNJHkpsrqojqnZPc2y
254+
```

0 commit comments

Comments
 (0)