Skip to main content

Signing Transactions with Figment's npm Package

Figment's npm package named slate simplifies the signing and payload decoding process for networks supported by the Staking API. This guide covers everything you will need to sign and decode Staking API transaction payloads.

note

The latest version number of the package and how recently it was published are always available at the top of the page at https://www.npmjs.com/package/@figmentio/slate

Installing the package

npm install @figmentio/slate

Check your package.json dependencies to confirm which version you’re using.

"dependencies": {
...
"@figmentio/slate": "^1.12.0",
...
}

Usage

In your code, require @figmentio/slate. The package exports two methods: decode and sign.

const slate = require("@figmentio/slate");

Sign

This method takes five parameters:

  • <network_code>: The name of the network
  • <version>: The Staking API version number used to generate the transaction
  • <transaction_payload>: The raw, unsigned transaction payload returned by the Staking API
  • ["<signing_private_keys>"]: An array of private keys used to sign the transaction payload
  • {<options>}: (optional) An object, containing any network-specific options. See Signing Options below for details
note

Because the format of private keys differs between networks, attempting to sign a payload with a private key from another network will fail. The private key used to sign the payload must match the delegator's public key or address.

Code Example
await slate.sign(
"near",
"v1",
"12000000736c6174652d64656d6f2e746573746e657400415d57cdf21d28e35e0cfa864e895c18b4bc6a711642d38eb6762af81ec1cf3907cc1038fc5700001600000030316e6f64652e706f6f6c2e663836333937332e6d30192fc3497d275b958dea9de8d901c456292125c060c1304318ae9582a7bff20f0100000002110000006465706f7369745f616e645f7374616b65020000007b7d00e057eb481b000000000025a4000a8bca22040000000000",
["ed25519:<private_key>"]
);
Example Output: Signed Transaction Payload
12000000736c6174652d64656d6f2e746573746e657400415d57cdf21d28e35e0cfa864e895c18b4bc6a711642d38eb6762af81ec1cf3907cc1038fc5700001600000030316e6f64652e706f6f6c2e663836333937332e6d3084edd29289b3a590be582317bb0061016a8df058b7c29a0278f5b82f86c283010100000002080000007769746864726177270000007b22616d6f756e74223a223235303030303030303030303030303030303030303030303030227d00e057eb481b00000000000000000000000000000000000000235397dfd3f0b7ce06a4fe11395e36ce40d12610eae07dcc1dc1d962b38fa9592cdc22f6c9164b0155967a773c369719f71d76b84185ec13c39381343c63cf0b
note

The array of private keys ["<signing_private_keys>"] only requires the private key of the delegator, however it can accommodate multiple private keys in cases where a multisig is used for delegation.

Passing multiple private keys would look like ["<signing_private_key_1>", "<signing_private_key_2>"], etc.

Signing occurs entirely offline; private keys are never exposed during this process.

See the guide on Signing Transactions with the Fireblocks API if you require a custodial signing solution.

Signing Options

Cosmos and Polkadot are currently the only networks that require options to be passed when signing with @figmentio/slate.

The options parameter is an optional fifth parameter to the sign method, which only needs to be included when a network requires additional options.

Cosmos

  • accountNumber This sequential number is generated when an account first receives funds on Cosmos. Account numbers can be determined by querying the Cosmos LCD for account details, or using the Cosmos CLI tool, gaiad.
  • sequence This sequential number is incremented for each transaction an account has made and is also used to prevent replay attacks. The sequence is commonly referred to as a "nonce" which means "number, used once". Sequence numbers can also be found by querying the Cosmos LCD for account details.
  • chainId Cosmos chain IDs refer to a specific chain in the Cosmos ecosystem, preventing transactions from one chain being applied to another. You must supply the appropriate chainId when signing a Cosmos transaction.
    • The chainId value for Cosmos mainnet is currently cosmoshub-4
    • The chainId value for Cosmos testnet is currently theta-testnet-001
