Quickstart
Everything you need to interact with Subtensor programmatically. Pick your language, copy the code.
Connect
All interactions start with a WebSocket connection to a Subtensor node. The finney endpoint is the main public network.
import { createClient } from "polkadot-api";
import { getWsProvider } from "polkadot-api/ws";
import { sub } from "@polkadot-api/descriptors"; // generated by: npx papi add sub -w wss://entrypoint-finney.opentensor.ai:443
const client = createClient(getWsProvider("wss://entrypoint-finney.opentensor.ai:443"));
const api = client.getTypedApi(sub);
const finalized = await client.getFinalizedBlock();
console.log(`Connected at block #${finalized.number}`);You can also browse live chain data on the bittensor.ai chain explorer.
Query Storage
Read on-chain state like balances, stake amounts, and subnet parameters. Storage items are the chain's database.
import { createClient, Binary } from "polkadot-api";
import { getWsProvider } from "polkadot-api/ws";
import { sub } from "@polkadot-api/descriptors"; // generated by: npx papi add sub -w wss://entrypoint-finney.opentensor.ai:443
const client = createClient(getWsProvider("wss://entrypoint-finney.opentensor.ai:443"));
const api = client.getTypedApi(sub);
// Query TotalStake storage (no keys - plain value)
const result = await api.query.SubtensorModule.TotalStake.getValue();
console.log("TotalStake:", result);Read Constants
Constants are fixed runtime values like limits and defaults. They change only with runtime upgrades, not transactions.
import { createClient, Binary } from "polkadot-api";
import { getWsProvider } from "polkadot-api/ws";
import { sub } from "@polkadot-api/descriptors"; // generated by: npx papi add sub -w wss://entrypoint-finney.opentensor.ai:443
const client = createClient(getWsProvider("wss://entrypoint-finney.opentensor.ai:443"));
const api = client.getTypedApi(sub);
// Query InitialMaxAllowedUids constant
const value = await api.constants.SubtensorModule.InitialMaxAllowedUids();
console.log("InitialMaxAllowedUids:", value);Subscribe to Events
Listen for events emitted by the chain. Events signal that something happened — a stake was added, a subnet was registered, etc.
import { createClient, Binary } from "polkadot-api";
import { getWsProvider } from "polkadot-api/ws";
import { sub } from "@polkadot-api/descriptors"; // generated by: npx papi add sub -w wss://entrypoint-finney.opentensor.ai:443
const client = createClient(getWsProvider("wss://entrypoint-finney.opentensor.ai:443"));
const api = client.getTypedApi(sub);
// Subscribe to StakeAdded events
client.finalizedBlock$.subscribe(async (block) => {
const events = await api.event.SubtensorModule.StakeAdded.get(block.hash);
for (const evt of events) {
console.log("StakeAdded:", evt.payload);
}
});Decode Event Data
After receiving an event, decode its arguments. Balance fields are in RAO (divide by 10⁹ for TAO). Accounts are SS58-encoded.
// Decode StakeAdded event payload (typed object)
const [coldkey, hotkey, tao_amount, alpha_received, netuid, fee_paid_tao] = evt.payload;Build an Extrinsic
Construct a call with its parameters. This example builds an add_stake call to stake TAO to a hotkey on a subnet.
import { createClient, Binary } from "polkadot-api";
import { getWsProvider } from "polkadot-api/ws";
import { sub } from "@polkadot-api/descriptors"; // generated by: npx papi add sub -w wss://entrypoint-finney.opentensor.ai:443
const client = createClient(getWsProvider("wss://entrypoint-finney.opentensor.ai:443"));
const api = client.getTypedApi(sub);
// Build add_stake call (typed, named args)
const hotkey = "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY";
const netuid = 1;
const amount_staked = 1_000_000_000n;
const tx = api.tx.SubtensorModule.add_stake({
hotkey,
netuid,
amount_staked,
});Submit an Extrinsic
Sign and submit the call, then wait for finalization. Check for success events and handle dispatch errors.
import { getPolkadotSigner } from "polkadot-api/signer";
import { sr25519CreateDerive } from "@polkadot-labs/hdkd";
import { DEV_PHRASE, entropyToMiniSecret, mnemonicToEntropy } from "@polkadot-labs/hdkd-helpers";
// Build a signer (replace DEV_PHRASE with your real seed phrase before running on mainnet)
const entropy = mnemonicToEntropy(DEV_PHRASE);
const miniSecret = entropyToMiniSecret(entropy);
const derive = sr25519CreateDerive(miniSecret);
const aliceKey = derive("//Alice");
const signer = getPolkadotSigner(aliceKey.publicKey, "Sr25519", aliceKey.sign);
// Submit and await finalization
const result = await tx.signAndSubmit(signer);
if (result.ok) {
console.log("add_stake succeeded in block", result.block.hash);
for (const evt of result.events) {
if (evt.type === "SubtensorModule" && evt.value.type === "StakeAdded") {
console.log("StakeAdded emitted:", evt.value.value);
}
}
} else {
console.error("add_stake failed:", result.dispatchError);
}
// For MEV-protected (encrypted) submission, see:
// https://subtensor.com/learn/technical/mev-shieldDecode SCALE Types
Substrate uses SCALE encoding for all on-chain data. Decode raw bytes back into typed values.
import { TypeRegistry } from "@polkadot/types";
const registry = new TypeRegistry();
// Decode u64 from hex bytes
const decoded = registry.createType("u64", "0x...");
console.log("Value:", decoded.toNumber());