Transaction Construction and Signing
This page will discuss the transaction format in Betelgeuse 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.
Betelgeuse provides some basic information 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.
*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 simultaneously.
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)
Once you have all the necessary information, you will need to:
- 1.Construct an unsigned transaction.
- 2.Create a signing payload.
- 3.Sign the payload.
- 4.Serialize the signed payload into a transaction.
- 5.Submit the serialized transaction.
Parity provides the following tools to help perform these steps:
Betelgeuse Web wallet contains a set of command line tools for interacting with a Substrate client, including "Signer CLI" to create, sign, and broadcast transactions.
This example will use the
signer submit
command, which will create and submit the transaction. ThesignersendOffline
command has the same API but will not broadcast the transaction. submit
andsendOffline
must be connected to a node to fetch the current metadata and construct a valid transaction. Their API has the format:yarn run:signer <submit|sendOffline> --account <from-account-ss58> --ws <endpoint> <module.method> [param1] [...] [paramX]
Signing:
yarn run:signer sign --account <from-account-ss58> --seed <seed> --type <sr25519|ed25519> <payload>
For example, let's send 0.5 ETP3 from
121X5bEgTZcGQx5NZjwuTjqqKoiG8B2wEAvrUFjuw24ZGZf2
to 15vrtLsCQFG3qRYUcaEeeEih4JwepocNJHkpsrqojqnZPc2y
.yarn run:signer submit --account 121X5bEgTZcGQx5NZjwuTjqqKoiG8B2wEAvrUFjuw24ZGZf2 --ws ws://127.0.0.1:9944 balances.transferKeepAlive 15vrtLsCQFG3qRYUcaEeeEih4JwepocNJHkpsrqojqnZPc2y 500000000000
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:
yarn run:signer sign --account 121X5bEgTZcGQx5NZjwuTjqqKoiG8B2wEAvrUFjuw24ZGZf2 --seed "pulp gaze fuel ... mercy inherit equal" --type sr25519 0x040300ff4a83f1...a8239139ff3ff7c3f6
Save the output and bring it to the machine that you will broadcast from, enter it into the
submit
's signature field, and send the transaction (or return the serialized transaction if using sendOffline
).Parity provides an SDK called TxWrapper to generate and sign transactions offline if you do not want to use the CLI for signing operations.
- Import private key
import { importPrivateKey } from '@substrate/txwrapper';
const keypair = importPrivateKey(“pulp gaze fuel ... mercy inherit equal”);
- Derive an address from a public key
import { deriveAddress } from '@substrate/txwrapper';
// Public key, can be either hex string, or Uint8Array
const publicKey = “0x2ca17d26ca376087dc30ed52deb74bf0f64aca96fe78b05ec3e720a72adb1235”;
const address = deriveAddress(publicKey);
- Construct a transaction offline
import { methods } from "@substrate/txwrapper";
const unsigned = methods.balances.transferKeepAlive(
{
dest: "15vrtLsCQFG3qRYUcaEeeEih4JwepocNJHkpsrqojqnZPc2y",
value: 500000000000,
},
{
address: "121X5bEgTZcGQx5NZjwuTjqqKoiG8B2wEAvrUFjuw24ZGZf2",
blockHash: "0x1fc7493f3c1e9ac758a183839906475f8363aafb1b1d3e910fe16fab4ae1b582",
blockNumber: 4302222,
genesisHash: "0xe3777fa922cafbff200cadeaea1a76bd7898ad5b89f7848999058b50e715f636",
metadataRpc, // must import from client RPC call state_getMetadata
nonce: 2,
specVersion: 1019,
tip: 0,
eraPeriod: 64, // number of blocks from checkpoint that transaction is valid
transactionVersion: 1,
},
{
metadataRpc,
registry, // Type registry
}
);
- Construct a signing payload
import { methods, createSigningPayload } from '@substrate/txwrapper';
// See "Construct a transaction offline" for "{...}"
const unsigned = methods.balances.transferKeepAlive({...}, {...}, {...});
const signingPayload = createSigningPayload(unsigned, { registry });
- Serialize a signed transaction
import { createSignedTx } from "@substrate/txwrapper";
// Example code, replace `signWithAlice` with actual remote signer.
// An example is given here:
// https://github.com/paritytech/txwrapper/blob/630c38d/examples/index.ts#L50-L68
const signature = await signWithAlice(signingPayload);
const signedTx = createSignedTx(unsigned, signature, { metadataRpc, registry });
- Decode payload types
You may want to decode payloads to verify their contents prior to submission.
import { decode } from "@substrate/txwrapper";
// Decode an unsigned tx
const txInfo = decode(unsigned, { metadataRpc, registry });
// Decode a signing payload
const txInfo = decode(signingPayload, { metadataRpc, registry });
// Decode a signed tx
const txInfo = decode(signedTx, { metadataRpc, registry });
- Check a transaction's hash
import { getTxHash } from ‘@substrate/txwrapper’;
const txHash = getTxHash(signedTx);
There are several ways to submit a signed payload:
- 1.Signer CLI (
yarn run:signer submit --tx <signed-transaction> --ws <endpoint>
) - 2.Substrate API Sidecar
- 3.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.
Last modified 2yr ago