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
Direct access to node RPC methods for chain information, state queries, and system operations. The RPC interface provides raw access to the underlying blockchain node functionality through standardized JSON-RPC calls.
Access blockchain and block-related information.
interface ChainRpc<ApiType> {
/**
* Get block by hash
* @param hash - Block hash (optional, defaults to best block)
* @returns Block information
*/
getBlock(hash?: Hash): ApiType extends 'rxjs' ? Observable<SignedBlock> : Promise<SignedBlock>;
/**
* Get block hash by number
* @param blockNumber - Block number (optional, defaults to best block)
* @returns Block hash
*/
getBlockHash(blockNumber?: BlockNumber): ApiType extends 'rxjs' ? Observable<Hash> : Promise<Hash>;
/**
* Get block header by hash
* @param hash - Block hash (optional, defaults to best block)
* @returns Block header
*/
getHeader(hash?: Hash): ApiType extends 'rxjs' ? Observable<Header> : Promise<Header>;
/**
* Subscribe to new block headers
* @param callback - Callback function for new headers
* @returns Unsubscribe function or Observable
*/
subscribeNewHeads(callback?: (header: Header) => void): ApiType extends 'rxjs'
? Observable<Header>
: UnsubscribePromise;
/**
* Subscribe to all new block headers including forks
* @param callback - Callback function for new headers
* @returns Unsubscribe function or Observable
*/
subscribeAllHeads(callback?: (header: Header) => void): ApiType extends 'rxjs'
? Observable<Header>
: UnsubscribePromise;
/**
* Subscribe to finalized block headers
* @param callback - Callback function for finalized headers
* @returns Unsubscribe function or Observable
*/
subscribeFinalizedHeads(callback?: (header: Header) => void): ApiType extends 'rxjs'
? Observable<Header>
: UnsubscribePromise;
}Query and subscribe to chain state changes.
interface StateRpc<ApiType> {
/**
* Get storage value by key
* @param key - Storage key
* @param at - Block hash to query at (optional)
* @returns Storage value
*/
getStorage(key: StorageKey, at?: Hash): ApiType extends 'rxjs'
? Observable<StorageData | null>
: Promise<StorageData | null>;
/**
* Get multiple storage values by keys
* @param keys - Array of storage keys
* @param at - Block hash to query at (optional)
* @returns Array of storage values
*/
queryStorage(keys: StorageKey[], at?: Hash): ApiType extends 'rxjs'
? Observable<[Hash, StorageChangeSet][]>
: Promise<[Hash, StorageChangeSet][]>;
/**
* Subscribe to storage changes
* @param keys - Storage keys to watch
* @param callback - Callback for storage changes
* @returns Unsubscribe function or Observable
*/
subscribeStorage(
keys: StorageKey[],
callback?: (changes: StorageChangeSet) => void
): ApiType extends 'rxjs'
? Observable<StorageChangeSet>
: UnsubscribePromise;
/**
* Get runtime metadata
* @param at - Block hash to query at (optional)
* @returns Runtime metadata
*/
getMetadata(at?: Hash): ApiType extends 'rxjs'
? Observable<Metadata>
: Promise<Metadata>;
/**
* Get runtime version
* @param at - Block hash to query at (optional)
* @returns Runtime version
*/
getRuntimeVersion(at?: Hash): ApiType extends 'rxjs'
? Observable<RuntimeVersion>
: Promise<RuntimeVersion>;
/**
* Call runtime API method
* @param method - Runtime API method name
* @param data - Method parameters
* @param at - Block hash to query at (optional)
* @returns Runtime API result
*/
call(method: string, data: Bytes, at?: Hash): ApiType extends 'rxjs'
? Observable<Bytes>
: Promise<Bytes>;
/**
* Get storage keys with pagination
* @param prefix - Storage key prefix
* @param pageSize - Number of keys per page
* @param startKey - Starting key for pagination
* @param at - Block hash to query at (optional)
* @returns Array of storage keys
*/
getKeysPaged(
prefix: StorageKey,
pageSize: number,
startKey?: StorageKey,
at?: Hash
): ApiType extends 'rxjs'
? Observable<StorageKey[]>
: Promise<StorageKey[]>;
}Access node and network information.
interface SystemRpc<ApiType> {
/**
* Get chain name
* @returns Chain name
*/
chain(): ApiType extends 'rxjs' ? Observable<Text> : Promise<Text>;
/**
* Get node name
* @returns Node implementation name
*/
name(): ApiType extends 'rxjs' ? Observable<Text> : Promise<Text>;
/**
* Get node version
* @returns Node version
*/
version(): ApiType extends 'rxjs' ? Observable<Text> : Promise<Text>;
/**
* Get account next index (nonce)
* @param account - Account address
* @returns Account nonce
*/
accountNextIndex(account: AccountId): ApiType extends 'rxjs'
? Observable<Index>
: Promise<Index>;
/**
* Add reserved peer
* @param peer - Peer multiaddress
* @returns Success result
*/
addReservedPeer(peer: string): ApiType extends 'rxjs'
? Observable<Text>
: Promise<Text>;
/**
* Get node health status
* @returns Health information
*/
health(): ApiType extends 'rxjs' ? Observable<Health> : Promise<Health>;
/**
* Get local peer ID
* @returns Peer ID
*/
localPeerId(): ApiType extends 'rxjs' ? Observable<Text> : Promise<Text>;
/**
* Get connected peers
* @returns Array of peer information
*/
peers(): ApiType extends 'rxjs' ? Observable<PeerInfo[]> : Promise<PeerInfo[]>;
/**
* Get node properties
* @returns Node properties
*/
properties(): ApiType extends 'rxjs' ? Observable<ChainProperties> : Promise<ChainProperties>;
}Submit and manage transactions in the transaction pool.
interface AuthorRpc<ApiType> {
/**
* Submit transaction to the pool
* @param extrinsic - Signed extrinsic
* @returns Transaction hash
*/
submitExtrinsic(extrinsic: Extrinsic): ApiType extends 'rxjs'
? Observable<Hash>
: Promise<Hash>;
/**
* Submit and watch transaction status
* @param extrinsic - Signed extrinsic
* @param callback - Status callback
* @returns Unsubscribe function or Observable
*/
submitAndWatchExtrinsic(
extrinsic: Extrinsic,
callback?: (status: ExtrinsicStatus) => void
): ApiType extends 'rxjs'
? Observable<ExtrinsicStatus>
: UnsubscribePromise;
/**
* Get pending extrinsics
* @returns Array of pending extrinsics
*/
pendingExtrinsics(): ApiType extends 'rxjs'
? Observable<Extrinsic[]>
: Promise<Extrinsic[]>;
/**
* Remove extrinsic from pool
* @param bytesOrHash - Extrinsic bytes or hash
* @returns Array of removed transaction hashes
*/
removeExtrinsic(bytesOrHash: Vec<ExtrinsicOrHash>): ApiType extends 'rxjs'
? Observable<Hash[]>
: Promise<Hash[]>;
}import { ApiPromise } from "@polkadot/api";
const api = await ApiPromise.create();
// Get basic chain information
const [chain, nodeName, nodeVersion] = await Promise.all([
api.rpc.system.chain(),
api.rpc.system.name(),
api.rpc.system.version()
]);
console.log(`Connected to ${chain} using ${nodeName} v${nodeVersion}`);
// Get current block information
const bestHash = await api.rpc.chain.getBlockHash();
const bestBlock = await api.rpc.chain.getBlock(bestHash);
const header = bestBlock.block.header;
console.log(`Best block: #${header.number} (${bestHash})`);
console.log(`Parent hash: ${header.parentHash}`);
console.log(`State root: ${header.stateRoot}`);import { ApiPromise } from "@polkadot/api";
const api = await ApiPromise.create();
// Subscribe to new blocks
const unsubscribeHeads = await api.rpc.chain.subscribeNewHeads((header) => {
console.log(`New block #${header.number} with hash ${header.hash}`);
console.log(` Parent: ${header.parentHash}`);
console.log(` Extrinsics root: ${header.extrinsicsRoot}`);
});
// Subscribe to finalized blocks only
const unsubscribeFinalized = await api.rpc.chain.subscribeFinalizedHeads((header) => {
console.log(`Finalized block #${header.number}`);
});
// Stop subscriptions
// unsubscribeHeads();
// unsubscribeFinalized();import { ApiPromise } from "@polkadot/api";
const api = await ApiPromise.create();
// Query specific storage item
const account = "1FRMM8PEiWXYax7rpS6X4XZX1aAAxSWx1CrKTyrVYhV24fg";
const storageKey = api.query.system.account.key(account);
const accountData = await api.rpc.state.getStorage(storageKey);
if (accountData) {
const decoded = api.createType('AccountInfo', accountData);
console.log(`Balance: ${decoded.data.free.toHuman()}`);
}
// Query at specific block
const blockHash = await api.rpc.chain.getBlockHash(1000000);
const historicalData = await api.rpc.state.getStorage(storageKey, blockHash);import { ApiPromise } from "@polkadot/api";
const api = await ApiPromise.create();
// Subscribe to storage changes
const account = "1FRMM8PEiWXYax7rpS6X4XZX1aAAxSWx1CrKTyrVYhV24fg";
const storageKey = api.query.system.account.key(account);
const unsubscribe = await api.rpc.state.subscribeStorage([storageKey], (changes) => {
console.log(`Block: ${changes.block}`);
changes.changes.forEach(([key, value]) => {
if (value) {
const decoded = api.createType('AccountInfo', value);
console.log(`Account balance changed: ${decoded.data.free.toHuman()}`);
}
});
});import { ApiPromise } from "@polkadot/api";
import { Keyring } from "@polkadot/keyring";
const api = await ApiPromise.create();
const keyring = new Keyring({ type: 'sr25519' });
// Create account from seed
const alice = keyring.addFromUri('//Alice');
// Create and sign transaction
const transfer = api.tx.balances.transferAllowDeath(
'1FRMM8PEiWXYax7rpS6X4XZX1aAAxSWx1CrKTyrVYhV24fg',
1000000000000
);
const signedTx = await transfer.signAsync(alice);
// Submit transaction
const txHash = await api.rpc.author.submitExtrinsic(signedTx);
console.log(`Transaction submitted: ${txHash}`);
// Submit and watch transaction
const unsubscribe = await api.rpc.author.submitAndWatchExtrinsic(signedTx, (status) => {
console.log(`Transaction status: ${status.type}`);
if (status.isInBlock) {
console.log(`Included in block: ${status.asInBlock}`);
} else if (status.isFinalized) {
console.log(`Finalized in block: ${status.asFinalized}`);
unsubscribe();
} else if (status.isError) {
console.log('Transaction error');
unsubscribe();
}
});import { ApiPromise } from "@polkadot/api";
const api = await ApiPromise.create();
// Get runtime metadata
const metadata = await api.rpc.state.getMetadata();
console.log(`Metadata version: ${metadata.version}`);
// Get runtime version
const runtimeVersion = await api.rpc.state.getRuntimeVersion();
console.log(`Runtime: ${runtimeVersion.specName} v${runtimeVersion.specVersion}`);
// Call custom runtime API
const method = 'TransactionPaymentApi_query_info';
const tx = api.tx.balances.transferAllowDeath('1FRMM8PEiWXYax7rpS6X4XZX1aAAxSWx1CrKTyrVYhV24fg', 1000);
const callData = api.createType('(Bytes, u32)', [tx.toHex(), tx.encodedLength]).toHex();
const result = await api.rpc.state.call(method, callData);
const feeInfo = api.createType('RuntimeDispatchInfo', result);
console.log(`Transaction fee: ${feeInfo.partialFee.toHuman()}`);import { ApiPromise } from "@polkadot/api";
const api = await ApiPromise.create();
// Get node health
const health = await api.rpc.system.health();
console.log(`Peers: ${health.peers}, Syncing: ${health.isSyncing}`);
// Get connected peers
const peers = await api.rpc.system.peers();
console.log(`Connected to ${peers.length} peers:`);
peers.forEach(peer => {
console.log(` ${peer.peerId}: ${peer.roles.toString()}`);
});
// Get chain properties
const properties = await api.rpc.system.properties();
console.log(`SS58 Format: ${properties.ss58Format?.toNumber()}`);
console.log(`Token symbol: ${properties.tokenSymbol?.toString()}`);
console.log(`Token decimals: ${properties.tokenDecimals?.toNumber()}`);import { ApiRx } from "@polkadot/api";
import { switchMap, tap } from "rxjs";
const api$ = ApiRx.create();
// Chain RxJS operations
api$.pipe(
switchMap(api => api.rpc.chain.subscribeNewHeads()),
tap(header => console.log(`Block #${header.number}`)),
switchMap(header => api$.pipe(
switchMap(api => api.rpc.chain.getBlock(header.hash))
))
).subscribe(block => {
console.log(`Block has ${block.block.extrinsics.length} extrinsics`);
});
// Combine multiple RPC streams
import { combineLatest } from "rxjs";
api$.pipe(
switchMap(api =>
combineLatest([
api.rpc.chain.subscribeNewHeads(),
api.rpc.system.health()
])
)
).subscribe(([header, health]) => {
console.log(`Block #${header.number}, Peers: ${health.peers}`);
});Install with Tessl CLI
npx tessl i tessl/npm-polkadot--api