npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

polkadot-cli

v1.19.0

Published

CLI tool for querying Polkadot-ecosystem on-chain state

Readme

npm version codecov

polkadot-cli

A command-line tool for interacting with Polkadot-ecosystem chains. Manage chains and accounts, query storage, look up constants, inspect metadata, submit extrinsics, and compute hashes — all from your terminal.

Ships with Polkadot and all system parachains preconfigured with multiple fallback RPC endpoints. Add any Substrate-based chain by pointing to its RPC endpoint(s).

Features

  • ✅ Same syntax as polkadot-api (PAPI)
  • ✅ Chain name prefix — dot polkadot.query.System.Number
  • ✅ zsh, bash, and fish autocompletion
  • ✅ Exposes all on-chain metadata documentation
  • ✅ Encode, dry-run, and submit extrinsics
  • ✅ Support for custom signed extensions — and a dot <chain>.extensions inspector to discover them
  • ✅ Built with agent use in mind — structured JSON output on every command (--json)
  • ✅ Fuzzy matching with typo suggestions
  • ✅ Account management — BIP39 mnemonics, derivation paths, env-backed secrets, watch-only, dev accounts
  • ✅ Named address resolution across all commands
  • ✅ Runtime API calls — dot polkadot.apis.Core.version
  • ✅ Raw JSON-RPC calls — dot polkadot.rpc.system_health, with discovery via rpc_methods and tab-completion
  • ✅ Full-metadata dump — dot metadata <chain> emits one JSON blob with pallets, runtime APIs, and transaction extensions (or raw SCALE bytes via --raw)
  • ✅ Stale-metadata detection — when a tx or query fails because the runtime upgraded, the CLI tells you exactly which dot chain update to run
  • ✅ Chain topology — relay/parachain hierarchy with tree display and auto-detection
  • ✅ Batteries included — all system parachains and testnets already setup to be used
  • ✅ File-based commands — run any command from a YAML/JSON file with variable substitution
  • ✅ Sovereign accounts — store a parachain (child / sibling) or pallet (Treasury, Bounties, NominationPools, …) sovereign as a named watch-only account in one command
  • ✅ Message signing — sign arbitrary bytes with account keypairs for use as MultiSignature arguments
  • ✅ Unsigned/authorized transactions — submit governance-authorized calls without a signer (--unsigned)
  • ✅ Non-native fee payment — pay tx fees in any asset the chain accepts via --asset (asset-hub-style chains)
  • ✅ Bandersnatch member keys — derive Ring VRF member keys from mnemonics for on-chain member sets
  • ✅ Export/import — portable chain and account configuration for backup, sharing, and CI bootstrapping
  • ✅ Claude Code skill — dot-cli skill installable as a plugin marketplace, teaches agents how to drive the CLI

Preconfigured chains

| Network | Chain | |---------|-------| | Polkadot | polkadot (relay) | | | polkadot-asset-hub | | | polkadot-bridge-hub | | | polkadot-collectives | | | polkadot-coretime | | | polkadot-people | | Paseo (testnet) | paseo (relay) | | | paseo-asset-hub | | | paseo-bridge-hub | | | paseo-collectives | | | paseo-coretime | | | paseo-people |

Each chain ships with multiple RPC endpoints from decentralized infrastructure providers (IBP, Dotters, Dwellir, and others). The CLI automatically falls back to the next endpoint if the primary is unreachable.

Install

npm install -g polkadot-cli@latest

This installs the dot command globally.

Claude Code skill

This repo ships a Claude Code skill that teaches Claude how to drive the dot CLI — query patterns, tx encoding, runtime API calls, and bash scripting gotchas.

Register the marketplace and install the skill:

/plugin marketplace add peetzweg/polkadot-cli
/plugin install dot-cli@polkadot-cli

The skill auto-triggers when you ask Claude about dot, Substrate storage queries, extrinsic submission, runtime APIs, or XCM. You can also invoke it directly with /dot-cli.

To pull the latest skill updates:

/plugin marketplace update polkadot-cli

Usage

Manage chains

# Show chain help
dot chain               # shows available actions
dot chains              # shorthand, same as above

# Add a chain (single RPC)
dot chain add kusama --rpc wss://kusama-rpc.polkadot.io

# Add a chain with fallback RPCs (repeat --rpc for each endpoint)
dot chain add kusama --rpc wss://kusama-rpc.polkadot.io --rpc wss://kusama-rpc.dwellir.com

# Add a parachain under a relay (auto-detects parachain ID)
dot chain add local-asset-hub --rpc ws://localhost:9945 --relay local-relay

# Add a parachain with explicit parachain ID
dot chain add my-para --rpc wss://rpc.example.com --relay polkadot --parachain-id 2000

# List configured chains (shows relay/parachain hierarchy)
dot chain list
dot chains                  # shorthand
dot chains -v               # include RPC endpoints in the list

# Inspect a single chain (rpc, parachains, metadata cache status)
dot chain info polkadot
dot chain polkadot          # bare-noun shortcut, same as `chain info polkadot`

# Re-fetch metadata after a runtime upgrade
dot chain update polkadot   # updates a specific chain
dot chain update --all      # updates all configured chains in parallel

# Remove a chain (only user-added chains can be removed)
dot chain remove kusama

Chain topology

dot chain list displays chains as a tree, grouping parachains under their parent relay:

Configured Chains

  polkadot
  ├─ polkadot-asset-hub [1000]
  ├─ polkadot-bridge-hub [1002]
  ├─ polkadot-collectives [1001]
  ├─ polkadot-coretime [1005]
  └─ polkadot-people [1004]

  paseo
  ├─ paseo-asset-hub [1000]
  └─ paseo-people [1004]

  my-solo-chain

The default list is intentionally compact — names + relay tree + parachain IDs. Pass -v / --verbose to also print every RPC endpoint inline (à la git remote -v):

Configured Chains

  polkadot  wss://polkadot.ibp.network
       wss://polkadot-rpc.n.dwellir.com
       wss://rpc.polkadot.io
  ├─ polkadot-asset-hub [1000]  wss://polkadot-asset-hub-rpc.polkadot.io
       ...

For the full set of fields for one chain (RPCs, parent relay, child parachains, metadata cache status), use dot chain info <name>:

dot chain info polkadot

# polkadot
#
#   rpc:
#     wss://polkadot.ibp.network
#     wss://rpc.polkadot.io
#     ...
#   parachains:
#     polkadot-asset-hub [1000]
#     polkadot-bridge-hub [1002]
#     ...
#   metadata:
#     not cached — run `dot chain update polkadot`

After a successful dot chain update <name>, the metadata: row shows the cached specName, specVersion, and fetchedAt timestamp instead of the not cached hint. dot chain info <name> --json emits the same data as a structured object (with metadata: null when no fingerprint is cached). Names resolve case-insensitively. The bare-noun form dot chain <name> is a shortcut for dot chain info <name> — known action verbs (add, remove, update, list, export, import, info) take precedence over chain names if there's ever a clash.

All built-in system parachains are preconfigured with their relay chain and parachain ID. When adding a custom parachain with --relay, the CLI auto-detects the parachain ID from on-chain ParachainInfo storage. Use --parachain-id to set it explicitly if auto-detection is not available.

Removing a relay chain that has parachains prints a warning listing the orphaned chains. The parachains remain in the config and can be re-associated later.

Selecting a chain

Every chain-consuming command must specify a chain explicitly. Prefer the dotpath chain prefix; the --chain <name> flag is equivalent. There is no hidden default; running a command without a chain errors out with a message listing the configured chains.

# Recommended — dotpath chain prefix
dot polkadot.query.System.Number
# Output:
# 31014744

