Promise and RxJS wrappers around the Polkadot JS RPC for interacting with Polkadot and Substrate-based blockchains
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Access runtime events, errors, and metadata for dynamic API behavior. The @polkadot/api provides comprehensive access to blockchain runtime information that enables dynamic interface generation and event monitoring.
Access to all runtime events emitted by the blockchain.
interface DecoratedEvents<ApiType> {
[section: string]: {
[event: string]: EventCreator;
};
}
interface EventCreator {
/**
* Create event instance
* @param data - Event data parameters
* @returns Event instance
*/
(...data: any[]): Event;
/**
* Check if event matches this type
* @param event - Event to check
* @returns Type guard result
*/
is(event: Event): boolean;
/** Event metadata */
readonly meta: EventMetadataLatest;
/** Event section name */
readonly section: string;
/** Event method name */
readonly method: string;
}
interface Event extends Codec {
/** Event section (pallet name) */
readonly section: string;
/** Event method name */
readonly method: string;
/** Event data parameters */
readonly data: Codec[];
/** Event metadata */
readonly meta: EventMetadataLatest;
/** Event index */
readonly index: Bytes;
}Access to all runtime errors that can be returned by extrinsics.
interface DecoratedErrors<ApiType> {
[section: string]: {
[error: string]: ErrorCreator;
};
}
interface ErrorCreator {
/**
* Check if error matches this type
* @param error - Error to check
* @returns Type guard result
*/
is(error: RegistryError): boolean;
/** Error metadata */
readonly meta: ErrorMetadataLatest;
/** Error section name */
readonly section: string;
/** Error name */
readonly name: string;
}
interface RegistryError extends Codec {
/** Error section (pallet name) */
readonly section: string;
/** Error name */
readonly name: string;
/** Error documentation */
readonly docs: string[];
/** Error index */
readonly index: number;
}Complete runtime metadata providing type and interface information.
interface Metadata extends Codec {
/** Metadata version */
readonly version: number;
/** Runtime metadata */
readonly asLatest: MetadataLatest;
/** Convert to JSON */
toJSON(): any;
/** Convert to human-readable format */
toHuman(): any;
}
interface MetadataLatest {
/** Lookup registry for types */
readonly lookup: PortableRegistry;
/** Pallet metadata */
readonly pallets: Vec<PalletMetadata>;
/** Extrinsic metadata */
readonly extrinsic: ExtrinsicMetadata;
/** Runtime API metadata */
readonly apis: Vec<RuntimeApiMetadata>;
/** Outer enums (Event, Call, Error) */
readonly outerEnums: MetadataOuterEnums;
/** Runtime type information */
readonly type: SiLookupTypeId;
}
interface PalletMetadata {
/** Pallet name */
readonly name: string;
/** Storage metadata */
readonly storage?: PalletStorageMetadata;
/** Call metadata */
readonly calls?: PalletCallMetadata;
/** Event metadata */
readonly events?: PalletEventMetadata;
/** Constants metadata */
readonly constants: Vec<PalletConstantMetadata>;
/** Error metadata */
readonly errors?: PalletErrorMetadata;
/** Pallet index */
readonly index: number;
}Structure for events emitted during block execution.
interface EventRecord extends Codec {
/** Execution phase when event was emitted */
readonly phase: Phase;
/** The actual event */
readonly event: Event;
/** Event topics for filtering */
readonly topics: Vec<Hash>;
}
interface Phase extends Enum {
/** Emitted during block initialization */
readonly isApplyExtrinsic: boolean;
/** Emitted during extrinsic execution */
readonly asApplyExtrinsic: number;
/** Emitted during block finalization */
readonly isFinalization: boolean;
/** Emitted during block initialization */
readonly isInitialization: boolean;
}Type registry providing runtime type definitions.
interface Registry {
/** Chain properties */
readonly chainDecimals: number[];
readonly chainSS58: number;
readonly chainTokens: string[];
/**
* Create type instance
* @param type - Type name or definition
* @param value - Initial value
* @returns Type instance
*/
createType<T = Codec>(type: string, value?: any): T;
/**
* Get type definition
* @param type - Type name
* @returns Type definition
*/
getDefinition(type: string): string;
/**
* Check if type exists
* @param type - Type name
* @returns Existence check
*/
hasType(type: string): boolean;
/**
* Register custom types
* @param types - Type definitions
*/
register(types: RegistryTypes): void;
/**
* Set chain properties
* @param properties - Chain properties
*/
setChainProperties(properties: ChainProperties): void;
/**
* Set metadata
* @param metadata - Runtime metadata
*/
setMetadata(metadata: Metadata): void;
/**
* Find call by index
* @param callIndex - Call index bytes
* @returns Call function
*/
findMetaCall(callIndex: Uint8Array): CallFunction;
/**
* Find error by index
* @param errorIndex - Error index bytes
* @returns Registry error
*/
findMetaError(errorIndex: Uint8Array): RegistryError;
}import { ApiPromise } from "@polkadot/api";
const api = await ApiPromise.create();
// Subscribe to all system events
const unsubscribe = await api.query.system.events((events) => {
console.log(`Received ${events.length} events:`);
events.forEach((record, index) => {
const { event, phase } = record;
const types = event.typeDef;
console.log(`\t${index}: ${event.section}.${event.method}${
phase.isApplyExtrinsic ? ` (extrinsic ${phase.asApplyExtrinsic})` : ''
}`);
// Show event data
event.data.forEach((data, index) => {
console.log(`\t\t${types[index].type}: ${data.toString()}`);
});
});
});import { ApiPromise } from "@polkadot/api";
const api = await ApiPromise.create();
// Filter for balance transfer events
const unsubscribe = await api.query.system.events((events) => {
events.forEach(({ event, phase }) => {
// Check if event is a balance transfer
if (api.events.balances.Transfer.is(event)) {
const [from, to, amount] = event.data;
console.log(`Transfer: ${from} -> ${to}: ${amount.toHuman()}`);
}
// Check for transaction fees
if (api.events.balances.Withdraw.is(event)) {
const [account, amount] = event.data;
console.log(`Fee paid by ${account}: ${amount.toHuman()}`);
}
// Check for failed extrinsics
if (api.events.system.ExtrinsicFailed.is(event)) {
const [dispatchError, dispatchInfo] = event.data;
console.log('Extrinsic failed:', dispatchError.toString());
}
});
});import { ApiPromise } from "@polkadot/api";
const api = await ApiPromise.create();
// Handle transaction with error checking
const transfer = api.tx.balances.transferAllowDeath(
'1FRMM8PEiWXYax7rpS6X4XZX1aAAxSWx1CrKTyrVYhV24fg',
1000000000000
);
const unsubscribe = await transfer.signAndSend(sender, ({ status, events, dispatchError }) => {
if (status.isInBlock) {
console.log(`Transaction included in block ${status.asInBlock}`);
if (dispatchError) {
if (dispatchError.isModule) {
// Module error - can decode using metadata
const decoded = api.registry.findMetaError(dispatchError.asModule);
const { docs, name, section } = decoded;
console.log(`Error: ${section}.${name}: ${docs.join(' ')}`);
// Check specific error types
if (api.errors.balances.InsufficientBalance.is(dispatchError)) {
console.log('Insufficient balance for transfer');
}
} else {
// Other error types
console.log('Error:', dispatchError.toString());
}
}
// Check events for additional context
events.forEach(({ event }) => {
if (api.events.system.ExtrinsicFailed.is(event)) {
console.log('Extrinsic failed event emitted');
} else if (api.events.system.ExtrinsicSuccess.is(event)) {
console.log('Extrinsic succeeded');
}
});
}
});import { ApiPromise } from "@polkadot/api";
const api = await ApiPromise.create();
// Get runtime metadata
const metadata = api.runtimeMetadata;
console.log(`Metadata version: ${metadata.version}`);
// Explore pallets
metadata.asLatest.pallets.forEach((pallet) => {
console.log(`\nPallet: ${pallet.name} (index: ${pallet.index})`);
// Storage items
if (pallet.storage) {
console.log(' Storage:');
pallet.storage.items.forEach((item) => {
console.log(` ${item.name}: ${item.type}`);
});
}
// Calls (extrinsics)
if (pallet.calls) {
console.log(' Calls:');
const calls = api.registry.lookup.getTypeDef(pallet.calls.type);
if (calls.type === 'Enum') {
calls.sub.forEach((call) => {
console.log(` ${call.name}`);
});
}
}
// Events
if (pallet.events) {
console.log(' Events:');
const events = api.registry.lookup.getTypeDef(pallet.events.type);
if (events.type === 'Enum') {
events.sub.forEach((event) => {
console.log(` ${event.name}`);
});
}
}
// Constants
if (pallet.constants.length > 0) {
console.log(' Constants:');
pallet.constants.forEach((constant) => {
const value = api.consts[pallet.name.toString()][constant.name.toString()];
console.log(` ${constant.name}: ${value?.toHuman() || 'N/A'}`);
});
}
});import { ApiPromise } from "@polkadot/api";
const api = await ApiPromise.create();
// Access registry
const registry = api.registry;
// Check chain properties
console.log('Chain decimals:', registry.chainDecimals);
console.log('Chain tokens:', registry.chainTokens);
console.log('SS58 format:', registry.chainSS58);
// Create custom types
const accountId = registry.createType('AccountId', '1FRMM8PEiWXYax7rpS6X4XZX1aAAxSWx1CrKTyrVYhV24fg');
console.log('Account ID:', accountId.toString());
const balance = registry.createType('Balance', 1000000000000);
console.log('Balance:', balance.toHuman());
// Type definitions
const hasAccountId = registry.hasType('AccountId');
console.log('Has AccountId type:', hasAccountId);
const accountIdDef = registry.getDefinition('AccountId');
console.log('AccountId definition:', accountIdDef);import { ApiPromise } from "@polkadot/api";
const api = await ApiPromise.create();
// Create custom event handlers
class EventHandler {
constructor(private api: ApiPromise) {}
handleBalanceEvents(events: EventRecord[]) {
events.forEach(({ event }) => {
if (this.api.events.balances.Transfer.is(event)) {
this.onTransfer(event.data);
} else if (this.api.events.balances.Deposit.is(event)) {
this.onDeposit(event.data);
} else if (this.api.events.balances.Withdraw.is(event)) {
this.onWithdraw(event.data);
}
});
}
private onTransfer([from, to, amount]: any[]) {
console.log(`💸 Transfer: ${from} -> ${to}: ${amount.toHuman()}`);
}
private onDeposit([account, amount]: any[]) {
console.log(`💰 Deposit: ${account}: ${amount.toHuman()}`);
}
private onWithdraw([account, amount]: any[]) {
console.log(`💳 Withdraw: ${account}: ${amount.toHuman()}`);
}
handleStakingEvents(events: EventRecord[]) {
events.forEach(({ event }) => {
if (this.api.events.staking.Bonded?.is(event)) {
const [stash, amount] = event.data;
console.log(`🔗 Bonded: ${stash}: ${amount.toHuman()}`);
} else if (this.api.events.staking.Unbonded?.is(event)) {
const [stash, amount] = event.data;
console.log(`🔓 Unbonded: ${stash}: ${amount.toHuman()}`);
} else if (this.api.events.staking.Rewarded?.is(event)) {
const [stash, amount] = event.data;
console.log(`🎁 Reward: ${stash}: ${amount.toHuman()}`);
}
});
}
}
// Usage
const eventHandler = new EventHandler(api);
const unsubscribe = await api.query.system.events((events) => {
eventHandler.handleBalanceEvents(events);
eventHandler.handleStakingEvents(events);
});import { ApiPromise } from "@polkadot/api";
const api = await ApiPromise.create();
// Monitor runtime version changes
let currentSpecVersion = api.runtimeVersion.specVersion.toNumber();
console.log(`Initial runtime version: ${currentSpecVersion}`);
const unsubscribe = await api.rpc.state.subscribeRuntimeVersion((version) => {
const newSpecVersion = version.specVersion.toNumber();
if (newSpecVersion !== currentSpecVersion) {
console.log(`🔄 Runtime upgraded: ${currentSpecVersion} -> ${newSpecVersion}`);
console.log(`Implementation: ${version.implName}`);
console.log(`Spec name: ${version.specName}`);
currentSpecVersion = newSpecVersion;
// Metadata will be automatically updated
// You may want to refresh your application state here
}
});
// Access version information
const version = api.runtimeVersion;
console.log('Runtime info:');
console.log(` Spec name: ${version.specName}`);
console.log(` Impl name: ${version.implName}`);
console.log(` Spec version: ${version.specVersion}`);
console.log(` Impl version: ${version.implVersion}`);
console.log(` Transaction version: ${version.transactionVersion}`);
console.log(` State version: ${version.stateVersion}`);import { ApiPromise } from "@polkadot/api";
const api = await ApiPromise.create();
// Application state manager based on events
class ChainStateManager {
private accounts = new Map<string, any>();
constructor(private api: ApiPromise) {
this.startEventListener();
}
private async startEventListener() {
const unsubscribe = await this.api.query.system.events((events) => {
events.forEach(({ event, phase }) => {
this.processEvent(event, phase);
});
});
}
private processEvent(event: Event, phase: Phase) {
// Process balance changes
if (this.api.events.balances.Transfer.is(event)) {
const [from, to, amount] = event.data;
this.updateAccountBalance(from.toString(), -amount.toBn());
this.updateAccountBalance(to.toString(), amount.toBn());
}
// Process staking events
if (this.api.events.staking?.Bonded?.is(event)) {
const [stash, amount] = event.data;
this.updateStakingInfo(stash.toString(), { bonded: amount.toBn() });
}
// Process democracy events
if (this.api.events.democracy?.Proposed?.is(event)) {
const [proposalIndex, deposit] = event.data;
console.log(`New proposal ${proposalIndex} with deposit ${deposit.toHuman()}`);
}
}
private updateAccountBalance(address: string, change: any) {
const account = this.accounts.get(address) || { balance: this.api.createType('Balance', 0) };
account.balance = account.balance.add(change);
this.accounts.set(address, account);
console.log(`Account ${address} balance: ${account.balance.toHuman()}`);
}
private updateStakingInfo(address: string, stakingInfo: any) {
const account = this.accounts.get(address) || {};
account.staking = { ...account.staking, ...stakingInfo };
this.accounts.set(address, account);
console.log(`Staking info for ${address}:`, account.staking);
}
getAccountInfo(address: string) {
return this.accounts.get(address);
}
getAllAccounts() {
return Array.from(this.accounts.entries());
}
}
// Usage
const stateManager = new ChainStateManager(api);
// Query account info after some time
setTimeout(() => {
const accountInfo = stateManager.getAccountInfo('1FRMM8PEiWXYax7rpS6X4XZX1aAAxSWx1CrKTyrVYhV24fg');
console.log('Account info:', accountInfo);
}, 10000);Install with Tessl CLI
npx tessl i tessl/npm-polkadot--api