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 raised when account mapping fails. Extends TxError so it flows on
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
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.
TxValidityErrorThe transaction failed before inclusion — a validity or submission failure

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.
formatValidityError()Extract a human-readable error from a transaction validity failure payload —
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 raised when account mapping fails. Extends TxError so it flows on the err channel of the mapping functions alongside the other tx errors and carries the shared SdkError marker.

Extends: TxError

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, options?: ErrorOptions): 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. Implements the cross-package SdkError marker so isSdkError(e) also recognizes it.

Extends: Error

Implements: SdkError

Constructors

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

Properties

isSdkError
propertyreadonlytrue

Discriminant present on all SDK errors.

source
propertyreadonly"tx"

The package that raised the error, e.g. "host", "signer", "contracts".

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

class TxValidityError

The transaction failed before inclusion — a validity or submission failure such as InvalidTransaction::Payment (the submitter cannot pay fees or is not authorized on a feeless-but-authorized chain).

These failures carry no dispatchError (that field only exists for a transaction that was included and then failed to dispatch), so they are reported as a distinct type instead of a TxDispatchError with a placeholder string. The raw failure payload is preserved on TxValidityError.reason for programmatic inspection.

Extends: TxError

Constructors

constructor
new TxValidityError(reason: unknown, formatted: string): TxValidityError

Properties

formatted
propertyreadonlystring

Human-readable error string (e.g., “Invalid.Payment”).

reason
propertyreadonlyunknown

Raw underlying failure payload: the decoded validity error from polkadot-api’s InvalidTxError (e.g. { type: "Invalid", value: { type: "Payment" } }), or the failed tx event when the node reported failure without any payload.

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<Result<TxResult, TxError>>

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

A Result: ok(TxResult) from the batch submission, or err(TxError) on failure. The err channel carries a typed TxError — a TxBatchError (empty calls, an invalid call, or an AsyncTransaction that resolves without .decodedCall), or any error surfaced by the underlying submitAndWatch (TxTimeoutError, TxDispatchError, TxSigningRejectedError).

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), }); if (!result.ok) handle(result.error);

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<Result<TxResult | null, TxError>>

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

A Result: ok(TxResult) if mapping was performed, ok(null) if the account was already mapped (an expected, non-failure state), or err(TxError) if the mapping check or map_account transaction fails (a TxAccountMappingError for a failed check, or a TxDispatchError / TxTimeoutError from the submission).

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, }; const result = await ensureAccountMapped(address, signer, checker, api); if (!result.ok) handle(result.error); // 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 }): Result<SubmittableTransaction, TxDryRunError>

Parameters

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

Returns

A Result: ok(SubmittableTransaction) ready to pass to submitAndWatch, or err(TxDryRunError) if the dry run failed, returned no value, or has no send().

Examples

import { extractTransaction, submitAndWatch, createDevSigner } from "@parity/product-sdk-tx"; const dryRun = await contract.query("createItem", { origin, data: { name, price } }); const extracted = extractTransaction(dryRun); if (!extracted.ok) return handle(extracted.error); const result = await submitAndWatch(extracted.value, createDevSigner("Alice"));

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".

A failed result with a missing dispatchError is a pre-inclusion validity or submission failure (e.g. InvalidTransaction::Payment) — dispatchError only exists for a transaction that was included and then failed — and is described as such rather than collapsed to "unknown error".

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.

formatValidityError()

Extract a human-readable error from a transaction validity failure payload — the decoded TransactionValidityError that polkadot-api attaches to an InvalidTxError (its .error property) when a transaction is rejected before inclusion.

Validity payloads are nested tagged enums: { type: "Invalid", value: { type: "Payment" } }"Invalid.Payment".

Kept separate from formatDispatchError on purpose: dispatch errors only nest under Module, and walking every non-Module type/value chain there would change established formats (e.g. "Token").

formatValidityError(reason: unknown): string

Parameters

  • reason: The raw validity payload (any shape is tolerated).

Returns

The joined type chain, or "unknown validity error" when the payload carries no recognizable tags.

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<Result<TxResult, TxError>>

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

A Result: ok(TxResult) once included/finalized, or err(TxError) on failure. The err channel carries a typed TxError — a TxTimeoutError (target state not reached within timeoutMs), TxDispatchError (on-chain dispatch failed, e.g. insufficient balance or contract revert), TxValidityError (pre-inclusion validity/submission failure, e.g. InvalidTransaction::Payment — no dispatch error exists for these), TxSigningRejectedError (user rejected signing), or a base TxError wrapping any other failure.

withRetry()

Wrap an async function with retry logic and exponential backoff.

Only retries transient errors (network disconnects, temporary RPC failures). Deterministic errors (TxDispatchError, TxValidityError, 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