# Equivalent — --chain flag
dot query.System.Number --chain polkadot
# Output:
# 31014744

# Both at once errors
dot polkadot.query.System.Number --chain polkadot  # ✗ errors

Chain names are case-insensitive (Polkadot.query.System.Number works the same).

Export/import chain configuration

Export and import chain configurations for backup, sharing across machines, or team collaboration.

# Export custom chains to stdout (pipe-friendly JSON)
dot chain export

# Export all chains including built-ins
dot chain export --all

# Export specific chains
dot chain export my-relay my-para

# Export to a file
dot chain export --all --file my-chains.json

# Import from a file
dot chain import my-chains.json

# Preview without applying
dot chain import my-chains.json --dry-run

# Overwrite existing chains
dot chain import my-chains.json --overwrite

# Skip automatic metadata fetch (faster for offline/CI bootstrap)
dot chain import my-chains.json --no-metadata

# Pipe between machines
ssh remote-dev "dot chain export" | dot chain import -

By default, export only includes user-added chains and built-ins with modified RPCs. Use --all to include everything. Import skips existing chains unless --overwrite is passed, and validates relay references with warnings for missing relays.

After a non-dry-run import, metadata is fetched automatically for each newly added or overwritten chain so tab completion and metadata-dependent commands work immediately. Pass --no-metadata to skip the fetch — you can always backfill later with dot chain update --all.

Output shows one line per chain with a status glyph and a terse summary:

  ✓ preview
  ✓ preview-people
  ⟳ polkadot (overwritten)
  - paseo (skipped)

2 added, 1 overwritten, 1 skipped

Updating metadata for 3 chain(s)...

  ✓ preview
  ✓ preview-people
  ✓ polkadot

Running dot chain import with no file path prints the subcommand help instead of blocking on stdin.

Manage accounts

Dev accounts (Alice, Bob, Charlie, Dave, Eve, Ferdie) are always available for testnets. Create or import your own accounts for any chain.

Security warning: Account secrets (mnemonics and seeds) are currently stored unencrypted in ~/.polkadot/accounts.json. Do not use this for high-value accounts on mainnet. Encrypted storage is planned for a future release. Use --env to keep secrets off disk entirely.

# Show account help
dot account             # shows available actions
dot accounts            # shorthand, same as above

# List all accounts (dev + stored)
dot account list

# Add a watch-only address (no secret — for use as tx recipient or query target)
dot account add treasury 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
dot account add council 0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d

# Create a new account (generates a mnemonic)
dot account create my-validator

# Create with a derivation path
dot account create my-staking --path //staking

# Add a keyed account from a BIP39 mnemonic
dot account add treasury --secret "word1 word2 ... word12"

# Add with a derivation path
dot account add hot-wallet --secret "word1 word2 ... word12" --path //hot

# Add an env-var-backed account (secret stays off disk)
dot account add ci-signer --env MY_SECRET

# Derive a child account from an existing one
dot account derive treasury treasury-staking --path //staking

# Use it — the env var is read at signing time
MY_SECRET="word1 word2 ..." dot polkadot.tx.System.remark 0xdead --from ci-signer

# Remove one or more accounts
dot account remove my-validator
dot account delete my-validator stale-key

# Export accounts (secrets redacted by default)
dot account export
dot account export --include-secrets --file backup.json
dot account export --watch-only

# Batch-import accounts from a file
dot account import team-accounts.json
dot account import accounts.json --dry-run
dot account import accounts.json --overwrite

# Inspect an account — show public key and SS58 address
dot account inspect alice
dot account alice                    # shorthand (same as inspect)
dot account inspect 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
dot account inspect 0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d
dot account inspect alice --prefix 0         # Polkadot mainnet prefix
dot account inspect alice --json             # JSON output

Watch-only accounts

Add named addresses without secrets — useful for saving frequently-used recipients, multisig members, or query targets:

dot account add treasury 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
dot account add council 0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d

Watch-only accounts appear in dot account list under a dedicated Watch-only section and can be inspected and removed like any other account. They cannot be used with --from (signing) or as a source for derive.

The add subcommand is context-sensitive: bare add <name> <address> creates a watch-only entry, while add --secret or add --env imports a keyed account. dot account import is reserved for file-based batch import.

Named address resolution

Named accounts (both watch-only and keyed) resolve automatically everywhere an AccountId32 or MultiAddress is expected — in dot tx arguments and dot query keys:

# Use a named account as transfer recipient
dot polkadot.tx.Balances.transfer_keep_alive treasury 1000000000000 --from alice

# Query by account name
dot polkadot.query.System.Account treasury

# Dev accounts also resolve
dot polkadot.tx.Balances.transfer_keep_alive bob 1000000000000 --from alice

Resolution order: dev account name > stored account name > SS58 address > hex public key. If the input doesn't match any, the CLI shows an error listing all available account names alphabetically, one per line. When the input is close to an existing name, a "Did you mean?" suggestion is included:

Error: Unknown account or address "people-sudo-signer".
  Did you mean: people-paseo-sudo?
  Available accounts:
    - alice
    - bob
    - charlie
    - people-paseo-sudo
    - ...

Inspect accounts

Convert between SS58 addresses, hex public keys, and account names. Accepts any of:

  • Dev account name (alice, bob, etc.) — resolves to public key and SS58
  • Stored account name — looks up the public key from the accounts file
  • SS58 address — decodes to the underlying public key
  • Hex public key (0x + 64 hex chars) — encodes to SS58
  • --pallet-id <id> — derives a pallet sovereign address without saving it (script-friendly; nothing persists)
  • --parachain <id> --parachain-type <child|sibling> — derives a parachain sovereign address without saving it
dot account inspect alice
dot account alice                    # shorthand — unknown subcommands fall through to inspect

dot account inspect 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
dot account inspect 0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d

# pallet-revive H160 — the 20-byte address shape from EVM tooling. Same
# input slot as SS58 / hex pubkey; resolved to the deterministic fallback
# AccountId32 (`H160 || 0xEE * 12`).
dot account inspect 0x9621DDe636dE098B43Efb0fA9b61fAcFE328F99D

Use --prefix to encode the SS58 address with a specific network prefix (default: 42):

dot account inspect alice --prefix 0     # Polkadot mainnet (prefix 0, starts with '1')
dot account inspect alice --prefix 2     # Kusama (prefix 2)

JSON output:

dot account inspect alice --json
# Output:
# {
#   "publicKey": "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d",
#   "ss58": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
#   "h160": "0x9621DDe636dE098B43Efb0fA9b61fAcFE328F99D",
#   "prefix": 42,
#   "name": "Alice",
#   "kind": "dev"
# }

Plain text output:

dot account inspect alice
# Output:
# Account Info
#
#   Name:        Alice
#   Kind:        dev
#   Public Key:  0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d
#   SS58:        5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
#   H160:        0x9621DDe636dE098B43Efb0fA9b61fAcFE328F99D
#   Prefix:      42

The Kind: line categorises the account: dev (built-in), signer (has a secret/env), watch-only (raw external address), pallet sovereign (derived from a PalletId), parachain sovereign (child|sibling) (derived from a parachain ID), or revive H160 fallback (a 20-byte input resolved to its deterministic Substrate AccountId32). For derived sovereigns, an extra Source: line shows what the address was derived from. For env-backed signers, an Env: line shows the variable; for derived child keys, Derivation: shows the path.

pallet-revive H160 address

