UNPKG

@polkadot/api

Version:

Promise and RxJS wrappers around the Polkadot JS RPC

192 lines (191 loc) 6.52 kB
import { noop, objectSpread } from '@polkadot/util'; import { ApiBase } from '../base/index.js'; import { Combinator } from './Combinator.js'; import { promiseTracker, toPromiseMethod } from './decorateMethod.js'; /** * # @polkadot/api/promise * * ## Overview * * @name ApiPromise * @description * ApiPromise is a standard JavaScript wrapper around the RPC and interfaces on the Polkadot network. As a full Promise-based, all interface calls return Promises, including the static `.create(...)`. Subscription calls utilise `(value) => {}` callbacks to pass through the latest values. * * The API is well suited to real-time applications where either the single-shot state is needed or use is to be made of the subscription-based features of Polkadot (and Substrate) clients. * * @see [[ApiRx]] * * ## Usage * * Making rpc calls - * <BR> * * ```javascript * import ApiPromise from '@polkadot/api/promise'; * * // initialise via static create * const api = await ApiPromise.create(); * * // make a subscription to the network head * api.rpc.chain.subscribeNewHeads((header) => { * console.log(`Chain is at #${header.number}`); * }); * ``` * <BR> * * Subscribing to chain state - * <BR> * * ```javascript * import { ApiPromise, WsProvider } from '@polkadot/api'; * * // initialise a provider with a specific endpoint * const provider = new WsProvider('wss://example.com:9944') * * // initialise via isReady & new with specific provider * const api = await new ApiPromise({ provider }).isReady; * * // retrieve the block target time * const blockPeriod = await api.query.timestamp.blockPeriod().toNumber(); * let last = 0; * * // subscribe to the current block timestamp, updates automatically (callback provided) * api.query.timestamp.now((timestamp) => { * const elapsed = last * ? `, ${timestamp.toNumber() - last}s since last` * : ''; * * last = timestamp.toNumber(); * console.log(`timestamp ${timestamp}${elapsed} (${blockPeriod}s target)`); * }); * ``` * <BR> * * Submitting a transaction - * <BR> * * ```javascript * import ApiPromise from '@polkadot/api/promise'; * * ApiPromise.create().then((api) => { * const [nonce] = await api.query.system.account(keyring.alice.address); * * api.tx.balances * // create transfer * transfer(keyring.bob.address, 12345) * // sign the transcation * .sign(keyring.alice, { nonce }) * // send the transaction (optional status callback) * .send((status) => { * console.log(`current status ${status.type}`); * }) * // retrieve the submitted extrinsic hash * .then((hash) => { * console.log(`submitted with hash ${hash}`); * }); * }); * ``` */ export class ApiPromise extends ApiBase { __internal__isReadyPromise; __internal__isReadyOrErrorPromise; /** * @description Creates an instance of the ApiPromise class * @param options Options to create an instance. This can be either [[ApiOptions]] or * an [[WsProvider]]. * @example * <BR> * * ```javascript * import Api from '@polkadot/api/promise'; * * new Api().isReady.then((api) => { * api.rpc.subscribeNewHeads((header) => { * console.log(`new block #${header.number.toNumber()}`); * }); * }); * ``` */ constructor(options) { super(options, 'promise', toPromiseMethod); this.__internal__isReadyPromise = new Promise((resolve) => { super.once('ready', () => resolve(this)); }); this.__internal__isReadyOrErrorPromise = new Promise((resolve, reject) => { const tracker = promiseTracker(resolve, reject); super.once('ready', () => tracker.resolve(this)); super.once('error', (error) => tracker.reject(error)); }); } /** * @description Creates an ApiPromise instance using the supplied provider. Returns an Promise containing the actual Api instance. * @param options options that is passed to the class contructor. Can be either [[ApiOptions]] or a * provider (see the constructor arguments) * @example * <BR> * * ```javascript * import Api from '@polkadot/api/promise'; * * Api.create().then(async (api) => { * const timestamp = await api.query.timestamp.now(); * * console.log(`lastest block timestamp ${timestamp}`); * }); * ``` */ static create(options) { const instance = new ApiPromise(options); if (options && options.throwOnConnect) { return instance.isReadyOrError; } // Swallow any rejections on isReadyOrError // (in Node 15.x this creates issues, when not being looked at) instance.isReadyOrError.catch(noop); return instance.isReady; } /** * @description Promise that resolves the first time we are connected and loaded */ get isReady() { return this.__internal__isReadyPromise; } /** * @description Promise that resolves if we can connect, or reject if there is an error */ get isReadyOrError() { return this.__internal__isReadyOrErrorPromise; } /** * @description Returns a clone of this ApiPromise instance (new underlying provider connection) */ clone() { return new ApiPromise(objectSpread({}, this._options, { source: this })); } /** * @description Creates a combinator that can be used to combine the latest results from multiple subscriptions * @param fns An array of function to combine, each in the form of `(cb: (value: void)) => void` * @param callback A callback that will return an Array of all the values this combinator has been applied to * @example * <BR> * * ```javascript * const address = '5DTestUPts3kjeXSTMyerHihn1uwMfLj8vU8sqF7qYrFacT7'; * * // combines values from balance & nonce as it updates * api.combineLatest([ * api.rpc.chain.subscribeNewHeads, * (cb) => api.query.system.account(address, cb) * ], ([head, [balance, nonce]]) => { * console.log(`#${head.number}: You have ${balance.free} units, with ${nonce} transactions sent`); * }); * ``` */ // eslint-disable-next-line @typescript-eslint/require-await async combineLatest(fns, callback) { const combinator = new Combinator(fns, callback); return () => { combinator.unsubscribe(); }; } }