Example sign method call for Cosmos, with options
slate.sign("cosmos", "v1", transaction_payload, ["<signing_private_key>"], {
accountNumber: "721397",
sequence: "3",
chainId: "theta-testnet-001",
});

To view an account's accountNumber and sequence, perform a GET request to the Cosmos LCD endpoint /cosmos​/auth​/v1beta1​/accounts​/{address} with the account address you want to query:

Example response: GET /cosmos​/auth​/v1beta1​/accounts​/cosmos1nm0rrq86ucezaf8uj35pq9fpwr5r82cl8sc7p5
{
"account": {
"@type": "/cosmos.auth.v1beta1.BaseAccount",
"address": "cosmos1nm0rrq86ucezaf8uj35pq9fpwr5r82cl8sc7p5",
"pub_key": {
"@type": "/cosmos.crypto.secp256k1.PubKey",
"key": "ApEvbc+4QJiDoaaXG6v0K3A4zmPON8pu4uzCm4o142u/"
},
"account_number": "15652",
"sequence": "114429"
}
}

Polkadot

  • rpcUrl WebSocket URL of the Polkadot RPC endpoint. In most cases, this value should be the public RPC endpoint for the chain you are signing a transaction on. If running your own node, you can specify its RPC endpoint.
    • For Polkadot mainnet, use wss://rpc.polkadot.io
    • For Westend testnet, use wss://westend-rpc.polkadot.io
Example sign method call for Polkadot, with options
slate.sign("polkadot", "v1", transaction_payload, ["<signing_private_key>"], {
rpcUrl: "wss://westend-rpc.polkadot.io",
});

Decode

The decode method takes six parameters:

  • <network_code>: The name of the network (ex. near)
  • <operation>: The operation the transaction is built to accomplish (ex. staking, see below for a complete list)
  • <version>: The Staking API version number used to generate the transaction (ex. v1)
  • <transaction_type>: The type of transaction being decoded (ex. delegateTransaction, see below for a complete list)
  • <transaction_payload>: The raw, unsigned transaction payload returned by the Staking API
  • {<options>}: (optional) An object, containing any network-specific options. See Decode Options below for details
Code Example
await slate.decode(
"near",
"staking",
"v1",
"delegateTransaction",
"12000000736c6174652d64656d6f2e746573746e657400415d57cdf21d28e35e0cfa864e895c18b4bc6a711642d38eb6762af81ec1cf3907cc1038fc5700001600000030316e6f64652e706f6f6c2e663836333937332e6d30192fc3497d275b958dea9de8d901c456292125c060c1304318ae9582a7bff20f0100000002110000006465706f7369745f616e645f7374616b65020000007b7d00e057eb481b000000000025a4000a8bca22040000000000"
);
Example Output: Decoded Transaction Payload
{
"delegatorPubkey": "5QA46X6NkNmsFdu9xWVBaLNowh9gGeF1c5r9u6NcxaLY",
"delegatorAddress": "slate-demo.testnet",
"validatorAddress": "01node.pool.f863973.m0",
"amount": "5"
}

Decode Options

The options parameter is an optional sixth parameter to the decode method, which only needs to be included when a network requires additional options.

Avalanche

  • hrp The Human-Readable-Part of the Bech32 encoded address must be passed when decoding Avalanche transaction payloads.
    • For mainnet transactions, the value must be avax
    • For testnet transactions, the value must be fuji
Example decode method call for Avalanche testnet, with options
slate.decode(
"avalanche",
"staking",
"v1",
"delegateTransaction",
transaction_payload,
{
hrp: "fuji",
}
);
note

There are two less commonly used hrp values: local and custom - however, these are not relevant to the Staking API.

Specifically, the hrp is used by AvalancheJS BinTools.

Read more about Bech32 address encoding in the Avalanche Knowledgebase.

Operations and Transaction Types

These operations are used by the Staking API and the associated transaction types are used when decoding a transaction payload.

  • staking
    • delegateTransaction

References