Every Substrate account has a corresponding 20-byte H160 address under pallet-revive (the new EVM-compatible smart-contracts pallet on Polkadot Hub / Asset Hub). dot account inspect always shows it, EIP-55 checksummed, on the H160: line. The mapping is offline and prefix-independent:

  • AccountId32 → H160: if the last 12 bytes are 0xEE, strip them (the account originated from an Eth address); otherwise keccak256(accountId32) and take the last 20 bytes.
  • H160 → AccountId32: deterministic fallback is H160 || 0xEE * 12. (The full mapping after a successful pallet_revive.map_account extrinsic lives in on-chain AddressSuffix storage and isn't recoverable offline — that's a chain-state lookup.)

Pass a 20-byte hex value as the inspect input to resolve it back to its fallback Substrate account:

dot account inspect 0x9621DDe636dE098B43Efb0fA9b61fAcFE328F99D
# Output:
# Account Info
#
#   Kind:        revive H160 fallback
#   Public Key:  0x9621dde636de098b43efb0fa9b61facfe328f99deeeeeeeeeeeeeeeeeeeeeeee
#   SS58:        5FTZ6n1wY3GBqEZ2DWEdspbTarvRnp8DM8x2YXbWubu7JN98
#   H160:        0x9621DDe636dE098B43Efb0fA9b61fAcFE328F99D
#   Prefix:      42

# Script-friendly: just the H160 for a given account
dot account inspect alice --json | jq -r .h160
# 0x9621DDe636dE098B43Efb0fA9b61fAcFE328F99D

Note: dot implements the current pallet-revive master variant (keccak fallback). Older stable2412 runtimes used plain accountId32[..20] truncation; if you target one, compute it manually until a --revive-truncate flag lands.

Stateless sovereign derivation (script-friendly)

Pass --pallet-id or --parachain / --parachain-type to compute a sovereign address without persisting anything to ~/.polkadot/accounts.json. The output shape matches the stored case (same Kind: / Source: / SS58 / public key + same --json schema), but no Name: line and nothing in dot account list afterwards. Use this in scripts when you just need the address:

# Polkadot Treasury — pallet sovereign on prefix 0
dot account inspect --pallet-id py/trsry --prefix 0
# Output:
# Account Info
#
#   Kind:        pallet sovereign
#   Public Key:  0x6d6f646c70792f74727372790000000000000000000000000000000000000000
#   SS58:        13UVJyLnbVp9RBZYFwFGyDvVd1y27Tt8tkntv6Q7JVPhFsTB
#   Source:      PalletId py/trsry (0x70792f7472737279)
#   Prefix:      0

# Hex form works the same
dot account inspect --pallet-id 0x70792f7472737279 --prefix 0

# Parachain sovereigns (type is required — child = on relay, sibling = on another parachain)
dot account inspect --parachain 1004 --parachain-type child

# Pipeline: just the SS58
SS58=$(dot account inspect --pallet-id py/trsry --prefix 0 --json | jq -r .ss58)

# JSON shape includes a `source` object describing the derivation
dot account inspect --pallet-id py/trsry --json
# {
#   "publicKey": "0x6d6f646c70792f74727372790000000000000000000000000000000000000000",
#   "ss58":      "5EYCAe5ijiYfyeZ2JJCGq56LmPyNRAKzpG4QkoQkkQNB5e6Z",
#   "prefix":    42,
#   "kind":      "pallet sovereign",
#   "source":    { "kind": "pallet", "palletId": "py/trsry", "palletIdHex": "0x70792f7472737279" }
# }

Constraints (will error): cannot combine a positional input with derivation flags; --pallet-id and --parachain are mutually exclusive; --parachain requires --parachain-type child|sibling; --show-secret doesn't apply (derived sovereigns have no key).

Reveal the sr25519 private key

For provisioning another signer (e.g. a server that expects a raw hex private key in an env var), add --show-secret to print the 64-byte sr25519 expanded secret as 0x-prefixed hex:

dot account inspect dave --show-secret
# Private Key: 0x<128 hex chars>   (sr25519 expanded, 64 bytes — never share)

Works for dev accounts (derived on-the-fly from the standard dev mnemonic) and for stored accounts that have a secret (mnemonic or hex seed). Refuses on watch-only accounts, bare SS58 addresses, or hex public keys. The hex is the final secret after any derivation path is applied, so it can be fed directly to signers that don't accept a mnemonic+path (e.g. @scure/sr25519's sign, or services like identity-backend that read a PROXY_PRIVATE_KEY). Combine with --json to include it under the privateKey field.

Env-var-backed accounts

For CI/CD and security-conscious workflows, store a reference to an environment variable instead of the secret itself:

dot account add ci-signer --env MY_SECRET

--secret and --env are mutually exclusive. Use dot account add for single-account imports; dot account import is reserved for file-based batch import.

The secret is never written to disk. At signing time, the CLI reads $MY_SECRET and derives the keypair. If the variable is not set, the CLI errors with a clear message. account list annotates env-backed signers with env $MY_SECRET in the metadata column and resolves the address live when the variable is available.

Derivation paths

Use --path with create, add, or the derive action to derive child keys from the same secret. Different paths produce different keypairs, enabling key separation (e.g. staking vs. governance) without managing multiple mnemonics.

# Create with a derivation path
dot account create my-staking --path //staking

# Multi-segment path (hard + soft junctions)
dot account create multi --path //polkadot//0/wallet

# Add with a path
dot account add hot --secret "word1 word2 ..." --path //hot

# Derive a child from an existing account
dot account derive treasury treasury-staking --path //staking

derive copies the source account's secret and applies the given path. It requires both a source name, a new name, and --path. Works with env-backed accounts too — the derived account shares the same env var reference.

account list shows derivation paths and env sources on tree-style continuation lines (label names mirror the --flag that sets them):

Signers
  treasury-staking  5FHneW46...
     └─ path: //staking
  ci-signer         5EPCUjPx...
     ├─ path: //ci
     └─ env:  $MY_SECRET

Supported secret formats for import:

| Format | Example | Status | |--------|---------|--------| | BIP39 mnemonic (12/24 words) | "abandon abandon ... about" | Supported | | Hex seed (0x + 64 hex chars) | 0xabcdef0123... | Not supported via CLI (see below) |

Known limitation: Hex seed import (--secret 0x...) does not work from the command line. The CLI argument parser (cac) interprets 0x-prefixed values as JavaScript numbers, which loses precision for 32-byte seeds. Use a BIP39 mnemonic instead. If you need to import a raw seed programmatically, write it directly to ~/.polkadot/accounts.json.

Export/import accounts

Export and import accounts for backup, sharing, or bootstrapping CI environments. Secrets are redacted by default — safe to share or commit.

# Export accounts (secrets redacted by default)
dot account export

# Export specific accounts
dot account export treasury my-validator

# Include secrets (explicit opt-in, prints warning)
dot account export --include-secrets --file backup.json

# Export only watch-only accounts (always safe)
dot account export --watch-only

# Batch-import accounts from a file (positional path, like `dot chain import`)
dot account import team-accounts.json

# Preview without applying
dot account import accounts.json --dry-run

# Overwrite existing accounts
dot account import accounts.json --overwrite

# Pipe from another machine
ssh remote-dev "dot account export --watch-only" | dot account import -

Output mirrors dot chain import — one line per account with a status glyph ( added, overwritten, - skipped) and a terse count summary at the end. Running dot account import with no file path prints the subcommand help instead of blocking on stdin.

dot account import is file-only. For a single-account import from a mnemonic or env variable, use dot account add <name> --secret "..." or dot account add <name> --env VAR.

Security: default export replaces mnemonic/seed with "<redacted>". --include-secrets is required for actual secrets. Env-backed accounts export the variable name (e.g. {"env": "MY_SECRET"}), never the value. Redacted accounts import as watch-only (public key preserved, no signing capability).

