Skip to Content
API Reference@parity/product-sdk-txOverview

@parity/product-sdk-tx

Submit Polkadot transactions and follow them to finality.

submitAndWatch signs, broadcasts, and tracks a single extrinsic through its lifecycle; batchSubmitAndWatch does the same for a list of calls. The package also bundles the things you almost always reach for next to a real submission: dry-run helpers, Asset Hub account mapping, retry primitives, dev signers, and a typed error hierarchy with formatters that turn dispatch errors into readable messages.

npm install @parity/product-sdk-tx

Exports

Classes

NameSummary
TxAccountMappingErrorError thrown when account mapping fails.
TxBatchErrorError specific to batch transaction construction (e.g., empty calls array).
TxDispatchErrorThe transaction was included on-chain but the dispatch failed.
TxDryRunErrorA dry-run simulation failed before the transaction was submitted on-chain.
TxErrorBase class for all transaction errors. Use instanceof TxError to catch any tx-related error.
TxSigningRejectedErrorThe user rejected the signing request in their wallet.
TxTimeoutErrorThe transaction did not finalize within the configured timeout. It may still be processing on-chain.

Functions

NameSummary
applyWeightBuffer()Apply a safety buffer to weight estimates from a dry-run result.
batchSubmitAndWatch()Batch multiple transactions into a single Substrate Utility batch and submit.
calculateDelay()Calculate delay with exponential backoff and jitter.
createDevSigner()Create a PolkadotSigner for a standard Substrate dev account.
ensureAccountMapped()Ensure an account’s SS58 address is mapped to its H160 EVM address on-chain.
extractTransaction()Validate an Ink SDK dry-run result and extract the submittable transaction.
formatDispatchError()Extract a human-readable error from a transaction result’s dispatch error.
formatDryRunError()Extract a human-readable error from a failed dry-run result.
getDevPublicKey()Get the public key bytes for a dev account.
isAccountMapped()Check if an address is mapped on-chain.
isSigningRejection()Check if an error looks like a user-rejected signing request.
submitAndWatch()Submit a transaction and watch its lifecycle through signing, broadcasting,
withRetry()Wrap an async function with retry logic and exponential backoff.

Interfaces

NameSummary
BatchApiMinimal structural type for a PAPI typed API with the Utility pallet.
BatchSubmitOptionsOptions for batchSubmitAndWatch. Extends SubmitOptions with batch mode.
EnsureAccountMappedOptionsOptions for ensureAccountMapped.
MappingCheckerMinimal interface for checking if an address is mapped on-chain.
RetryOptionsOptions for withRetry.
ReviveApiMinimal typed API shape for Revive.map_account().
SubmitOptionsOptions for submitAndWatch.
SubmittableTransactionStructural type for any transaction object that supports Observable-based
TxResultSuccessful transaction result.
WeightSubstrate weight representing computational and storage resources.

Type Aliases

NameSummary
BatchableCallA transaction or decoded call that can be included in a batch.
BatchModeBatch execution mode corresponding to Substrate’s Utility pallet.
DevAccountNameStandard Substrate dev account names.
TxEventPAPI transaction event (discriminated union).
TxStatusTransaction lifecycle status for UI callbacks.
WaitForWhen to resolve the submission promise.

Classes

class TxAccountMappingError

Error thrown when account mapping fails.

Extends: Error

Constructors

constructor
new TxAccountMappingError(message: string, options?: ErrorOptions): TxAccountMappingError

class TxBatchError

Error specific to batch transaction construction (e.g., empty calls array).

Extends: TxError

Constructors

constructor
new TxBatchError(message: string): TxBatchError

class TxDispatchError

The transaction was included on-chain but the dispatch failed.

Extends: TxError

Constructors

constructor
new TxDispatchError(dispatchError: unknown, formatted: string): TxDispatchError

Properties

dispatchError
propertyreadonlyunknown

Raw dispatch error from polkadot-api.

formatted
propertyreadonlystring

Human-readable error string (e.g., “Revive.ContractReverted”).

class TxDryRunError

A dry-run simulation failed before the transaction was submitted on-chain.

Thrown by extractTransaction when the dry-run result indicates failure. Carries structured error information so callers can distinguish revert reasons from dispatch errors programmatically.

Extends: TxError

Examples

