CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-polkadot--api

Promise and RxJS wrappers around the Polkadot JS RPC for interacting with Polkadot and Substrate-based blockchains

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

events-metadata.mddocs/

Events and Metadata

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.

Capabilities

Runtime Events

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;
}

Runtime Errors

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;
}

Runtime Metadata

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;
}

Event Records

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;
}

Registry and Type Information

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;
}

Usage Examples

Event Monitoring

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()}`);
    });
  });
});

Specific Event Filtering

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());
    }
  });
});

Error Handling

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');
      }
    });
  }
});

Metadata Exploration

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'}`);
    });
  }
});

Type Registry Usage

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);

Custom Event Handling

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);
});

Runtime Version Monitoring

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}`);

Event-Driven Application Logic

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

docs

api-initialization.md

blockchain-queries.md

events-metadata.md

index.md

network-providers.md

rpc-operations.md

transaction-submission.md

tile.json