Chain prefix

Instead of the --chain flag, you can prefix the dot-path with the chain name. The prefix becomes the first segment of the dot-path:

dot polkadot.query.System.Account 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
dot polkadot.const.Balances.ExistentialDeposit
dot polkadot.tx.Balances.transfer_keep_alive bob 1000000000 --from alice --dry-run
dot inspect polkadot.System            # for `inspect`, the prefix is the first arg
dot inspect polkadot.System.Account

Chain names are case-insensitive — Polkadot.query.System.Account, POLKADOT.query.System.Account, and polkadot.query.System.Account all resolve the same way. The same applies to --chain Polkadot.

Every invocation must specify a chain explicitly: either via a dotpath prefix (as above) or via --chain <name>. If both are provided, the CLI errors.

Space-separated syntax

The Pallet and Item segments can be passed as separate arguments instead of dot-joined. These pairs are equivalent:

# Dot notation vs fully space-separated — these are identical:
dot polkadot.query.System                 # dot notation
dot query System --chain polkadot         # space-separated

dot polkadot.events.Balances.Transfer
dot events Balances Transfer --chain polkadot

dot polkadot.apis.Core
dot apis Core --chain polkadot

This works for all categories (query, tx, const, events, errors, apis, extensions, rpc). When passing positional method arguments, keep Pallet and Item either fully dot-joined (query.System.Account 5Grw...) or fully space-separated (query System Account 5Grw...) — mixing the two (query System.Account 5Grw...) does not work because the second arg gets parsed as a pallet name.

Query storage

# Plain storage value
dot polkadot.query.System.Number
# Output:
# 31014744

# Map entry by key — Alice's account on Polkadot (free balance is u128 as a quoted string)
dot polkadot.query.System.Account 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
# Output:
# {
#   "nonce": 0,
#   "consumers": 0,
#   "providers": 0,
#   "sufficients": 0,
#   "data": {
#     "free": "0",
#     "reserved": "0",
#     "frozen": "0",
#     "flags": "170141183460469231731687303715884105728"
#   }
# }

# Map without key — shows help/usage (use --dump to fetch all entries)
dot polkadot.query.System.Account

# Dump all map entries (requires --dump)
dot polkadot.query.System.Account --dump

# Pipe-safe — stdout is clean data, progress messages go to stderr
dot polkadot.query.System.Account --dump | jq '.[0].value.data.free'
dot polkadot.query.System.Number --json | jq '.+1'

# --chain flag is equivalent to the dotpath prefix
dot query.System.Account 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY --chain polkadot

Partial key queries

For storage maps with multiple keys (NMaps), you can provide fewer keys than expected to retrieve all entries matching that prefix. This uses the chain's prefix-based iteration and does not require --dump.

# Full key — returns a single value
dot polkadot.query.Staking.ErasStakers 100 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY

# Partial key — returns all entries matching the first key
dot polkadot.query.Staking.ErasStakers 100

# No keys — requires --dump (safety net for large maps)
dot polkadot.query.Staking.ErasStakers --dump

Output formatting

Query results automatically convert on-chain types for readability:

  • BigInt values (e.g. balances) render as decimal strings
  • Binary fields (e.g. token name, symbol) render as text when the value contains only printable characters, or as 0x-prefixed hex otherwise (values containing control characters, Private Use Area code points, or invalid UTF-8 sequences always fall back to hex)
  • Uint8Array values render as 0x-prefixed hex
# Token metadata — symbol and name display as text, not {}
dot polkadot-asset-hub.query.Assets.Metadata 1984
# Output:
# {
#   "deposit": "2008200000",
#   "name": "Tether USD",
#   "symbol": "USDt",
#   "decimals": 6,
#   "is_frozen": false
# }

Historical reads — --at <block>

Storage queries default to the latest finalized head. Pass --at to read state at a specific block hash, the chain head (best), or finalized (explicit). Accepted on both query.* and apis.* runtime calls.

# Read at the current best (non-finalized) head — useful for low-latency reads
dot polkadot.query.System.Number --at best

# Read at the last finalized block — same as the default, but explicit
dot polkadot.query.System.Number --at finalized

# Pin a finalized hash and read multiple items at that exact block
HASH=$(dot polkadot.rpc.chain_getFinalizedHead | tr -d '"')
dot polkadot.query.System.Number --at "$HASH"
dot polkadot.apis.Core.version --at "$HASH" --json | jq .spec_version

--at accepts a 32-byte 0x… block hash, "best", or "finalized". Anything else errors before any network call. Tx submission rejects "best".

Archive-only blocks: papi v2 talks to the chainHead_v1_* JSON-RPC API, which only serves pinned (recent) blocks. Querying a hash older than a few minutes against a non-archive node fails with a clean error that includes a copy-pasteable --rpc wss://<archive-endpoint> hint:

⚠ 0x… is not available on the current RPC endpoint.
   Public nodes serve only recent (pinned) blocks via chainHead_v1_*.
   For deep historical reads, point --rpc at an archive endpoint, e.g.:
     dot ... --at 0x… --rpc wss://<archive-endpoint>

Look up constants

dot polkadot.const.Balances.ExistentialDeposit
# Output:
# "10000000000"

dot polkadot.const.System.SS58Prefix
# Output:
# 0

# --chain flag is equivalent
dot const.Balances.ExistentialDeposit --chain polkadot

# Pipe-safe — stdout is clean JSON, progress messages go to stderr
dot polkadot.const.Balances.ExistentialDeposit --json | jq

Inspect metadata

Works offline from cached metadata after the first fetch. The chain is required. Prefer the chain-prefix-on-target form (dot inspect polkadot.System); --chain is equivalent. Note that dot polkadot.inspect.X does not parse — inspect is a top-level command, not a dotpath category.

Output is width-aware: short type signatures stay on a single line, longer ones expand across multiple lines with field names aligned. Composite struct fields, enum variants, and call arguments are color-coded (cyan field names, yellow primitives, magenta container keywords like Vec/Option, green enum variants) when stdout is a TTY; piped output stays plain.

# Pallet detail — list storage, constants, calls, events, and errors
dot inspect polkadot.System
# Output:
# System Pallet
#
#   Storage Items:
#     Account [map]
#       Key:   AccountId32
#       Value: {
#         nonce      : u32,
#         consumers  : u32,
#         providers  : u32,
#         sufficients: u32,
#         data       : { free: u128, reserved: u128, frozen: u128, flags: u128 },
#       }
#         The full account information for a particular account ID.
#     ...

# Storage item detail — full type and docs (each part on its own line)
dot inspect polkadot.System.Account
# Output:
# System.Account (Storage)
#
#   Type:  map
#   Key:   AccountId32
#   Value: {
#     nonce      : u32,
#     consumers  : u32,
#     providers  : u32,
#     sufficients: u32,
#     data       : { free: u128, reserved: u128, frozen: u128, flags: u128 },
#   }
#
#   The full account information for a particular account ID.

# Event detail — shows field signature and docs
dot inspect polkadot.Balances.Transfer
# Output:
# Balances.Transfer (Event)
#
#   Fields: (from: AccountId32, to: AccountId32, amount: u128)
#
#   Transfer succeeded.

# Error detail — shows docs
dot inspect polkadot.Balances.InsufficientBalance
# Output:
# Balances.InsufficientBalance (Error)
#
#   Balance too low to send value.

# List all pallets — single positional is read as a pallet name, so use --chain here
dot inspect --chain polkadot

All listings — pallets, storage items, constants, calls, events, and errors — are sorted alphabetically, making it easy to find a specific item at a glance.