try { const tx = extractTransaction(await contract.query("mint", { origin, data })); } catch (e) { if (e instanceof TxDryRunError) { console.log(e.revertReason); // "InsufficientBalance" (if contract provided one) console.log(e.formatted); // "Revive.StorageDepositNotEnoughFunds" } }

Constructors

constructor
new TxDryRunError(raw: unknown, formatted: string, revertReason?: string): TxDryRunError

Properties

formatted
propertyreadonlystring

Human-readable error string derived from the dry-run result.

raw
propertyreadonlyunknown

The raw dry-run result for programmatic inspection.

revertReason
propertyreadonlyoptionalstring

Solidity revert reason, if the contract provided one.

class TxError

Base class for all transaction errors. Use instanceof TxError to catch any tx-related error.

Extends: Error

Constructors

constructor
new TxError(message: string, options?: ErrorOptions): TxError

class TxSigningRejectedError

The user rejected the signing request in their wallet.

Extends: TxError

Constructors

constructor
new TxSigningRejectedError(): TxSigningRejectedError

class TxTimeoutError

The transaction did not finalize within the configured timeout. It may still be processing on-chain.

Extends: TxError

Constructors

constructor
new TxTimeoutError(timeoutMs: number): TxTimeoutError

Properties

timeoutMs
propertyreadonlynumber

Functions

applyWeightBuffer()

Apply a safety buffer to weight estimates from a dry-run result.

Dry-run weight estimates reflect the exact execution cost at the time of simulation. On-chain conditions can change between dry-run and actual submission (storage growth, state changes by other transactions), so a buffer prevents unexpected OutOfGas failures.

The default 25% buffer matches the convention used across Polkadot ecosystem tooling.

applyWeightBuffer(weight: Weight, options?: { percent?: number }): Weight

Parameters

  • weight: The weight_required from a ReviveApi.call or ReviveApi.eth_transact dry-run.
  • options: Override the buffer percentage (default: 25%).

Returns

A new weight with both components scaled up.

Examples

const dryRun = await api.apis.ReviveApi.call(origin, dest, value, undefined, undefined, data); const tx = api.tx.Revive.call({ dest, value, data, weight_limit: applyWeightBuffer(dryRun.weight_required), storage_deposit_limit: dryRun.storage_deposit.value, });
applyWeightBuffer(dryRun.weight_required, { percent: 50 });

batchSubmitAndWatch()

Batch multiple transactions into a single Substrate Utility batch and submit.

Extracts .decodedCall from each transaction (handling Ink SDK AsyncTransaction wrappers), wraps them in Utility.batch_all (or batch/force_batch via the mode option), and submits via submitAndWatch with full lifecycle tracking.

batchSubmitAndWatch(calls: BatchableCall[], api: BatchApi, signer: PolkadotSigner, options?: BatchSubmitOptions): Promise<TxResult>

Parameters

  • calls: Array of transactions, AsyncTransactions, or raw decoded calls to batch.
  • api: A typed API with tx.Utility.batch_all/batch/force_batch. Works with any chain that has the Utility pallet — no chain-specific imports required. All calls must target the same chain as this API. Do not mix decoded calls from different chains (e.g., Asset Hub and Bulletin) in a single batch.
  • signer: The signer to use. Can come from the Host API (getProductAccountSigner) or createDevSigner.
  • options: Optional BatchSubmitOptions (extends SubmitOptions with mode).

Returns

The transaction result from the batch submission.

Throws

  • If calls is empty.
  • If an AsyncTransaction resolves without a .decodedCall property.
  • If the batch transaction does not reach the target state within timeoutMs.
  • If the on-chain dispatch fails.
  • If the user rejects signing in their wallet.

Examples

import { batchSubmitAndWatch } from "@parity/product-sdk-tx"; const tx1 = api.tx.Balances.transfer_keep_alive({ dest: addr1, value: 1_000n }); const tx2 = api.tx.Balances.transfer_keep_alive({ dest: addr2, value: 2_000n }); const result = await batchSubmitAndWatch([tx1, tx2], api, signer, { onStatus: (status) => console.log(status), });

calculateDelay()

Calculate delay with exponential backoff and jitter.

Jitter prevents thundering-herd when multiple clients retry simultaneously. The delay is min(baseDelay * 2^attempt, maxDelay) * random(0.5, 1.0).

calculateDelay(attempt: number, baseDelayMs: number, maxDelayMs: number): number

createDevSigner()

Create a PolkadotSigner for a standard Substrate dev account.

Dev accounts use the well-known Substrate dev mnemonic (DEV_PHRASE) with Sr25519 key derivation at the path //{Name}. These accounts have known private keys and are pre-funded on dev/test chains.

Only for local development, scripts, and testing. Never use in production.

createDevSigner(name: DevAccountName): PolkadotSigner

Parameters

  • name: Dev account name (“Alice”, “Bob”, “Charlie”, “Dave”, “Eve”, or “Ferdie”).

Returns

A PolkadotSigner that can sign transactions.

Examples

import { createDevSigner } from "@parity/product-sdk-tx"; const alice = createDevSigner("Alice"); const result = await submitAndWatch(tx, alice);

ensureAccountMapped()

Ensure an account’s SS58 address is mapped to its H160 EVM address on-chain.

Account mapping is a prerequisite for any EVM contract interaction on Asset Hub. This function checks the on-chain mapping status and, if unmapped, submits a Revive.map_account() transaction and waits for inclusion.

Idempotent — safe to call multiple times. Returns immediately if already mapped.

ensureAccountMapped(address: string, signer: PolkadotSigner, checker: MappingChecker, api: ReviveApi, options?: EnsureAccountMappedOptions): Promise<TxResult | null>

Parameters

  • address: The SS58 address to check/map.
  • signer: The signer for the account (must match the address).
  • checker: An object with addressIsMapped(). Easiest path: build one from a pallet-revive-capable typed API by querying Revive.OriginalAccount.
  • api: A typed API with tx.Revive.map_account().
  • options: Optional timeout and status callback.

Returns

The transaction result if mapping was performed, or null if already mapped.

Throws

  • If the mapping check or transaction fails.
  • If the map_account transaction fails on-chain.
  • If the mapping transaction times out.

Examples

import { ensureAccountMapped } from "@parity/product-sdk-tx"; import { ss58ToH160 } from "@parity/product-sdk-address"; const api = client.getTypedApi(paseo_asset_hub); const checker = { addressIsMapped: async (addr: string) => (await api.query.Revive.OriginalAccount.getValue(ss58ToH160(addr))) !== undefined, }; await ensureAccountMapped(address, signer, checker, api); // Account is now mapped — safe to call PolkaVM/Solidity contracts

extractTransaction()

Validate an Ink SDK dry-run result and extract the submittable transaction.

Replaces the 5-10 line boilerplate that every contract interaction repeats: check success, parse the error, verify send() exists, and call it.

Works with any object whose shape matches the Ink SDK contract query result (typed structurally — no Ink SDK import required):

  • contract.query("method", { origin, data }) (Ink SDK)
  • contract.write("method", args, origin) (patched SDK wrappers)
  • Any object with { success: boolean; value?: { send?(): ... } }
extractTransaction(result: { error?: unknown; success: boolean; value?: unknown }): SubmittableTransaction

Parameters

  • result: The dry-run result from a contract query or write simulation.

Returns

The submittable transaction, ready to pass to submitAndWatch.

Throws

  • If the dry run failed or the result has no send().

Examples

import { extractTransaction, submitAndWatch, createDevSigner } from "@parity/product-sdk-tx"; const dryRun = await contract.query("createItem", { origin, data: { name, price } }); const tx = extractTransaction(dryRun); const result = await submitAndWatch(tx, createDevSigner("Alice"));
const tx = extractTransaction(await contract.query("transfer", { origin, data })); const result = await withRetry(() => submitAndWatch(tx, signer));

formatDispatchError()

Extract a human-readable error from a transaction result’s dispatch error.

PAPI dispatch errors for pallet modules are nested: { type: "Module", value: { type: "Revive", value: { type: "ContractReverted" } } }

This walks the chain to build a string like "Revive.ContractReverted".

formatDispatchError(result: { dispatchError?: unknown; ok: boolean }): string

Parameters

  • result: A transaction result with ok and optional dispatchError.

Returns

A human-readable error string, or "" if the result is ok, or "unknown error" if the dispatch error cannot be decoded.

formatDryRunError()

Extract a human-readable error from a failed dry-run result.

Handles every error shape found across the Polkadot contract ecosystem:

  1. Revert reason (Ink SDK patched results / EVM contracts): { value: { revertReason: "InsufficientBalance" } }

  2. Nested dispatch errors (raw Ink SDK / pallet errors): { value: { type: "Module", value: { type: "Revive", value: { type: "StorageDepositNotEnoughFunds" } } } } Delegates to formatDispatchError for the Module.Pallet.Error chain.

  3. ReviveApi runtime messages (eth_transact / ReviveApi.call): { value: { type: "Message", value: "Insufficient balance for gas * price + value" } }

  4. ReviveApi contract revert data: { value: { type: "Data", value: "0x08c379a0..." } }

  5. Wrapped raw errors (patched SDK wrappers): { value: { raw: { type: "Message", value: "..." } } }

  6. Generic error field: { error: { type: "ContractTrapped" } } or { error: { name: "..." } }

formatDryRunError(result: { error?: unknown; success?: boolean; value?: unknown }): string

Parameters

  • result: A dry-run result with at least success, and optionally value / error.

Returns

A human-readable error string, or "" if the result succeeded.

getDevPublicKey()

Get the public key bytes for a dev account.

Useful for address derivation or identity checks in tests without needing the full signer.

getDevPublicKey(name: DevAccountName): Uint8Array

Parameters

  • name: Dev account name.

Returns

32-byte Sr25519 public key.

isAccountMapped()

Check if an address is mapped on-chain.

Convenience wrapper around checker.addressIsMapped() with error handling.

isAccountMapped(address: string, checker: MappingChecker): Promise<boolean>

isSigningRejection()

Check if an error looks like a user-rejected signing request.

Different wallets use different error messages when the user rejects signing: “Cancelled”, “Rejected”, “User rejected”, “denied”. This checks for common patterns as a best-effort heuristic. Non-Error values always return false.

isSigningRejection(error: unknown): boolean

Parameters

  • error: The error to check.

Returns

true if the error message matches a known rejection pattern.

submitAndWatch()

Submit a transaction and watch its lifecycle through signing, broadcasting, block inclusion, and (optionally) finalization.

submitAndWatch(tx: SubmittableTransaction, signer: PolkadotSigner, options?: SubmitOptions): Promise<TxResult>

Parameters

  • tx: A transaction object with signSubmitAndWatch. Works with raw PAPI transactions and Ink SDK AsyncTransaction wrappers (resolved automatically).
  • signer: The signer to use. Can come from the Host API (getProductAccountSigner) or createDevSigner.
  • options: Submission options (waitFor, timeout, mortality, status callback).

Returns

The transaction result once included/finalized.

Throws

  • If the transaction does not reach the target state within timeoutMs.
  • If the on-chain dispatch fails (e.g., insufficient balance, contract revert).
  • If the user rejects signing in their wallet.

withRetry()

Wrap an async function with retry logic and exponential backoff.

Only retries transient errors (network disconnects, temporary RPC failures). Deterministic errors (TxDispatchError, TxBatchError), user rejections (TxSigningRejectedError), and timeouts (TxTimeoutError) are rethrown immediately without retry.

withRetry(fn: () => Promise<T>, options?: RetryOptions): Promise<T>

Parameters

  • fn: The async function to retry.
  • options: Retry configuration.

Returns

The result of the first successful call.

Examples

const result = await withRetry( () => submitAndWatch(tx, signer), { maxAttempts: 3, baseDelayMs: 1_000 }, );

Interfaces

interface BatchApi

Minimal structural type for a PAPI typed API with the Utility pallet.

Structural so it works with any chain that has the Utility pallet, without importing chain-specific descriptors.

Properties

tx
property{ Utility: { batch: unknown; batch_all: unknown; force_batch: unknown } }

interface BatchSubmitOptions

Options for batchSubmitAndWatch. Extends SubmitOptions with batch mode.

Extends: SubmitOptions

Properties

mode
propertyoptionalBatchMode

Batch execution mode. Default: "batch_all" (atomic, all-or-nothing).

  • "batch_all" — Atomic. Reverts all calls if any single call fails.
  • "batch" — Best-effort. Stops at first failure but earlier successful calls are not reverted.
  • "force_batch" — Like batch but continues executing remaining calls after failures (never aborts early).

interface EnsureAccountMappedOptions

Options for ensureAccountMapped.

Properties

onStatus
propertyoptional(status: "checking" | "mapping" | "mapped" | "already-mapped") => void

Called on mapping transaction status changes.

timeoutMs
propertyoptionalnumber

Timeout in ms for the map_account transaction. Default: 60_000 (1 minute).

interface MappingChecker

Minimal interface for checking if an address is mapped on-chain.

Accepted structurally so this module stays runtime-agnostic. Build one directly from the typed API of any pallet-revive-capable chain — see the example on ensureAccountMapped.

Methods

addressIsMapped
addressIsMapped(address: string): Promise<boolean>

interface RetryOptions

Options for withRetry.

Properties

baseDelayMs
propertyoptionalnumber

Base delay in ms for exponential backoff. Default: 1_000.

maxAttempts
propertyoptionalnumber

Total attempts including the first. Default: 3.

maxDelayMs
propertyoptionalnumber

Maximum delay in ms. Default: 15_000.

interface ReviveApi

Minimal typed API shape for Revive.map_account().

Accepted structurally so this module works with any PAPI typed API that has the Revive pallet, without importing chain-specific descriptors.

Properties

tx
property{ Revive: { map_account: unknown } }

interface SubmitOptions

Options for submitAndWatch.

Properties

mortalityPeriod
propertyoptionalnumber

Mortality period in blocks. Default: 256 (~43 minutes on Polkadot).

onStatus
propertyoptional(status: TxStatus) => void

Called on each lifecycle transition for UI progress indicators.

timeoutMs
propertyoptionalnumber

Timeout in milliseconds. Default: 300_000 (5 minutes).

waitFor
propertyoptionalWaitFor

When to resolve the promise. Default: "best-block".

interface SubmittableTransaction

Structural type for any transaction object that supports Observable-based sign-submit-and-watch. Works with raw PAPI transactions and Ink SDK resolved transactions.

Properties

decodedCall
propertyoptionalunknown

The decoded call data. Present on PAPI transactions.

signSubmitAndWatch
property(signer: PolkadotSigner, options?: { mortality?: { mortal: boolean; period: number } }) => { subscribe: (handlers: { error: (error: Error) => void; next: (event: TxEvent) => void }) => { unsubscribe: () => void } }
waited
propertyoptionalPromise<SubmittableTransaction>

Present on Ink SDK AsyncTransaction wrappers.

interface TxResult

Successful transaction result.

Properties

block
property{ hash: string; index: number; number: number }

Block where the transaction was included.

dispatchError
propertyoptionalunknown

Dispatch error details when ok is false.

events
propertyunknown[]

Raw events emitted by the transaction.

ok
propertyboolean

Whether the on-chain dispatch succeeded.

txHash
propertystring

Transaction hash.

interface Weight

Substrate weight representing computational and storage resources.

Matches the shape returned by ReviveApi.call and ReviveApi.eth_transact dry-run results in the weight_required field.

Properties

proof_size
propertybigint

Proof size component in bytes.

ref_time
propertybigint

Reference time component in picoseconds.

Type Aliases

type BatchableCall

A transaction or decoded call that can be included in a batch.

Accepts:

  • A SubmittableTransaction (has .decodedCall)
  • An Ink SDK AsyncTransaction (has .waited that resolves to one with .decodedCall)
  • A raw decoded call object (passed through as Record<string, unknown>)

The Record<string, unknown> variant is intentionally broad because PAPI decoded calls are chain-specific enum types that cannot be imported without chain descriptors. Runtime validation in resolveDecodedCall rejects null, undefined, and primitives.

type BatchableCall = SubmittableTransaction | { decodedCall: unknown } | Record<string, unknown>

type BatchMode

Batch execution mode corresponding to Substrate’s Utility pallet.

type BatchMode = "batch_all" | "batch" | "force_batch"

type DevAccountName

Standard Substrate dev account names.

type DevAccountName = "Alice" | "Bob" | "Charlie" | "Dave" | "Eve" | "Ferdie"

type TxEvent

PAPI transaction event (discriminated union).

type TxEvent = { txHash: string; type: "signed" } | { txHash: string; type: "broadcasted" } | { block?: { hash: string; index: number; number: number }; dispatchError?: unknown; events?: unknown[]; found: boolean; ok?: boolean; txHash: string; type: "txBestBlocksState" } | { block: { hash: string; index: number; number: number }; dispatchError?: unknown; events: unknown[]; ok: boolean; txHash: string; type: "finalized" }

type TxStatus

Transaction lifecycle status for UI callbacks.

type TxStatus = "signing" | "broadcasting" | "in-block" | "finalized" | "error"

type WaitFor

When to resolve the submission promise.

type WaitFor = "best-block" | "finalized"
Last updated on