The pallet listing view shows type information inline so you can understand item shapes at a glance:

  • Storage: name with optional [map] tag, followed by indented Key: / Value: lines (composite values expand to one field per line when wide)
  • Constants: the constant's type (e.g. ExistentialDeposit: u128)
  • Calls: argument signature inline if it fits, otherwise one argument per line with names aligned by colon
  • Events: field signature inline if it fits, otherwise one field per line
  • Errors: name and documentation (e.g. InsufficientBalance)

Long call signatures expand automatically:

dot inspect polkadot.Referenda
# Output (excerpt):
#   cancel(index: u32)
#       Cancel an ongoing referendum.
#   ...
#   submit(
#     proposal_origin : system | Origins | ParachainsOrigin | XcmPallet,
#     proposal        : Legacy | Inline | Lookup,
#     enactment_moment: At | After,
#   )
#       Propose a referendum on a privileged action.

Enums up to 24 variants now render as A | B | C | … (previously variants were collapsed to enum(N variants) for >4); only enums with more than 24 variants are still summarized for readability. So a transaction extension's value type like Option<AsPersonalAliasWithAccount | AsPersonalAliasWithProof | …> now spells out the variants you actually need to construct.

Documentation from the runtime metadata is shown on an indented line below each item. The detail view (dot inspect Balances.transfer_allow_death) shows the full argument signature and complete documentation text. Use call inspection to discover argument names, types, and docs before constructing dot tx commands.

Dump full metadata

dot metadata <chain> fetches the chain's runtime metadata and prints everything as one JSON blob — pallets (with calls, events, errors, storage, constants), runtime APIs, and transaction extensions, headed by a runtime fingerprint (specVersion, transactionVersion, codeHash, etc.). Useful for feeding LLM agents or pipelines that want a single source of truth instead of walking dot inspect piecemeal.

# Decoded JSON — fetches fresh from the chain (top-level command; no chain prefix)
dot metadata polkadot

# SCALE-encoded metadata bytes as a single 0x… hex line (for tools that re-parse)
dot metadata polkadot --raw

# Skip the network round-trip and use cached metadata
dot metadata polkadot --cached

# Override the RPC endpoint
dot metadata polkadot --rpc wss://rpc.example.com

# Slice with jq — list call names in Balances
dot metadata polkadot | jq -r '.pallets[] | select(.name=="Balances") | .calls[].name'
# Output:
# burn
# force_adjust_total_issuance
# force_set_balance
# force_transfer
# force_unreserve
# transfer_all
# transfer_allow_death
# transfer_keep_alive
# upgrade_accounts

# All transaction extension identifiers
dot metadata polkadot | jq -r '.transactionExtensions[].identifier'
# Output:
# AuthorizeCall
# CheckNonZeroSender
# CheckSpecVersion
# CheckTxVersion
# CheckGenesis
# CheckMortality
# CheckNonce
# CheckWeight
# ChargeTransactionPayment
# PrevalidateAttests
# CheckMetadataHash

The decoded JSON is structured-only (no colorization), so it's safe to redirect to a file or pipe into other tools. The default fetches fresh from the RPC and atomically updates the local metadata cache and runtime-fingerprint sidecar — so subsequent commands benefit from the freshest possible metadata.

Runtime APIs

Browse and call Substrate runtime APIs. These are top-level APIs exposed by the runtime (e.g. Core, AccountNonceApi, TransactionPaymentApi), accessed as dot <chain>.apis.ApiName.method.

# List all runtime APIs with method counts
dot polkadot.apis
# Output:
# Runtime APIs on polkadot (24)
#
#   AccountNonceApi  1 methods
#   AssetHubMigrationApi  2 methods
#   AuthorityDiscoveryApi  1 methods
#   BabeApi  6 methods
#   ...

# List methods in a specific API (with signatures)
dot polkadot.apis.Core
# Output:
# Core Methods
#
#   execute_block(block: { header: { ... }, extrinsics: Vec<Vec<u8>> }) → unknown
#       Execute the given block.
#   initialize_block(header: { ... }) → AllExtrinsics | OnlyInherents
#       Initialize a block with the given header and return the runtime executive mode.
#   version() → { spec_name: str, impl_name: str, ... }
#       Returns the version of the runtime.

# Call a runtime API method
dot polkadot.apis.Core.version --json
# Output:
# {
#   "spec_name": "polkadot",
#   "impl_name": "parity-polkadot",
#   "spec_version": 2002001,
#   "transaction_version": 26,
#   ...
# }

# --chain flag is equivalent
dot apis.Core.version --chain polkadot

# Show method signature and docs (chain still required to load metadata)
dot polkadot.apis.Core.version --help

api is an alias for apis.

Runtime API info requires v15 metadata. If dot <chain>.apis shows 0 APIs, update the cached metadata:

dot chain update polkadot   # specific chain
dot chain update --all      # all configured chains

Argument formats

Runtime API arguments accept the same shorthand as dot tx arguments:

| Type | Pass as | Example | |------|---------|---------| | Integers (u8u32, i8i32) | decimal | 0, 42 | | Big integers (u64, u128, u256, i64 …) | decimal | 1000000000000 | | bool | true / false | true | | AccountId32 | dev name, stored account, SS58, or 0x + 64 hex pubkey | alice, 5GrwvaEF… | | Vec<u8> (unsized bytes) | 0x… hex or text | 0xdeadbeef, hello | | [u8; N] (sized bytes, e.g. H160/H256/raw AccountId) | 0x + exactly 2 * N hex chars (recommended), or text | 0x970951a12f975e6762482aca81e57d5a2a4e73f4 | | Option<T> | null (recommended), none, undefined — or a T value for Some(value) | null | | Vec<T> (non-byte) | JSON array or comma-separated | [1,2,3], 1,2,3 | | Structs / nested enums | JSON | {"type":"X1","value":{…}} |

For sized byte arrays ([u8; N]) — common for Ethereum-style addresses (H160, [u8; 20]), 32-byte hashes (H256), and raw AccountId32 bytes — pass a 0x-prefixed hex string. Example: a contract call against the pallet-revive runtime API. Use dot <chain>.apis.<ApiName>.<method> --help to see the exact argument signature for any method.

ORIGIN=alice
CONTRACT=0x970951a12f975e6762482aca81e57d5a2a4e73f4         # H160, [u8; 20]
CALLDATA=$(cast calldata "set(uint256)" 42)

# Args: origin, dest, value, gas_limit (Option), storage_deposit (Option), input_data
dot paseo-asset-hub.apis.ReviveApi.call \
  "$ORIGIN" "$CONTRACT" 0 null null "$CALLDATA"
Passing Option<T>

Absent options (None) can be written three ways, all equivalent:

# null (recommended), none, and undefined all mean None
dot paseo-asset-hub.apis.ReviveApi.call "$ORIGIN" "$CONTRACT" 0 null       null       "$CALLDATA"
dot paseo-asset-hub.apis.ReviveApi.call "$ORIGIN" "$CONTRACT" 0 none       none       "$CALLDATA"
dot paseo-asset-hub.apis.ReviveApi.call "$ORIGIN" "$CONTRACT" 0 undefined  undefined  "$CALLDATA"

null is the recommended form — it matches JSON / YAML semantics, so args read identically on the CLI and inside file-based command YAML/JSON inputs.

A present option (Some(value)) is just the value itself — no wrapping:

# gas_limit = Some({ ref_time: 1_000_000, proof_size: 100_000 })
dot paseo-asset-hub.apis.ReviveApi.call "$ORIGIN" "$CONTRACT" 0 \
  '{"ref_time":1000000,"proof_size":100000}' \
  null \
  "$CALLDATA"

Notes:

  • The null / none / undefined literals are case-sensitive (lowercase only).
  • There is no Some(value) prefix — bare values are already treated as Some.

Focused metadata listings

Each category supports partial dot-paths for browsing metadata. Category-only invocations list pallets (or APIs); pallet-level invocations list items; item-level invocations show detail. Singular and plural aliases work: event = events, error = errors, api = apis, consts = constants = const.

# List pallets with calls (and the calls themselves)
dot polkadot.tx                                 # pallets with calls
dot polkadot.tx.Balances                        # calls with arg signatures
dot polkadot.tx.Balances.transfer_allow_death   # call detail (or use --help)

# Events
dot polkadot.events                             # pallets with events
dot polkadot.events.Balances                    # events with field signatures
dot polkadot.events.Balances.Transfer           # event detail

# Errors
dot polkadot.errors                             # pallets with errors
dot polkadot.errors.Balances                    # errors with docs
dot polkadot.errors.Balances.InsufficientBalance

# Storage (via the query category)
dot polkadot.query                              # pallets with storage items
dot polkadot.query.System                       # storage items with types
dot polkadot.query.System.Account               # storage help (use --dump for all entries)

# Constants
dot polkadot.const                              # pallets with constants
dot polkadot.const.Balances                     # list constants (offline)
dot polkadot.const.Balances.ExistentialDeposit  # look up value (connects to chain)

# Runtime APIs
dot polkadot.apis                               # all runtime APIs
dot polkadot.apis.Core                          # methods in Core

# Transaction extensions (flat — no pallet sub-level)
dot polkadot.extensions                         # all transaction extensions
dot polkadot.extensions.CheckMortality          # extension detail

--chain <name> works as an alternative to the prefix in every form (e.g. dot tx.Balances --chain polkadot). To browse pallets across all categories at once, use dot inspect (see Inspect metadata).

Transaction extensions

List the transaction extensions (also known as signed extensions) a chain declares in its runtime, with types and a marker indicating whether polkadot-api handles the extension automatically or whether you need to provide a value via --ext when building a transaction (see Submit extrinsics).

# List all transaction extensions on a chain
dot polkadot.extensions
# Output:
# Transaction extensions on polkadot (11)
#
#   AuthorizeCall              unknown               [custom]
#   ChargeTransactionPayment   Compact<u128>         [builtin]
#   CheckGenesis               unknown               [builtin]
#   CheckMetadataHash          Disabled | Enabled    [builtin]
#   CheckMortality             enum(256 variants)    [builtin]
#   CheckNonce                 Compact<u64>          [builtin]
#   CheckNonZeroSender         unknown               [builtin]
#   CheckSpecVersion           unknown               [builtin]
#   CheckTxVersion             unknown               [builtin]
#   CheckWeight                unknown               [builtin]
#   PrevalidateAttests         unknown               [builtin]

# Detail view for a single extension
dot polkadot.extensions.CheckMortality
# Output:
# CheckMortality (Transaction Extension)
#
#   Value type:       enum(256 variants)
#   AdditionalSigned: [u8; 32]
#   Handled by:       polkadot-api (builtin)

# Structured output for scripts
dot polkadot.extensions --json

extension and ext are aliases for extensions. Shell completion suggests identifiers after dot polkadot.extensions.<Tab>.

The list view tags each entry:

  • [builtin]polkadot-api fills this in for you (e.g. CheckMortality, CheckNonce, ChargeTransactionPayment, CheckMetadataHash)
  • [custom] — you must provide a value with --ext when signing, for example --ext '{"<Identifier>":{"value":<v>}}'

The detail view shows the extension's value type, its additionalSigned type, and a ready-to-adapt --ext snippet for custom extensions. Use this to discover what --ext payload a chain expects before submitting a dot tx command.

Raw JSON-RPC

Substrate nodes expose a JSON-RPC surface that lives outside runtime metadata: system_* (sync state, peers, version), chain_* (blocks, headers, finalized head), state_* (raw storage, key iteration, runtime version), author_* (mempool, key management), payment_* (fee estimation), consensus families (babe_*, grandpa_*, mmr_*, beefy_*), and the new spec families (chainSpec_v1_*, archive_v1_*, rpc_methods). The rpc category exposes them all.

Methods are discovered per-chain via the standard rpc_methods JSON-RPC call and cached at ~/.polkadot/chains/<chain>/rpc-methods.json. The set of available methods depends on the node, not the chain — an archive node adds archive_v1_*, validators may add babe_epochAuthorship, dev nodes add dev_newBlock, and --rpc-methods safe strips writes.

# List all methods the node exposes, grouped by family
dot polkadot.rpc
# Output:
# RPC methods on polkadot (129)
#
# system (20)
#   system_health  Node sync state (peers, isSyncing, shouldHavePeers).
#   system_version  Node software version string.
#   system_chain  Chain name as reported by the node.
#   ...
# chain (19)
#   chain_getBlock  Full block (header + extrinsics) by hash.
#   chain_getFinalizedHead  Hash of the latest finalized head.
#   ...

# Call a method
dot polkadot.rpc.system_health
# Output:
# {
#   "peers": 131,
#   "isSyncing": false,
#   "shouldHavePeers": true
# }

# Positional args (parsed by the same heuristic as tx args)
dot polkadot.rpc.chain_getBlockHash 1000
# Output:
# "0xcf36a1e4a16fc579136137b8388f35490f09c5bdd7b9133835eba907a8b76c30"

# Show curated info for a known method
dot polkadot.rpc.author_insertKey --help
# Output:
# author_insertKey
#   ⚠️  WRITE / state-changing
#   Insert a key into the node keystore.
#
#   Family: author
#   Args:   <keyType: string> <suri: string> <publicKey: hex>

# Refresh the cached method list (after a node upgrade)
dot polkadot.rpc --refresh

# JSON output works on any method
dot polkadot.rpc.system_health --json

dot chain add and dot chain update automatically populate the method cache, and dot chain info <name> shows a per-family breakdown. Tab completion sources from the cache, so methods on a custom chain start completing the moment you've added or updated it.

About 50 well-known methods carry curated metadata (description, named args, ⚠️ WRITE tag for state-changing calls). Any other method the node reports is callable via raw passthrough — args are forwarded as-is to the JSON-RPC params array.

Subscription methods (*_subscribe*, chainHead_v1_follow, transaction_v1_*) appear in tab-completion but error out as one-shots — they need a long-running follow session that doesn't fit a single CLI invocation:

dot polkadot.rpc.chain_subscribeAllHeads
# Error: "chain_subscribeAllHeads" is a subscription method (requires a follow
# session) and is not callable as a one-shot. Use a long-running client for
# streaming RPC.

The rpc category is flat — there's no pallet level. The form is [chain.]rpc.<method_name>, where <method_name> keeps its underscores in a single segment (e.g. polkadot.rpc.chain_getBlock).

Submit extrinsics

Build, sign, and submit transactions. Pass a Pallet.Call with arguments, or a raw SCALE-encoded call hex (e.g. from a multisig proposal or governance). Both forms display a decoded human-readable representation of the call.

# Estimate fees without submitting (no broadcast). The Decode block shows
# the call name on the header line and indented JSON below it.
dot polkadot.tx.System.remark 0xdeadbeef --from alice --dry-run
# Output:
#   Chain:  polkadot
#   From:   alice (5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY)
#   Call:   0x000010deadbeef
#   Decode: System.remark
#     {
#       "remark": "0xdeadbeef"
#     }
#   Estimated fees: 125598975

# Transfer (amount in plancks). Method names are snake_case.
dot polkadot.tx.Balances.transfer_keep_alive bob 1000000000000 --from alice --dry-run

# Submit (omit --dry-run)
dot polkadot.tx.System.remark 0xdeadbeef --from alice

# Submit a raw SCALE-encoded call (e.g. from a multisig proposal or another tool)
dot polkadot.tx 0x000010deadbeef --from alice --dry-run

# Batch multiple remarks with Utility.batch_all (comma-separated encoded calls).
# Complex decoded calls remain readable because each level is indented.
A=$(dot polkadot.tx.System.remark 0xdeadbeef --encode)
B=$(dot polkadot.tx.System.remark 0xcafe --encode)
dot polkadot.tx.Utility.batch_all "$A,$B" --from alice --dry-run
# Output (excerpt — nested calls each get their own enum {type, value} envelope):
#   Chain:  polkadot
#   From:   alice (5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY)
#   Call:   0x1a0208000010deadbeef000008cafe
#   Decode: Utility.batch_all
#     {
#       "calls": [
#         {
#           "type": "System",
#           "value": {
#             "type": "remark",
#             "value": { "remark": "0xdeadbeef" }
#           }
#         },
#         ...
#       ]
#     }
#   Estimated fees: 133994995

The --chain flag is equivalent to the chain prefix:

dot polkadot.tx.System.remark 0xdeadbeef --from alice
dot tx.System.remark 0xdeadbeef --from alice --chain polkadot

Enum shorthand

Enum arguments accept a concise Variant(value) syntax instead of verbose JSON:

# Instead of: '{"type":"system","value":{"type":"Authorized"}}'
dot polkadot.tx.Utility.dispatch_as 'system(Authorized)' $(dot polkadot.tx.System.remark 0xcafe --encode) --from alice --dry-run
# Output:
#   Chain:  polkadot
#   From:   alice (5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY)
#   Call:   0x1a030003000008cafe
#   Decode: Utility.dispatch_as
#     {
#       "as_origin": { "type": "system", "value": { "type": "Authorized" } },
#       "call":      { "type": "System", "value": { "type": "remark", "value": { "remark": "0xcafe" } } }
#     }
#   Estimated fees: 127644270

# Nested enums work too — Signed origin with an account
dot polkadot.tx.Utility.dispatch_as 'system(Signed(bob))' "$INNER_CALL" --from alice --dry-run

# Void variants — empty parens or just the name
dot polkadot.tx.Pallet.call 'Root()' ... --from alice
dot polkadot.tx.Pallet.call 'Root' ... --from alice

# JSON inside parens for struct values
dot polkadot.tx.Pallet.call 'AccountId32({"id":"0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"})' ... --from alice

Variant matching is case-insensitive (system resolves to system, authorized to Authorized). All existing formats (JSON objects, hex, SS58 addresses) continue to work unchanged.

Encode call data

Encode a call to hex without signing or submitting. Useful for preparing calls to pass to Sudo.sudo, multisig proposals, or governance. Works offline from cached metadata and does not require --from.

# Encode a remark call
dot polkadot.tx.System.remark 0xdeadbeef --encode
# Output:
# 0x000010deadbeef

# Encode a transfer (use the hex output in a batch or sudo call)
dot polkadot.tx.Balances.transfer_keep_alive bob 1000000000 --encode
# Output:
# 0x050300...02286bee

# Use encoded output with Sudo.sudo (Sudo only exists on testnets like Paseo)
dot paseo-asset-hub.tx.Sudo.sudo $(dot paseo-asset-hub.tx.System.remark 0xcafe --encode) --from alice --dry-run
# Output:
#   Chain:  paseo-asset-hub
#   From:   alice (5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY)
#   Call:   0xfb00000008cafe
#   Decode: Sudo.sudo
#     {
#       "call": { "type": "System", "value": { "type": "remark", "value": { "remark": "0xcafe" } } }
#     }
#   Estimated fees: ...

Decode call data to YAML / JSON

Decode a hex-encoded call into a YAML or JSON file that is compatible with file-based commands. This is useful for inspecting opaque call data, sharing human-readable transaction definitions, or editing parameters before re-submitting. Works offline from cached metadata and does not require --from.

# Decode a raw hex call to YAML
dot polkadot.tx.0x000010deadbeef --to-yaml
# Output:
# chain: polkadot
# tx:
#   System:
#     remark:
#       remark: "0xdeadbeef"

# Decode a raw hex call to JSON
dot polkadot.tx.0x000010deadbeef --to-json
# Output:
# {
#   "chain": "polkadot",
#   "tx": {
#     "System": {
#       "remark": {
#         "remark": "0xdeadbeef"
#       }
#     }
#   }
# }

# Encode a named call and output as YAML
dot polkadot.tx.System.remark 0xdeadbeef --to-yaml

# Round-trip: encode to hex, decode to YAML, re-encode from file
dot polkadot.tx.System.remark 0xdeadbeef --encode      # 0x000010deadbeef
dot polkadot.tx.0x000010deadbeef --to-yaml > remark.yaml
dot ./remark.yaml --encode                              # chain comes from the file

--to-yaml / --to-json are mutually exclusive with each other and with --encode and --dry-run.

Both dry-run and submission display the encoded call hex and a decoded human-readable form. The decoded call is rendered as JSON with two-space indentation under the Decode: header so even deeply nested calls (XCM, sudo, batch, dispatch_as) remain easy to scan:

  Call:   0x000010deadbeef
  Decode: System.remark
    {
      "remark": "0xdeadbeef"
    }
  Tx:     0xabc123...
  Status: ok

Complex calls (XCM teleports, batched governance proposals) keep the same shape — every nested enum becomes a { "type": ..., "value": ... } block, indented one level deeper, so you can read the structure top-down without it ever wrapping past the terminal width.

Exit codes

The CLI exits with code 1 when a finalized transaction has a dispatch error (e.g. insufficient balance, bad origin). The full transaction output (events, explorer links) is still printed before the error so you can debug the failure. Module errors are formatted as PalletName.ErrorVariant (e.g. Balances.InsufficientBalance).

dot polkadot.tx.Balances.transfer_keep_alive bob 999999999999999999 --from alice
# ... events and explorer links ...
# Error: Transaction dispatch error: Balances.InsufficientBalance
echo $?  # 1

Stale metadata detection

When a tx, --dry-run, or query fails with an error that smells like stale metadata — a runtime wasm trap, a SCALE codec/decode error, or a fee-estimation panic — the CLI compares your locally cached metadata's runtime fingerprint against the live chain. If it has changed, the CLI appends a one-line suggestion telling you exactly which command to run:

Error: The runtime rejected this transaction in the runtime's validate_transaction step.
  Cause: a runtime invariant failed — typically the call's arguments are out of range, …

⚠ Local metadata for "paseo-people-next" is out of date (spec 1018 → 1020).
   Run: dot chain update paseo-people-next

The fingerprint includes the runtime code hash, so the check also catches local-node restarts where the wasm changed but specVersion was kept the same. No automatic refetch happens — the original error still propagates with a non-zero exit code, you just get an actionable suggestion.

The check only fires on suspected-stale errors, so the happy path pays no extra RPC. Set DOT_TRUST_CACHED_METADATA=1 to disable the check entirely (e.g. for CI loops where you've just refreshed manually).

Argument parsing errors

When a call argument is invalid, the CLI shows a contextual error message with the argument name, the expected type, and a hint:

dot polkadot.tx.Balances.transfer_keep_alive bob abc --encode
# Error: Invalid value for argument 'value' (expected Compact<u128>): "abc"
#   Hint: Compact<u128>

For struct-based calls, the error identifies the specific field that failed. For tuple-based calls, it shows the argument index. The original parse error is preserved as the cause for programmatic access.

Wait level

By default, dot tx waits for finalization (~30s on Polkadot). Use --wait / -w to return earlier:

# Return as soon as the tx is broadcast (fastest)
dot polkadot.tx.System.remark 0xdead --from alice --wait broadcast

# Return when included in a best block
dot polkadot.tx.System.remark 0xdead --from alice -w best-block
dot polkadot.tx.System.remark 0xdead --from alice -w best    # alias

# Wait for finalization (default, unchanged)
dot polkadot.tx.System.remark 0xdead --from alice --wait finalized
dot polkadot.tx.System.remark 0xdead --from alice            # same

| Level | Resolves when | Events shown | Explorer links | |-------|---------------|:---:|:---:| | broadcast | Tx is broadcast to the network | — | — | | best-block / best | Tx is included in a best block | yes | yes | | finalized (default) | Tx is finalized | yes | yes |

The --wait flag is silently ignored when combined with --dry-run or --encode (both return before submission).

Custom signed extensions

Chains with non-standard signed extensions are auto-handled:

  • void → empty bytes
  • Option<T>None
  • enum with Disabled variant → Disabled

For manual override, use --ext with a JSON object:

dot polkadot.tx.System.remark 0xdeadbeef --from alice --ext '{"MyExtension":{"value":"..."}}'

Not sure which extensions a chain exposes? Run dot <chain>.extensions (see Transaction extensions) to list them all with value types and a [builtin] / [custom] marker.

Transaction options

Override low-level transaction parameters. Useful for rapid-fire submission (custom nonce), priority fees (tip), or controlling transaction lifetime (mortality).

| Flag | Value | Description | |------|-------|-------------| | --nonce <n> | non-negative integer | Override the auto-detected nonce | | --tip <amount> | non-negative integer (planck) | Priority tip for the transaction pool | | --mortality <spec> | immortal or period (min 4) | Transaction mortality window | | --at <block> | 0x-prefixed block hash, "best", or "finalized" | Block to read/validate against (defaults to finalized). Also honored on query.* and apis.* for historical reads; tx submission rejects "best". |

# Fire-and-forget: submit two txs in rapid succession with manual nonces
dot polkadot.tx.System.remark 0xdead --from alice --nonce 0 --wait broadcast
dot polkadot.tx.System.remark 0xbeef --from alice --nonce 1 --wait broadcast

# Add a priority tip (in planck)
dot polkadot.tx.Balances.transfer_keep_alive bob 1000000000 --from alice --tip 1000000

# Submit an immortal transaction (no expiry)
dot polkadot.tx.System.remark 0xdead --from alice --mortality immortal

# Set a custom mortality period (rounds up to nearest power of two)
dot polkadot.tx.System.remark 0xdead --from alice --mortality 128

# Validate against a specific block hash
dot polkadot.tx.System.remark 0xdead --from alice --at 0x1234...abcd

# Combine: rapid-fire with tip and broadcast-only
dot polkadot.tx.System.remark 0xdead --from alice --nonce 5 --tip 500000 --wait broadcast

When set, nonce / tip / mortality / at are shown in both --dry-run and submission output. These flags are silently ignored with --encode, --to-yaml, and --to-json (which return before signing).

Pay fees in an alternative asset

On asset-hub-style chains (Polkadot Asset Hub, Paseo Asset Hub, etc.) the ChargeAssetTxPayment signed extension lets a transaction pay its fees in a non-native asset. Use --asset <json> to select the asset — the value is an XCM location (JSON) identifying the asset, which the runtime's asset-conversion pool swaps for native tokens at dispatch time.

# Define the USDT location once (asset id 1337, PalletInstance 50)
USDT='{"parents":0,"interior":{"type":"X2","value":[{"type":"PalletInstance","value":50},{"type":"GeneralIndex","value":"1337"}]}}'

# Dry-run to see the native-denominated fee estimate
dot polkadot-asset-hub.tx.Balances.transfer_keep_alive bob 1000000000 \
  --from alice --dry-run --asset "$USDT"
# Output:
#   Chain:  polkadot-asset-hub
#   From:   alice (5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY)
#   Call:   0x0a0300d435...02286bee
#   Decode: Balances.transfer_keep_alive { dest: Id(...), value: 1000000000 }
#   Asset:  {"parents":0,"interior":{"type":"X2","value":[{"type":"PalletInstance","value":50},{"type":"GeneralIndex","value":"1337"}]}}
#   Estimated fees: 9249754

# Submit (omit --dry-run)
dot polkadot-asset-hub.tx.Balances.transfer_keep_alive bob 1000000000 \
  --from alice --asset "$USDT"

The --asset echo is included in dry-run and submission output (and --json). A few things to know:

  • The target chain must expose ChargeAssetTxPayment in its signed extensions — asset-hub-style chains do; plain relay chains don't, and --asset is silently ignored on those.
  • The estimated fee shown is native-denominated. The on-chain asset-conversion pool determines the actual asset amount charged at execution time.
  • --asset is unnecessary (and not compatible) with --unsigned: unsigned transactions default ChargeAssetTxPayment to zero tip / no asset.
  • Combine freely with --tip, --nonce, --mortality, and --at. --tip is encoded inside the asset-payment extension alongside the asset id.

Under the hood, the CLI routes the asset through its custom signed-extension pipeline rather than polkadot-api's native asset option — this works around a papi compatibility check that rejects XCM Location JSON on the unsafe API path.

Unsigned/authorized transactions

Submit transactions without a signer using --unsigned. This is for calls authorized by on-chain mechanisms (e.g. the AuthorizeCall extension) rather than cryptographic signatures — typically governance-authorized calls on chains like the People chain.

# Submit an authorized call on the People chain
dot polkadot-people.tx.People.create_people_collection --unsigned

# Dry-run to inspect before submitting
dot polkadot-people.tx.People.create_people_collection --unsigned --dry-run

# Encode the full general transaction bytes
dot polkadot-people.tx.People.create_people_collection --unsigned --encode

# With raw hex call data
dot polkadot-people.tx 0x3306 --unsigned

# JSON output for scripting
dot polkadot-people.tx.People.create_people_collection --unsigned --json

The CLI constructs a v5 general transaction with all extension values auto-defaulted (VerifySignature::Disabled, Era::Immortal, nonce 0, tip 0, etc.). Override individual extensions with --ext if needed.

--unsigned is mutually exclusive with --from, --nonce, --tip, and --mortality. File-based input supports unsigned: true:

chain: polkadot-people
unsigned: true
tx:
  People:
    create_people_collection: null

File-based commands

Run any dot command from a YAML or JSON file. Especially useful for complex calls like XCM messages that are hard to construct inline.

Teleport DOT from Asset Hub to the relay chain:

# teleport-dot.xcm.yaml
chain: polkadot-asset-hub
tx:
  PolkadotXcm:
    limited_teleport_assets:
      dest:
        type: V4
        value:
          parents: 1
          interior:
            type: Here
      beneficiary:
        type: V4
        value:
          parents: 0
          interior:
            type: X1
            value:
              - type: AccountId32
                value:
                  network: null
                  id: "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"
      assets:
        type: V4
        value:
          - id:
              parents: 1
              interior:
                type: Here
            fun:
              type: Fungible
              value: 10000000000
      fee_asset_item: 0
      weight_limit:
        type: Unlimited

Reserve transfer USDC (asset 1337, 6 decimals) from Asset Hub to Hydration:

# reserve-transfer-usdc.xcm.yaml
chain: polkadot-a