Pull in vuepress foir docs (as opposed to gitbook) (#742)

* Pull in latest @polkadot/dev, cleanup existing *.html

* Cleanups (old gitbook interfaces)

* Bump, WIP

* Ready for testing

* Update examples

* Include source

* Cleanup links

* Adjust links

* Add typedoc exclusions

* Default classes from class files

* Adjust imports

* Adjust config

* Re-add types split

* Update links to substrate interfaces

* Anchors for metadata

* Adjust links

* Restore HTML (to be deleted on CI)

* Bump deps

* Re-gen yarn.lock

* Remove a in links

* Adjust interfaces
This commit is contained in:
Jaco Greeff
2019-02-26 15:21:39 +01:00
committed by GitHub
parent 879fa28d0d
commit b313614935
104 changed files with 5510 additions and 7085 deletions
+4
View File
@@ -1,3 +1,7 @@
# @polkadot/api-derive
Common functions used across Polkadot, derived from RPC calls and storage queries.
## Classes
[Classes](SUMMARY.md)
+1 -1
View File
@@ -3,7 +3,7 @@
// of the Apache-2.0 license. See the LICENSE file for details.
import { Observable, from } from 'rxjs';
import ApiRx from '@polkadot/api/rx';
import ApiRx from '@polkadot/api/rx/Api';
import { ApiInterface$Rx } from '@polkadot/api/types';
import MockProvider from '@polkadot/rpc-provider/mock';
+1 -1
View File
@@ -3,7 +3,7 @@
// of the Apache-2.0 license. See the LICENSE file for details.
import BN from 'bn.js';
import ApiRx from '@polkadot/api/rx';
import ApiRx from '@polkadot/api/rx/Api';
import { ApiInterface$Rx } from '@polkadot/api/types';
import { BlockNumber } from '@polkadot/types/index';
import { WsProvider } from '@polkadot/rpc-provider/index';
+5
View File
@@ -91,3 +91,8 @@ Some of the users of the API (let us know if you are missing from the list), inc
- [Polkadot-JS UI](https://github.com/polkadot-js/apps) A user-interface that allows you to make transactions, query the network or participate in actions on the network such as referendums and staking
- [Polkabot](https://gitlab.com/Polkabot) Polkabot is a Matrix chatbot that keeps an eye on the Polkadot network. You can see Polkabot in action in https://matrix.to/#/#polkadot-network-status:matrix.org
- [Polkawallet.io](https://polkawallet.io) and [Polkawallet (Github)](https://github.com/polkawallet-io/polkawallet-RN/) A mobile wallet for the Polkadot network to manage funds and make transactions, available on both Androind and iOS
## Classes
[Classes](SUMMARY.md)
+1 -1
View File
@@ -36,6 +36,6 @@
"@polkadot/rpc-rx": "^0.45.14",
"@polkadot/storage": "^0.45.14",
"@polkadot/types": "^0.45.14",
"@polkadot/util-crypto": "^0.34.24"
"@polkadot/util-crypto": "^0.34.27"
}
}
+241
View File
@@ -0,0 +1,241 @@
// Copyright 2017-2019 @polkadot/api authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { ProviderInterface } from '@polkadot/rpc-provider/types';
import { CodecArg, CodecCallback } from '@polkadot/types/types';
import { RxResult } from '../rx/types';
import { ApiOptions, OnCallFunction } from '../types';
import { ApiPromiseInterface, CodecResult, SubscriptionResult } from './types';
import { EMPTY } from 'rxjs';
import { catchError, first, tap } from 'rxjs/operators';
import { isFunction, assert } from '@polkadot/util';
import ApiBase from '../Base';
import Combinator, { CombinatorCallback, CombinatorFunction } from './Combinator';
/**
* # @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.subscribeNewHead((header) => {
* console.log(`Chain is at #${header.blockNumber}`);
* });
* ```
* <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.accountNonce(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 default class ApiPromise extends ApiBase<CodecResult, SubscriptionResult> implements ApiPromiseInterface {
private _isReadyPromise: Promise<ApiPromise>;
/**
* @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: ApiOptions | ProviderInterface = {}): Promise<ApiPromise> {
return new ApiPromise(options).isReady;
}
/**
* @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.subscribeNewHead((header) => {
* console.log(`new block #${header.blockNumber.toNumber()}`);
* });
* });
* ```
*/
constructor (options?: ApiOptions | ProviderInterface) {
super(options, 'promise');
this._isReadyPromise = new Promise((resolveReady) =>
super.once('ready', () =>
resolveReady(this)
)
);
}
/**
* @description Promise that returns the first time we are connected and loaded
*/
get isReady (): Promise<ApiPromise> {
return this._isReadyPromise;
}
/**
* @description Returns a clone of this ApiPromise instance (new underlying provider connection)
*/
clone (): ApiPromise {
return new ApiPromise({
...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.subscribeNewHead,
* [api.query.balances.freeBalance, address],
* (cb) => api.query.system.accountNonce(address, cb)
* ], ([head, balance, nonce]) => {
* console.log(`#${head.number}: You have ${balance} units, with ${nonce} transactions sent`);
* });
* ```
*/
async combineLatest (fns: Array<CombinatorFunction | [CombinatorFunction, ...Array<any>]>, callback: CombinatorCallback): SubscriptionResult {
const combinator = new Combinator(fns, callback);
return (): void => {
combinator.unsubscribe();
};
}
protected onCall (method: OnCallFunction<RxResult, RxResult>, params: Array<CodecArg> = [], callback?: CodecCallback, needsCallback?: boolean): CodecResult | SubscriptionResult {
// When we need a subscription, ensure that a valid callback is actually passed
assert(!needsCallback || isFunction(callback), 'Expected a callback to be passed with subscriptions');
if (!callback) {
return method(...params).pipe(first()).toPromise();
}
// FIXME TSLint shouts that type assertion is unnecessary, but tsc shouts
// when I remove it...
// tslint:disable-next-line
return new Promise((resolve, reject) => {
let isCompleted = false;
const subscription = method(...params)
.pipe(
// if we find an error (invalid params, etc), reject the promise
catchError((error) => {
if (!isCompleted) {
isCompleted = true;
reject(error);
}
// we don't want to continue, so empty observable it is
return EMPTY;
}),
// upon the first result, resolve the with the unsub function
tap(() => {
if (!isCompleted) {
isCompleted = true;
resolve(() => subscription.unsubscribe());
}
})
)
.subscribe(callback);
}) as SubscriptionResult;
}
}
+2 -238
View File
@@ -2,242 +2,6 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { ProviderInterface } from '@polkadot/rpc-provider/types';
import { CodecArg, CodecCallback } from '@polkadot/types/types';
import { RxResult } from '../rx/types';
import { ApiOptions, OnCallFunction } from '../types';
import { ApiPromiseInterface, CodecResult, SubscriptionResult } from './types';
import { default as ApiPromise } from './Api';
import { EMPTY } from 'rxjs';
import { catchError, first, tap } from 'rxjs/operators';
import { isFunction, assert } from '@polkadot/util';
import ApiBase from '../Base';
import Combinator, { CombinatorCallback, CombinatorFunction } from './Combinator';
/**
* # @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.subscribeNewHead((header) => {
* console.log(`Chain is at #${header.blockNumber}`);
* });
* ```
* <BR>
*
* Subscribing to chain state -
* <BR>
*
* ```javascript
* import { ApiPromise } from '@polkadot/api';
* import WsProvider from '@polkadot/rpc-provider/ws';
*
* // 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.accountNonce(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 default class ApiPromise extends ApiBase<CodecResult, SubscriptionResult> implements ApiPromiseInterface {
private _isReadyPromise: Promise<ApiPromise>;
/**
* @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: ApiOptions | ProviderInterface = {}): Promise<ApiPromise> {
return new ApiPromise(options).isReady;
}
/**
* @description Creates an instance of the ApiPromise class
*
* @param options Options to create an instance. This can be either [[ApiOptions]] or
* an [[HttpProvider]] or [[WsProvider]]. In the case of [[HttpProvider]] subscriptions
* are not supported, only latest values are returned.
*
* @example
* <BR>
*
* ```javascript
* import Api from '@polkadot/api/promise';
*
* new Api().isReady.then((api) => {
* api.rpc.subscribeNewHead((header) => {
* console.log(`new block #${header.blockNumber.toNumber()}`);
* });
* });
* ```
*/
constructor (options?: ApiOptions | ProviderInterface) {
super(options, 'promise');
this._isReadyPromise = new Promise((resolveReady) =>
super.once('ready', () =>
resolveReady(this)
)
);
}
/**
* @description Promise that returns the first time we are connected and loaded
*/
get isReady (): Promise<ApiPromise> {
return this._isReadyPromise;
}
/**
* @description Returns a clone of this ApiPromise instance (new underlying provider connection)
*/
clone (): ApiPromise {
return new ApiPromise({
...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.subscribeNewHead,
* [api.query.balances.freeBalance, address],
* (cb) => api.query.system.accountNonce(address, cb)
* ], ([head, balance, nonce]) => {
* console.log(`#${head.number}: You have ${balance} units, with ${nonce} transactions sent`);
* });
* ```
*/
async combineLatest (fns: Array<CombinatorFunction | [CombinatorFunction, ...Array<any>]>, callback: CombinatorCallback): SubscriptionResult {
const combinator = new Combinator(fns, callback);
return (): void => {
combinator.unsubscribe();
};
}
protected onCall (method: OnCallFunction<RxResult, RxResult>, params: Array<CodecArg> = [], callback?: CodecCallback, needsCallback?: boolean): CodecResult | SubscriptionResult {
// When we need a subscription, ensure that a valid callback is actually passed
assert(!needsCallback || isFunction(callback), 'Expected a callback to be passed with subscriptions');
if (!callback) {
return method(...params).pipe(first()).toPromise();
}
// FIXME TSLint shouts that type assertion is unnecessary, but tsc shouts
// when I remove it...
// tslint:disable-next-line
return new Promise((resolve, reject) => {
let isCompleted = false;
const subscription = method(...params)
.pipe(
// if we find an error (invalid params, etc), reject the promise
catchError((error) => {
if (!isCompleted) {
isCompleted = true;
reject(error);
}
// we don't want to continue, so empty observable it is
return EMPTY;
}),
// upon the first result, resolve the with the unsub function
tap(() => {
if (!isCompleted) {
isCompleted = true;
resolve(() => subscription.unsubscribe());
}
})
)
.subscribe(callback);
}) as SubscriptionResult;
}
}
export default ApiPromise;
+205
View File
@@ -0,0 +1,205 @@
// Copyright 2017-2019 @polkadot/api authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { ProviderInterface } from '@polkadot/rpc-provider/types';
import { CodecArg } from '@polkadot/types/types';
import { ApiRxInterface, RxResult } from './types';
import { ApiOptions, OnCallFunction } from '../types';
import { Observable, from } from 'rxjs';
import ApiBase from '../Base';
/**
* # @polkadot/api/rx
*
* ## Overview
*
* @name ApiRx
*
* @description
* ApiRx is a powerfull RxJS Observable wrapper around the RPC and interfaces on the Polkadot network. As a full Observable API, all interface calls return RxJS Observables, including the static `.create(...)`. In the same fashion and subscription-based methods return long-running Observables that update with the latest values.
*
* The API is well suited to real-time applications where the latest state is needed, unlocking the subscription-based features of Polkadot (and Substrate) clients. Some familiarity with RxJS is a requirement to use the API, however just understanding `.subscribe` and `.pipe` on Observables will unlock full-scale use thereof.
*
* @see [[ApiPromise]]
*
* ## Usage
*
* Making rpc calls -
* <BR>
*
* ```javascript
* import ApiRx from '@polkadot/api/rx';
*
* // initialise via Promise & static create
* const api = await ApiRx.create().toPromise();
*
* // make a call to retrieve the current network head
* api.rpc.chain.subscribeNewHead().subscribe((header) => {
* console.log(`Chain is at #${header.blockNumber}`);
* });
* ```
* <BR>
*
* Subscribing to chain state -
* <BR>
*
* ```javascript
* import { combineLatest } from 'rxjs';
* import { pairwise, switchMap } from 'rxjs/operators';
* import { ApiRx, 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
* new ApiRx(provider)
* .isReady
* .pipe(
* switchMap((api) =>
* combineLatest([
* api.query.timestamp.blockPeriod(),
* api.query.timestamp.now().pipe(pairwise())
* ])
* )
* )
* .subscribe(([blockPeriod, timestamp]) => {
* const elapsed = timestamp[1].toNumber() - timestamp[0].toNumber();
* console.log(`timestamp ${timestamp[1]} \nelapsed ${elapsed} \n(${blockPeriod}s target)`);
* });
* ```
* <BR>
*
* Submitting a transaction -
* <BR>
*
* ```javascript
* import { first, switchMap } from 'rxjs/operators';
* import ApiRx from '@polkadot/api/rx';
*
* // import the test keyring (already has dev keys for Alice, Bob, Charlie, Eve & Ferdie)
* import testingPairs from '@polkadot/keyring/testingPairs';
* const keyring = testingPairs();
*
* // get api via Promise
* const api = await ApiRx.create().toPromise();
*
* // retrieve nonce for the account
* api.query.system
* .accountNonce(keyring.alice.address())
* .pipe(
* first(),
* // pipe nonce into transfer
* switchMap((nonce) =>
* api.tx.balances
* // create transfer
* .transfer(keyring.bob.address(), 12345)
* // sign the transcation
* .sign(keyring.alice, { nonce })
* // send the transaction
* .send()
* )
* )
* // subscribe to overall result
* .subscribe(({ status, type }) => {
* if (type === 'Finalised') {
* console.log('Completed at block hash', status.asFinalised.toHex());
* }
* });
* ```
*/
export default class ApiRx extends ApiBase<RxResult, RxResult> implements ApiRxInterface {
private _isReadyRx: Observable<ApiRx>;
/**
* @description Creates an ApiRx instance using the supplied provider. Returns an Observable containing the actual Api instance.
*
* @param options options that is passed to the class contructor. Can be either [[ApiOptions]] or [[WsProvider]]
*
* @example
* <BR>
*
* ```javascript
* import { switchMap } from 'rxjs/operators';
* import Api from '@polkadot/api/rx';
*
* Api.create()
* .pipe(
* switchMap((api) =>
* api.rpc.chain.subscribeNewHead()
* ))
* .subscribe((header) => {
* console.log(`new block #${header.blockNumber.toNumber()}`);
* });
* ```
*/
static create (options?: ApiOptions | ProviderInterface): Observable<ApiRx> {
return new ApiRx(options).isReady;
}
/**
* @description Create an instance of the ApiRx class
*
* @param options Options to create an instance. Can be either [[ApiOptions]] or [[WsProvider]]
*
* @example
* <BR>
*
* ```javascript
* import { switchMap } from 'rxjs/operators';
* import Api from '@polkadot/api/rx';
*
* new Api().isReady
* .pipe(
* switchMap((api) =>
* api.rpc.chain.subscribeNewHead()
* ))
* .subscribe((header) => {
* console.log(`new block #${header.blockNumber.toNumber()}`);
* });
* ```
*/
constructor (provider?: ApiOptions | ProviderInterface) {
super(provider, 'rxjs');
this._isReadyRx = from(
// convinced you can observable from an event, however my mind groks this form better
new Promise((resolveReady) =>
super.on('ready', () =>
resolveReady(this)
)
)
) as Observable<ApiRx>;
}
/**
* @description Observable that carries the connected state for the provider. Results in a boolean flag that is true/false based on the connectivity.
*/
get isConnected (): Observable<boolean> {
return this._rpcRx.isConnected();
}
/**
* @description Observable that returns the first time we are connected and loaded
*/
get isReady (): Observable<ApiRx> {
return this._isReadyRx;
}
/**
* @description Returns a clone of this ApiRx instance (new underlying provider connection)
*/
clone (): ApiRx {
return new ApiRx({
...this._options,
source: this
});
}
protected onCall (method: OnCallFunction<RxResult, RxResult>, params: Array<CodecArg> = []): RxResult {
return method(...params);
}
}
+2 -201
View File
@@ -2,205 +2,6 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { ProviderInterface } from '@polkadot/rpc-provider/types';
import { CodecArg } from '@polkadot/types/types';
import { ApiRxInterface, RxResult } from './types';
import { ApiOptions, OnCallFunction } from '../types';
import { default as ApiRx } from './Api';
import { Observable, from } from 'rxjs';
import ApiBase from '../Base';
/**
* # @polkadot/api/rx
*
* ## Overview
*
* @name ApiRx
*
* @description
* ApiRx is a powerfull RxJS Observable wrapper around the RPC and interfaces on the Polkadot network. As a full Observable API, all interface calls return RxJS Observables, including the static `.create(...)`. In the same fashion and subscription-based methods return long-running Observables that update with the latest values.
*
* The API is well suited to real-time applications where the latest state is needed, unlocking the subscription-based features of Polkadot (and Substrate) clients. Some familiarity with RxJS is a requirement to use the API, however just understanding `.subscribe` and `.pipe` on Observables will unlock full-scale use thereof.
*
* @see [[ApiPromise]]
*
* ## Usage
*
* Making rpc calls -
* <BR>
*
* ```javascript
* import ApiRx from '@polkadot/api/rx';
*
* // initialise via Promise & static create
* const api = await ApiRx.create().toPromise();
*
* // make a call to retrieve the current network head
* api.rpc.chain.subscribeNewHead().subscribe((header) => {
* console.log(`Chain is at #${header.blockNumber}`);
* });
* ```
* <BR>
*
* Subscribing to chain state -
* <BR>
*
* ```javascript
* import { combineLatest } from 'rxjs';
* import { pairwise, switchMap } from 'rxjs/operators';
* import { ApiRx } from '@polkadot/api';
* import { WsProvider } from '@polkadot/rpc-provider';
*
*
* // initialise a provider with a specific endpoint
* const provider = new WsProvider('wss://example.com:9944')
*
* // initialise via isReady & new with specific provider
* new ApiRx(provider)
* .isReady
* .pipe(
* switchMap((api) =>
* combineLatest([
* api.query.timestamp.blockPeriod(),
* api.query.timestamp.now().pipe(pairwise())
* ])
* )
* )
* .subscribe(([blockPeriod, timestamp]) => {
* const elapsed = timestamp[1].toNumber() - timestamp[0].toNumber();
* console.log(`timestamp ${timestamp[1]} \nelapsed ${elapsed} \n(${blockPeriod}s target)`);
* });
* ```
* <BR>
*
* Submitting a transaction -
* <BR>
*
* ```javascript
* import { first, switchMap } from 'rxjs/operators';
* import ApiRx from '@polkadot/api/rx';
*
* // import the test keyring (already has dev keys for Alice, Bob, Charlie, Eve & Ferdie)
* import testingPairs from '@polkadot/keyring/testingPairs';
* const keyring = testingPairs();
*
* // get api via Promise
* const api = await ApiRx.create().toPromise();
*
* // retrieve nonce for the account
* api.query.system
* .accountNonce(keyring.alice.address())
* .pipe(
* first(),
* // pipe nonce into transfer
* switchMap((nonce) =>
* api.tx.balances
* // create transfer
* .transfer(keyring.bob.address(), 12345)
* // sign the transcation
* .sign(keyring.alice, { nonce })
* // send the transaction
* .send()
* )
* )
* // subscribe to overall result
* .subscribe(({ status, type }) => {
* if (type === 'Finalised') {
* console.log('Completed at block hash', status.asFinalised.toHex());
* }
* });
* ```
*/
export default class ApiRx extends ApiBase<RxResult, RxResult> implements ApiRxInterface {
private _isReadyRx: Observable<ApiRx>;
/**
* @description Creates an ApiRx instance using the supplied provider. Returns an Observable containing the actual Api instance.
*
* @param options options that is passed to the class contructor. Can be either [[ApiOptions]] or [[WsProvider]]
*
* @example
* <BR>
*
* ```javascript
* import { switchMap } from 'rxjs/operators';
* import Api from '@polkadot/api/rx';
*
* Api.create()
* .pipe(
* switchMap((api) =>
* api.rpc.chain.subscribeNewHead()
* ))
* .subscribe((header) => {
* console.log(`new block #${header.blockNumber.toNumber()}`);
* });
* ```
*/
static create (options?: ApiOptions | ProviderInterface): Observable<ApiRx> {
return new ApiRx(options).isReady;
}
/**
* @description Create an instance of the ApiRx class
*
* @param options Options to create an instance. Can be either [[ApiOptions]] or [[WsProvider]]
*
* @example
* <BR>
*
* ```javascript
* import { switchMap } from 'rxjs/operators';
* import Api from '@polkadot/api/rx';
*
* new Api().isReady
* .pipe(
* switchMap((api) =>
* api.rpc.chain.subscribeNewHead()
* ))
* .subscribe((header) => {
* console.log(`new block #${header.blockNumber.toNumber()}`);
* });
* ```
*/
constructor (provider?: ApiOptions | ProviderInterface) {
super(provider, 'rxjs');
this._isReadyRx = from(
// convinced you can observable from an event, however my mind groks this form better
new Promise((resolveReady) =>
super.on('ready', () =>
resolveReady(this)
)
)
) as Observable<ApiRx>;
}
/**
* @description Observable that carries the connected state for the provider. Results in a boolean flag that is true/false based on the connectivity.
*/
get isConnected (): Observable<boolean> {
return this._rpcRx.isConnected();
}
/**
* @description Observable that returns the first time we are connected and loaded
*/
get isReady (): Observable<ApiRx> {
return this._isReadyRx;
}
/**
* @description Returns a clone of this ApiRx instance (new underlying provider connection)
*/
clone (): ApiRx {
return new ApiRx({
...this._options,
source: this
});
}
protected onCall (method: OnCallFunction<RxResult, RxResult>, params: Array<CodecArg> = []): RxResult {
return method(...params);
}
}
export default ApiRx;
+4
View File
@@ -65,3 +65,7 @@ api.chain
console.error('error subscribing:', error);
});
```
## Classes
[Classes](SUMMARY.md)
+1 -1
View File
@@ -33,6 +33,6 @@
"@polkadot/jsonrpc": "^0.45.14",
"@polkadot/rpc-provider": "^0.45.14",
"@polkadot/types": "^0.45.14",
"@polkadot/util": "^0.34.24"
"@polkadot/util": "^0.34.27"
}
}
+1 -1
View File
@@ -7,7 +7,7 @@ import { RpcSection, RpcMethod } from '@polkadot/jsonrpc/types';
import { RpcInterface, RpcInterface$Method, RpcInterface$Section } from './types';
import interfaces from '@polkadot/jsonrpc/index';
import WsProvider from '@polkadot/rpc-provider/ws';
import { WsProvider } from '@polkadot/rpc-provider/index';
import { Codec } from '@polkadot/types/types';
import { Option, StorageChangeSet, StorageKey, Vector, createClass, createType } from '@polkadot/types/index';
import { ExtError, assert, isFunction, isNull, logger } from '@polkadot/util';
+4
View File
@@ -39,3 +39,7 @@ const version = await provider.send('chain_getBlockHash', []);
console.log('latest block Hash', hash);
```
## Classes
[Classes](SUMMARY.md)
+3 -3
View File
@@ -30,10 +30,10 @@
"homepage": "https://github.com/polkadot-js/api/tree/master/packages/rpc-provider#readme",
"dependencies": {
"@babel/runtime": "^7.3.1",
"@polkadot/keyring": "^0.34.24",
"@polkadot/keyring": "^0.34.27",
"@polkadot/storage": "^0.45.14",
"@polkadot/util": "^0.34.24",
"@polkadot/util-crypto": "^0.34.24",
"@polkadot/util": "^0.34.27",
"@polkadot/util-crypto": "^0.34.27",
"@types/nock": "^9.3.1",
"eventemitter3": "^3.1.0",
"isomorphic-fetch": "^2.2.1",
+388
View File
@@ -0,0 +1,388 @@
// Copyright 2017-2019 @polkadot/rpc-provider authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { JsonRpcResponse, ProviderInterface, ProviderInterface$Callback, ProviderInterface$Emitted, ProviderInterface$EmitCb } from '../types';
import './polyfill';
import EventEmitter from 'eventemitter3';
import { assert, isNull, isUndefined, logger } from '@polkadot/util';
import Coder from '../coder';
import defaults from '../defaults';
type CallbackHandler = (error?: null | Error, value?: any) => void;
type SubscriptionHandler = {
callback: CallbackHandler,
type: string
};
type WsState$Awaiting = {
callback: CallbackHandler,
method: string,
params: Array<any>,
subscription?: SubscriptionHandler
};
type WsState$Subscription = SubscriptionHandler & {
method: string,
params: Array<any>
};
interface WSProviderInterface extends ProviderInterface {
connect (): void;
}
const l = logger('api-ws');
/**
* # @polkadot/rpc-provider/ws
*
* @name WsProvider
*
* @description The WebSocket Provider allows sending requests using WebSocket to a WebSocket RPC server TCP port. Unlike the [[HttpProvider]], it does support subscriptions and allows listening to events such as new blocks or balance changes.
*
* @example
* <BR>
*
* ```javascript
* import Api from '@polkadot/api/promise';
* import WsProvider from '@polkadot/rpc-provider/ws';
*
* const provider = new WsProvider('ws://127.0.0.1:9944');
* const api = new Api(provider);
* ```
*
* @see [[HttpProvider]]
*/
export default class WsProvider implements WSProviderInterface {
private _eventemitter: EventEmitter;
private _isConnected: boolean = false;
private autoConnect: boolean;
private coder: Coder;
private endpoint: string;
private handlers: {
[index: string]: WsState$Awaiting
};
private queued: {
[index: string]: string
};
private subscriptions: {
[index: string]: WsState$Subscription
};
private websocket: WebSocket | null;
/**
* @param {string} endpoint The endpoint url. Usually `ws://ip:9944` or `wss://ip:9944`
* @param {boolean} autoConnect Whether to connect automatically or not.
*/
constructor (endpoint: string = defaults.WS_URL, autoConnect: boolean = true) {
assert(/^(wss|ws):\/\//.test(endpoint), `Endpoint should start with 'ws://', received '${endpoint}'`);
this._eventemitter = new EventEmitter();
this.autoConnect = autoConnect;
this.coder = new Coder();
this.endpoint = endpoint;
this.handlers = {};
this.queued = {};
this.subscriptions = {};
this.websocket = null;
if (autoConnect) {
this.connect();
}
}
/**
* @summary `true` when this provider supports subscriptions
*/
get hasSubscriptions (): boolean {
return true;
}
/**
* @description Returns a clone of the object
*/
clone (): WsProvider {
return new WsProvider(this.endpoint);
}
/**
* @summary Manually connect
* @description The [[WsProvider]] connects automatically by default, however if you decided otherwise, you may
* connect manually using this method.
*/
connect (): void {
try {
this.websocket = new WebSocket(this.endpoint);
this.websocket.onclose = this.onSocketClose;
this.websocket.onerror = this.onSocketError;
this.websocket.onmessage = this.onSocketMessage;
this.websocket.onopen = this.onSocketOpen;
} catch (error) {
l.error(error);
}
}
/**
* @description Manually disconnect from the connection, clearing autoconnect logic
*/
disconnect (): void {
if (isNull(this.websocket)) {
throw new Error('Cannot disconnect on a non-open websocket');
}
// switch off autoConnect, we are in manual mode now
this.autoConnect = false;
// 1000 - Normal closure; the connection successfully completed
this.websocket.close(1000);
this.websocket = null;
}
/**
* @summary Whether the node is connected or not.
* @return {boolean} true if connected
*/
isConnected (): boolean {
return this._isConnected;
}
/**
* @summary Listens on events after having subscribed using the [[subscribe]] function.
* @param {ProviderInterface$Emitted} type Event
* @param {ProviderInterface$EmitCb} sub Callback
*/
on (type: ProviderInterface$Emitted, sub: ProviderInterface$EmitCb): void {
this._eventemitter.on(type, sub);
}
/**
* @summary Send JSON data using WebSockets to configured HTTP Endpoint or queue.
* @param method The RPC methods to execute
* @param params Encoded paramaters as appliucable for the method
* @param subscription Subscription details (internally used)
*/
async send (method: string, params: Array<any>, subscription?: SubscriptionHandler): Promise<any> {
return new Promise((resolve, reject): void => {
try {
const json = this.coder.encodeJson(method, params);
const id = this.coder.getId();
const callback = (error?: Error | null, result?: any) => {
error
? reject(error)
: resolve(result);
};
l.debug(() => ['calling', method, params, json, !!subscription]);
this.handlers[id] = {
callback,
method,
params,
subscription
};
if (this.isConnected() && !isNull(this.websocket)) {
this.websocket.send(json);
} else {
this.queued[id] = json;
}
} catch (error) {
reject(error);
}
});
}
/**
* @name subscribe
* @summary Allows subscribing to a specific event.
* @param {string} type Subscription type
* @param {string} method Subscription method
* @param {Array<any>} params Parameters
* @param {ProviderInterface$Callback} callback Callback
* @return {Promise<number>} Promise resolving to the dd of the subscription you can use with [[unsubscribe]].
*
* @example
* <BR>
*
* ```javascript
* const provider = new WsProvider('ws://127.0.0.1:9944');
* const rpc = new Rpc(provider);
*
* rpc.state.subscribeStorage([[storage.balances.freeBalance, <Address>]], (_, values) => {
* console.log(values)
* }).then((subscriptionId) => {
* console.log('balance changes subscription id: ', subscriptionId)
* })
* ```
*/
async subscribe (type: string, method: string, params: Array<any>, callback: ProviderInterface$Callback): Promise<number> {
const id = await this.send(method, params, { callback, type });
return id as number;
}
/**
* @summary Allows unsubscribing to subscriptions made with [[subscribe]].
*/
async unsubscribe (type: string, method: string, id: number): Promise<boolean> {
const subscription = `${type}::${id}`;
// FIXME This now could happen with re-subscriptions. The issue is that with a re-sub
// the assigned id now does not match what the API user originally received. It has
// a slight complication in solving - since we cannot rely on the send id, but rather
// need to find the actual subscription id to map it
if (isUndefined(this.subscriptions[subscription])) {
l.debug(() => `Unable to find active subscription=${subscription}`);
return false;
}
delete this.subscriptions[subscription];
const result = await this.send(method, [id]);
return result as boolean;
}
private emit (type: ProviderInterface$Emitted, ...args: Array<any>): void {
this._eventemitter.emit(type, ...args);
}
private onSocketClose = (event: CloseEvent): void => {
if (this.autoConnect) {
l.error(`disconnected from ${this.endpoint}::${event.code}: ${event.reason}`);
}
this._isConnected = false;
this.emit('disconnected');
if (this.autoConnect) {
setTimeout(() => {
this.connect();
}, 1000);
}
}
private onSocketError = (error: Event): void => {
l.debug(() => ['socket error', error]);
this.emit('error', error);
}
private onSocketMessage = (message: MessageEvent): void => {
l.debug(() => ['received', message.data]);
const response: JsonRpcResponse = JSON.parse(message.data as string);
return isUndefined(response.method)
? this.onSocketMessageResult(response)
: this.onSocketMessageSubscribe(response);
}
private onSocketMessageResult = (response: JsonRpcResponse): void => {
l.debug(() => ['handling: response =', response, 'id =', response.id]);
const handler = this.handlers[response.id];
if (!handler) {
l.debug(() => `Unable to find handler for id=${response.id}`);
return;
}
try {
const { method, params, subscription } = handler;
const result = this.coder.decodeResponse(response);
if (subscription) {
this.subscriptions[`${subscription.type}::${result}`] = {
...subscription,
method,
params
};
}
handler.callback(null, result);
} catch (error) {
handler.callback(error, undefined);
}
delete this.handlers[response.id];
}
private onSocketMessageSubscribe = (response: JsonRpcResponse): void => {
const subscription = `${response.method}::${response.params.subscription}`;
l.debug(() => ['handling: response =', response, 'subscription =', subscription]);
const handler = this.subscriptions[subscription];
if (!handler) {
l.debug(() => `Unable to find handler for subscription=${subscription}`);
return;
}
try {
const result = this.coder.decodeResponse(response);
handler.callback(null, result);
} catch (error) {
handler.callback(error, undefined);
}
}
private onSocketOpen = (): boolean => {
assert(!isNull(this.websocket), 'WebSocket cannot be null in onOpen');
l.debug(() => ['connected to', this.endpoint]);
this._isConnected = true;
this.emit('connected');
this.sendQueue();
this.resubscribe();
return true;
}
private resubscribe (): void {
const subscriptions = this.subscriptions;
this.subscriptions = {};
Object.keys(subscriptions).forEach(async (id) => {
const { callback, method, params, type } = subscriptions[id];
// only re-create subscriptions which are not in author (only area where
// transactions are created, i.e. submissions such as 'author_submitAndWatchExtrinsic'
// are not included (and will not be re-broadcast)
if (type.startsWith('author_')) {
return;
}
try {
await this.subscribe(type, method, params, callback);
} catch (error) {
l.error(error);
}
});
}
private sendQueue (): void {
Object.keys(this.queued).forEach((id) => {
try {
// @ts-ignore we have done the websocket check in onSocketOpen, if an issue, will catch it
this.websocket.send(
this.queued[id]
);
delete this.queued[id];
} catch (error) {
l.error(error);
}
});
}
}
+2 -383
View File
@@ -2,387 +2,6 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { JsonRpcResponse, ProviderInterface, ProviderInterface$Callback, ProviderInterface$Emitted, ProviderInterface$EmitCb } from '../types';
import { default as WsProvider } from './Provider';
import './polyfill';
import EventEmitter from 'eventemitter3';
import { assert, isNull, isUndefined, logger } from '@polkadot/util';
import Coder from '../coder';
import defaults from '../defaults';
type CallbackHandler = (error?: null | Error, value?: any) => void;
type SubscriptionHandler = {
callback: CallbackHandler,
type: string
};
type WsState$Awaiting = {
callback: CallbackHandler,
method: string,
params: Array<any>,
subscription?: SubscriptionHandler
};
type WsState$Subscription = SubscriptionHandler & {
method: string,
params: Array<any>
};
interface WSProviderInterface extends ProviderInterface {
connect (): void;
}
const l = logger('api-ws');
/**
* # @polkadot/rpc-provider/ws
*
* @name WsProvider
*
* @description The WebSocket Provider allows sending requests using WebSocket to a WebSocket RPC server TCP port. Unlike the [[HttpProvider]], it does support subscriptions and allows listening to events such as new blocks or balance changes.
*
* @example
* <BR>
*
* ```javascript
* import Api from '@polkadot/api/promise';
* import WsProvider from '@polkadot/rpc-provider/ws';
*
* const provider = new WsProvider('ws://127.0.0.1:9944');
* const api = new Api(provider);
* ```
*
* @see [[HttpProvider]]
*/
export default class WsProvider implements WSProviderInterface {
private _eventemitter: EventEmitter;
private _isConnected: boolean = false;
private autoConnect: boolean;
private coder: Coder;
private endpoint: string;
private handlers: {
[index: string]: WsState$Awaiting
};
private queued: {
[index: string]: string
};
private subscriptions: {
[index: string]: WsState$Subscription
};
private websocket: WebSocket | null;
/**
* @param {string} endpoint The endpoint url. Usually `ws://ip:9944` or `wss://ip:9944`
* @param {boolean} autoConnect Whether to connect automatically or not.
*/
constructor (endpoint: string = defaults.WS_URL, autoConnect: boolean = true) {
assert(/^(wss|ws):\/\//.test(endpoint), `Endpoint should start with 'ws://', received '${endpoint}'`);
this._eventemitter = new EventEmitter();
this.autoConnect = autoConnect;
this.coder = new Coder();
this.endpoint = endpoint;
this.handlers = {};
this.queued = {};
this.subscriptions = {};
this.websocket = null;
if (autoConnect) {
this.connect();
}
}
/**
* @summary `true` when this provider supports subscriptions
*/
get hasSubscriptions (): boolean {
return true;
}
/**
* @description Returns a clone of the object
*/
clone (): WsProvider {
return new WsProvider(this.endpoint);
}
/**
* @summary Manually connect
* @description The [[WsProvider]] connects automatically by default, however if you decided otherwise, you may
* connect manually using this method.
*/
connect (): void {
try {
this.websocket = new WebSocket(this.endpoint);
this.websocket.onclose = this.onSocketClose;
this.websocket.onerror = this.onSocketError;
this.websocket.onmessage = this.onSocketMessage;
this.websocket.onopen = this.onSocketOpen;
} catch (error) {
l.error(error);
}
}
/**
* @description Manually disconnect from the connection, clearing autoconnect logic
*/
disconnect (): void {
if (isNull(this.websocket)) {
throw new Error('Cannot disconnect on a non-open websocket');
}
// switch off autoConnect, we are in manual mode now
this.autoConnect = false;
// 1000 - Normal closure; the connection successfully completed
this.websocket.close(1000);
this.websocket = null;
}
/**
* @summary Whether the node is connected or not.
* @return {boolean} true if connected
*/
isConnected (): boolean {
return this._isConnected;
}
/**
* @summary Listens on events after having subscribed using the [[subscribe]] function.
* @param {ProviderInterface$Emitted} type Event
* @param {ProviderInterface$EmitCb} sub Callback
*/
on (type: ProviderInterface$Emitted, sub: ProviderInterface$EmitCb): void {
this._eventemitter.on(type, sub);
}
/**
* @summary Send JSON data using WebSockets to configured HTTP Endpoint or queue.
* @param method The RPC methods to execute
* @param params Encoded paramaters as appliucable for the method
* @param subscription Subscription details (internally used)
*/
async send (method: string, params: Array<any>, subscription?: SubscriptionHandler): Promise<any> {
return new Promise((resolve, reject): void => {
try {
const json = this.coder.encodeJson(method, params);
const id = this.coder.getId();
const callback = (error?: Error | null, result?: any) => {
error
? reject(error)
: resolve(result);
};
l.debug(() => ['calling', method, params, json, !!subscription]);
this.handlers[id] = {
callback,
method,
params,
subscription
};
if (this.isConnected() && !isNull(this.websocket)) {
this.websocket.send(json);
} else {
this.queued[id] = json;
}
} catch (error) {
reject(error);
}
});
}
/**
* @name subscribe
* @summary Allows subscribing to a specific event.
* @param {string} type Subscription type
* @param {string} method Subscription method
* @param {Array<any>} params Parameters
* @param {ProviderInterface$Callback} callback Callback
* @return {Promise<number>} Promise resolving to the dd of the subscription you can use with [[unsubscribe]].
*
* @example
* <BR>
*
* ```javascript
* const provider = new WsProvider('ws://127.0.0.1:9944');
* const rpc = new Rpc(provider);
*
* rpc.state.subscribeStorage([[storage.balances.freeBalance, <Address>]], (_, values) => {
* console.log(values)
* }).then((subscriptionId) => {
* console.log('balance changes subscription id: ', subscriptionId)
* })
* ```
*/
async subscribe (type: string, method: string, params: Array<any>, callback: ProviderInterface$Callback): Promise<number> {
const id = await this.send(method, params, { callback, type });
return id as number;
}
/**
* @summary Allows unsubscribing to subscriptions made with [[subscribe]].
*/
async unsubscribe (type: string, method: string, id: number): Promise<boolean> {
const subscription = `${type}::${id}`;
// FIXME This now could happen with re-subscriptions. The issue is that with a re-sub
// the assigned id now does not match what the API user originally received. It has
// a slight complication in solving - since we cannot rely on the send id, but rather
// need to find the actual subscription id to map it
if (isUndefined(this.subscriptions[subscription])) {
l.debug(() => `Unable to find active subscription=${subscription}`);
return false;
}
delete this.subscriptions[subscription];
const result = await this.send(method, [id]);
return result as boolean;
}
private emit (type: ProviderInterface$Emitted, ...args: Array<any>): void {
this._eventemitter.emit(type, ...args);
}
private onSocketClose = (event: CloseEvent): void => {
if (this.autoConnect) {
l.error(`disconnected from ${this.endpoint}::${event.code}: ${event.reason}`);
}
this._isConnected = false;
this.emit('disconnected');
if (this.autoConnect) {
setTimeout(() => {
this.connect();
}, 1000);
}
}
private onSocketError = (error: Event): void => {
l.debug(() => ['socket error', error]);
this.emit('error', error);
}
private onSocketMessage = (message: MessageEvent): void => {
l.debug(() => ['received', message.data]);
const response: JsonRpcResponse = JSON.parse(message.data as string);
return isUndefined(response.method)
? this.onSocketMessageResult(response)
: this.onSocketMessageSubscribe(response);
}
private onSocketMessageResult = (response: JsonRpcResponse): void => {
l.debug(() => ['handling: response =', response, 'id =', response.id]);
const handler = this.handlers[response.id];
if (!handler) {
l.debug(() => `Unable to find handler for id=${response.id}`);
return;
}
try {
const { method, params, subscription } = handler;
const result = this.coder.decodeResponse(response);
if (subscription) {
this.subscriptions[`${subscription.type}::${result}`] = {
...subscription,
method,
params
};
}
handler.callback(null, result);
} catch (error) {
handler.callback(error, undefined);
}
delete this.handlers[response.id];
}
private onSocketMessageSubscribe = (response: JsonRpcResponse): void => {
const subscription = `${response.method}::${response.params.subscription}`;
l.debug(() => ['handling: response =', response, 'subscription =', subscription]);
const handler = this.subscriptions[subscription];
if (!handler) {
l.debug(() => `Unable to find handler for subscription=${subscription}`);
return;
}
try {
const result = this.coder.decodeResponse(response);
handler.callback(null, result);
} catch (error) {
handler.callback(error, undefined);
}
}
private onSocketOpen = (): boolean => {
assert(!isNull(this.websocket), 'WebSocket cannot be null in onOpen');
l.debug(() => ['connected to', this.endpoint]);
this._isConnected = true;
this.emit('connected');
this.sendQueue();
this.resubscribe();
return true;
}
private resubscribe (): void {
const subscriptions = this.subscriptions;
this.subscriptions = {};
Object.keys(subscriptions).forEach(async (id) => {
const { callback, method, params, type } = subscriptions[id];
// only re-create subscriptions which are not in author (only area where
// transactions are created, i.e. submissions such as 'author_submitAndWatchExtrinsic'
// are not included (and will not be re-broadcast)
if (type.startsWith('author_')) {
return;
}
try {
await this.subscribe(type, method, params, callback);
} catch (error) {
l.error(error);
}
});
}
private sendQueue (): void {
Object.keys(this.queued).forEach((id) => {
try {
// @ts-ignore we have done the websocket check in onSocketOpen, if an issue, will catch it
this.websocket.send(
this.queued[id]
);
delete this.queued[id];
} catch (error) {
l.error(error);
}
});
}
}
export default WsProvider;
+4
View File
@@ -1,3 +1,7 @@
# @polkadot/rpc-rx
An RxJs wrapper around the [@polkadot/rpc-core](../rpc-core).
## Classes
[Classes](SUMMARY.md)
+5 -1
View File
@@ -1,3 +1,7 @@
# @polkadot/extrinsics
Base extrinsic definitions & codecs
Base extrinsic definitions & codecs
## Classes
[Classes](SUMMARY.md)
+1 -1
View File
@@ -12,6 +12,6 @@
"dependencies": {
"@babel/runtime": "^7.3.1",
"@polkadot/types": "^0.45.14",
"@polkadot/util": "^0.34.24"
"@polkadot/util": "^0.34.27"
}
}
+4
View File
@@ -18,3 +18,7 @@ As methods are added, simply adding the name, inputs & output will prepare it fo
- Add the method to the correct file in [src/rpc/](src/rpc/) (Input/Output types as cross-referenced from the canonical implementation and match one-to-one)
- Should a new type be required, add it to the type list, [src/types.js](src/types.js) (Required for TSLint type checking)
## Classes
[Classes](SUMMARY.md)
+4
View File
@@ -1,3 +1,7 @@
# @polkadot/storage
An base interface to the storage layer
## Classes
[Classes](SUMMARY.md)
+3 -3
View File
@@ -30,9 +30,9 @@
"homepage": "https://github.com/polkadot-js/api/tree/master/packages/type-storage#readme",
"dependencies": {
"@babel/runtime": "^7.3.1",
"@polkadot/keyring": "^0.34.24",
"@polkadot/keyring": "^0.34.27",
"@polkadot/types": "^0.45.14",
"@polkadot/util": "^0.34.24",
"@polkadot/util-crypto": "^0.34.24"
"@polkadot/util": "^0.34.27",
"@polkadot/util-crypto": "^0.34.27"
}
}
+2 -2
View File
@@ -30,8 +30,8 @@
"homepage": "https://github.com/polkadot-js/api/tree/master/packages/types#readme",
"dependencies": {
"@babel/runtime": "^7.3.1",
"@polkadot/keyring": "^0.34.24",
"@polkadot/util": "^0.34.24",
"@polkadot/keyring": "^0.34.27",
"@polkadot/util": "^0.34.27",
"core-js": "^2.6.5"
}
}
+1 -1
View File
@@ -7,7 +7,7 @@ import extrinsicsFromMeta from '@polkadot/extrinsics/fromMetadata';
import createType from '../codec/createType';
import Method from '../primitive/Method';
import Metadata from './index';
import Metadata from './Metadata';
import latestParsed from './v1/latest.substrate.v1.json';
import rpcData from './static';
+44
View File
@@ -0,0 +1,44 @@
// Copyright 2017-2019 @polkadot/types authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { isHex, hexToU8a, u8aConcat } from '@polkadot/util';
import { MAGIC_ERROR, MAGIC_NUMBER } from './MagicNumber';
import MetadataVersioned from './MetadataVersioned';
/**
* @name Metadata
* @description
* The versioned runtime metadata as a decoded structure
*/
export default class Metadata extends MetadataVersioned {
constructor (value?: Uint8Array | string) {
super(Metadata.decodeMetadata(value));
}
// first we try and parse using the versioned structure, if this does fail,
// we adjust with the magic number and a manual version and re-try. As soon as
// we remove support for V0, we will just do a new here
private static decodeMetadata (_value: Uint8Array | string = new Uint8Array()): MetadataVersioned {
const value = isHex(_value)
? hexToU8a(_value)
: _value;
try {
return new MetadataVersioned(value);
} catch (error) {
if (error.code !== MAGIC_ERROR) {
throw error;
}
}
return new MetadataVersioned(
u8aConcat(
MAGIC_NUMBER.toU8a(), // manually add the magic number
Uint8Array.from([0]), // add the version for the original
value // the actual data as retrieved
)
);
}
}
+2 -39
View File
@@ -2,43 +2,6 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { isHex, hexToU8a, u8aConcat } from '@polkadot/util';
import { default as Metadata } from './Metadata';
import { MAGIC_ERROR, MAGIC_NUMBER } from './MagicNumber';
import MetadataVersioned from './MetadataVersioned';
/**
* @name Metadata
* @description
* The versioned runtime metadata as a decoded structure
*/
export default class Metadata extends MetadataVersioned {
constructor (value?: Uint8Array | string) {
super(Metadata.decodeMetadata(value));
}
// first we try and parse using the versioned structure, if this does fail,
// we adjust with the magic number and a manual version and re-try. As soon as
// we remove support for V0, we will just do a new here
private static decodeMetadata (_value: Uint8Array | string = new Uint8Array()): MetadataVersioned {
const value = isHex(_value)
? hexToU8a(_value)
: _value;
try {
return new MetadataVersioned(value);
} catch (error) {
if (error.code !== MAGIC_ERROR) {
throw error;
}
}
return new MetadataVersioned(
u8aConcat(
MAGIC_NUMBER.toU8a(), // manually add the magic number
Uint8Array.from([0]), // add the version for the original
value // the actual data as retrieved
)
);
}
}
export default Metadata;
@@ -7,7 +7,7 @@ import extrinsicsFromMeta from '@polkadot/extrinsics/fromMetadata';
import createType from '../../codec/createType';
import Method from '../../primitive/Method';
import Metadata from './index';
import Metadata from './Metadata';
import latestParsed from './latest.substrate.json';
import rpcData from './static';
+116
View File
@@ -0,0 +1,116 @@
// Copyright 2017-2019 @polkadot/types authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { MetadataInterface } from '../types';
import { hexToU8a, isHex, isU8a } from '@polkadot/util';
import Compact from '../../codec/Compact';
import Struct from '../../codec/Struct';
import Vector from '../../codec/Vector';
import { flattenUniq, validateTypes } from '../util';
import { OuterDispatchMetadata, OuterDispatchCall } from './Calls';
import { OuterEventMetadata, OuterEventMetadataEvent } from './Events';
import { RuntimeModuleMetadata } from './Modules';
// Decodes the runtime metadata as passed through from the `state_getMetadata` call. This
// file is probably best understood from the bottom-up, i.e. start reading right at the
// end and work up. (Just so we don't use before definition)
/**
* @name MetadataV0
* @description
* The runtime metadata as a decoded structure
*/
export default class MetadataV0 extends Struct implements MetadataInterface {
constructor (value?: any) {
super({
outerEvent: OuterEventMetadata,
modules: Vector.with(RuntimeModuleMetadata),
outerDispatch: OuterDispatchMetadata
}, MetadataV0.decodeMetadata(value));
}
static decodeMetadata (value: string | Uint8Array | object): object | Uint8Array {
if (isHex(value)) {
// We receive this as an hex in the JSON output from the Node.
// Convert to u8a and use the U8a version to do the actual parsing.
return MetadataV0.decodeMetadata(hexToU8a(value));
} else if (isU8a(value)) {
// HACK 13 Oct 2018 - For current running BBQ nodes, Metadata is not properly
// encoded, it does not have a length prefix. For latest substrate master, it
// is properly encoded. Here we pull the prefix, check it agianst the length -
// if matches, then we have the length, otherwise we assume it is an older node
// and use the whole buffer
const [offset, length] = Compact.decodeU8a(value);
return value.length === (offset + length.toNumber())
? value.subarray(offset)
: value;
}
// Decode as normal struct
return value;
}
/**
* @description Wrapped [[OuterDispatchCall]]
*/
get calls (): Vector<OuterDispatchCall> {
return (this.get('outerDispatch') as OuterDispatchMetadata).calls;
}
/**
* @description Wrapped [[OuterEventMetadataEvent]]
*/
get events (): Vector<OuterEventMetadataEvent> {
return (this.get('outerEvent') as OuterEventMetadata).events;
}
/**
* @description Wrapped [[RuntimeModuleMetadata]]
*/
get modules (): Vector<RuntimeModuleMetadata> {
return this.get('modules') as Vector<RuntimeModuleMetadata>;
}
private get argNames () {
return this.modules.map((module) =>
module.module.call.functions.map((fn) =>
fn.arguments.map((argument) => argument.type.toString())
)
);
}
private get eventNames () {
return this.events.map((module) =>
module.events.map((event) =>
event.arguments.map((argument) => argument.toString())
)
);
}
private get storageNames () {
return this.modules.map((module) =>
module.storage.isSome
? module.storage.unwrap().functions.map((fn) =>
fn.type.isMap
? [fn.type.asMap.key.toString(), fn.type.asMap.value.toString()]
: [fn.type.asType.toString()]
)
: []
);
}
/**
* @description Helper to retrieve a list of all type that are found, sorted and de-deuplicated
*/
getUniqTypes (throwError: boolean): Array<string> {
const types = flattenUniq([this.argNames, this.eventNames, this.storageNames]);
validateTypes(types, throwError);
return types;
}
}
+2 -111
View File
@@ -2,115 +2,6 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { MetadataInterface } from '../types';
import { default as MetadataV0 } from './Metadata';
import { hexToU8a, isHex, isU8a } from '@polkadot/util';
import Compact from '../../codec/Compact';
import Struct from '../../codec/Struct';
import Vector from '../../codec/Vector';
import { flattenUniq, validateTypes } from '../util';
import { OuterDispatchMetadata, OuterDispatchCall } from './Calls';
import { OuterEventMetadata, OuterEventMetadataEvent } from './Events';
import { RuntimeModuleMetadata } from './Modules';
// Decodes the runtime metadata as passed through from the `state_getMetadata` call. This
// file is probably best understood from the bottom-up, i.e. start reading right at the
// end and work up. (Just so we don't use before definition)
/**
* @name MetadataV0
* @description
* The runtime metadata as a decoded structure
*/
export default class MetadataV0 extends Struct implements MetadataInterface {
constructor (value?: any) {
super({
outerEvent: OuterEventMetadata,
modules: Vector.with(RuntimeModuleMetadata),
outerDispatch: OuterDispatchMetadata
}, MetadataV0.decodeMetadata(value));
}
static decodeMetadata (value: string | Uint8Array | object): object | Uint8Array {
if (isHex(value)) {
// We receive this as an hex in the JSON output from the Node.
// Convert to u8a and use the U8a version to do the actual parsing.
return MetadataV0.decodeMetadata(hexToU8a(value));
} else if (isU8a(value)) {
// HACK 13 Oct 2018 - For current running BBQ nodes, Metadata is not properly
// encoded, it does not have a length prefix. For latest substrate master, it
// is properly encoded. Here we pull the prefix, check it agianst the length -
// if matches, then we have the length, otherwise we assume it is an older node
// and use the whole buffer
const [offset, length] = Compact.decodeU8a(value);
return value.length === (offset + length.toNumber())
? value.subarray(offset)
: value;
}
// Decode as normal struct
return value;
}
/**
* @description Wrapped [[OuterDispatchCall]]
*/
get calls (): Vector<OuterDispatchCall> {
return (this.get('outerDispatch') as OuterDispatchMetadata).calls;
}
/**
* @description Wrapped [[OuterEventMetadataEvent]]
*/
get events (): Vector<OuterEventMetadataEvent> {
return (this.get('outerEvent') as OuterEventMetadata).events;
}
/**
* @description Wrapped [[RuntimeModuleMetadata]]
*/
get modules (): Vector<RuntimeModuleMetadata> {
return this.get('modules') as Vector<RuntimeModuleMetadata>;
}
private get argNames () {
return this.modules.map((module) =>
module.module.call.functions.map((fn) =>
fn.arguments.map((argument) => argument.type.toString())
)
);
}
private get eventNames () {
return this.events.map((module) =>
module.events.map((event) =>
event.arguments.map((argument) => argument.toString())
)
);
}
private get storageNames () {
return this.modules.map((module) =>
module.storage.isSome
? module.storage.unwrap().functions.map((fn) =>
fn.type.isMap
? [fn.type.asMap.key.toString(), fn.type.asMap.value.toString()]
: [fn.type.asType.toString()]
)
: []
);
}
/**
* @description Helper to retrieve a list of all type that are found, sorted and de-deuplicated
*/
getUniqTypes (throwError: boolean): Array<string> {
const types = flattenUniq([this.argNames, this.eventNames, this.storageNames]);
validateTypes(types, throwError);
return types;
}
}
export default MetadataV0;
+130
View File
@@ -0,0 +1,130 @@
// Copyright 2017-2019 @polkadot/types authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { MetadataInterface } from '../types';
import Option from '../../codec/Option';
import Struct from '../../codec/Struct';
import Vector from '../../codec/Vector';
import Text from '../../primitive/Text';
import { flattenUniq, validateTypes } from '../util';
import { MetadataCall } from './Calls';
import { MetadataEvent } from './Events';
import { MetadataStorage } from './Storage';
/**
* @name MetadataModule
* @description
* The definition of a module in the system
*/
export class MetadataModule extends Struct {
constructor (value?: any) {
super({
name: Text,
prefix: Text,
storage: Option.with(Vector.with(MetadataStorage)),
calls: Option.with(Vector.with(MetadataCall)),
events: Option.with(Vector.with(MetadataEvent))
}, value);
}
/**
* @description the module calls
*/
get calls (): Option<Vector<MetadataCall>> {
return this.get('calls') as Option<Vector<MetadataCall>>;
}
/**
* @description the module events
*/
get events (): Option<Vector<MetadataEvent>> {
return this.get('events') as Option<Vector<MetadataEvent>>;
}
/**
* @description the module name
*/
get name (): Text {
return this.get('name') as Text;
}
/**
* @description the module prefix
*/
get prefix (): Text {
return this.get('prefix') as Text;
}
/**
* @description the associated module storage
*/
get storage (): Option<Vector<MetadataStorage>> {
return this.get('storage') as Option<Vector<MetadataStorage>>;
}
}
/**
* @name MetadataV1
* @description
* The runtime metadata as a decoded structure
*/
export default class MetadataV1 extends Struct implements MetadataInterface {
constructor (value?: any) {
super({
modules: Vector.with(MetadataModule)
}, value);
}
/**
* @description The associated modules for this structure
*/
get modules (): Vector<MetadataModule> {
return this.get('modules') as Vector<MetadataModule>;
}
private get callNames () {
return this.modules.map((mod) =>
mod.calls.isNone
? []
: mod.calls.unwrap().map((fn) =>
fn.args.map((arg) => arg.type.toString())
)
);
}
private get eventNames () {
return this.modules.map((mod) =>
mod.events.isNone
? []
: mod.events.unwrap().map((event) =>
event.args.map((arg) => arg.toString())
)
);
}
private get storageNames () {
return this.modules.map((mod) =>
mod.storage.isNone
? []
: mod.storage.unwrap().map((fn) =>
fn.type.isMap
? [fn.type.asMap.key.toString(), fn.type.asMap.value.toString()]
: [fn.type.asType.toString()]
)
);
}
/**
* @description Helper to retrieve a list of all type that are found, sorted and de-deuplicated
*/
getUniqTypes (throwError: boolean): Array<string> {
const types = flattenUniq([this.callNames, this.eventNames, this.storageNames]);
validateTypes(types, throwError);
return types;
}
}
+5 -124
View File
@@ -2,129 +2,10 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { MetadataInterface } from '../types';
import { default as MetadataV1, MetadataModule as MetadataModuleV1 } from './Metadata';
import Option from '../../codec/Option';
import Struct from '../../codec/Struct';
import Vector from '../../codec/Vector';
import Text from '../../primitive/Text';
import { flattenUniq, validateTypes } from '../util';
export default MetadataV1;
import { MetadataCall } from './Calls';
import { MetadataEvent } from './Events';
import { MetadataStorage } from './Storage';
/**
* @name MetadataModule
* @description
* The definition of a module in the system
*/
export class MetadataModule extends Struct {
constructor (value?: any) {
super({
name: Text,
prefix: Text,
storage: Option.with(Vector.with(MetadataStorage)),
calls: Option.with(Vector.with(MetadataCall)),
events: Option.with(Vector.with(MetadataEvent))
}, value);
}
/**
* @description the module calls
*/
get calls (): Option<Vector<MetadataCall>> {
return this.get('calls') as Option<Vector<MetadataCall>>;
}
/**
* @description the module events
*/
get events (): Option<Vector<MetadataEvent>> {
return this.get('events') as Option<Vector<MetadataEvent>>;
}
/**
* @description the module name
*/
get name (): Text {
return this.get('name') as Text;
}
/**
* @description the module prefix
*/
get prefix (): Text {
return this.get('prefix') as Text;
}
/**
* @description the associated module storage
*/
get storage (): Option<Vector<MetadataStorage>> {
return this.get('storage') as Option<Vector<MetadataStorage>>;
}
}
/**
* @name MetadataV1
* @description
* The runtime metadata as a decoded structure
*/
export default class MetadataV1 extends Struct implements MetadataInterface {
constructor (value?: any) {
super({
modules: Vector.with(MetadataModule)
}, value);
}
/**
* @description The associated modules for this structure
*/
get modules (): Vector<MetadataModule> {
return this.get('modules') as Vector<MetadataModule>;
}
private get callNames () {
return this.modules.map((mod) =>
mod.calls.isNone
? []
: mod.calls.unwrap().map((fn) =>
fn.args.map((arg) => arg.type.toString())
)
);
}
private get eventNames () {
return this.modules.map((mod) =>
mod.events.isNone
? []
: mod.events.unwrap().map((event) =>
event.args.map((arg) => arg.toString())
)
);
}
private get storageNames () {
return this.modules.map((mod) =>
mod.storage.isNone
? []
: mod.storage.unwrap().map((fn) =>
fn.type.isMap
? [fn.type.asMap.key.toString(), fn.type.asMap.value.toString()]
: [fn.type.asType.toString()]
)
);
}
/**
* @description Helper to retrieve a list of all type that are found, sorted and de-deuplicated
*/
getUniqTypes (throwError: boolean): Array<string> {
const types = flattenUniq([this.callNames, this.eventNames, this.storageNames]);
validateTypes(types, throwError);
return types;
}
}
export {
MetadataModuleV1
};
+2 -2
View File
@@ -6,8 +6,8 @@ import { OuterDispatchCall, OuterDispatchMetadata } from '../v0/Calls';
import { EventMetadata, OuterEventMetadata, OuterEventMetadataEvent } from '../v0/Events';
import { CallMetadata, FunctionMetadata, RuntimeModuleMetadata, ModuleMetadata, StorageFunctionMetadata, StorageMetadata } from '../v0/Modules';
import MetadataV0 from '../v0';
import MetadataV1, { MetadataModule as MetadataModuleV1 } from './index';
import MetadataV0 from '../v0/Metadata';
import MetadataV1, { MetadataModuleV1 } from './index';
function storageV0 (mod: MetadataModuleV1): StorageMetadata | null {
if (mod.storage.isNone) {
+3 -3
View File
@@ -11,8 +11,8 @@ import Metadata from '../Metadata';
import rpcdata from '../Metadata/static';
import MetadataV0 from '../Metadata/v0';
const ANCHOR_TOP = `<a id='top' style='text-decoration: none;'>`;
const LINK_BACK_TO_TOP = `<a href='#top' style='float: right; font-size: 1.6rem; font-weight: bold;'>Back To Top</a>`;
const ANCHOR_TOP = '';
const LINK_BACK_TO_TOP = '';
const DESC_EXTRINSICS = '\n\n_The following sections contain Extrinsics methods are part of the default Substrate runtime._\n';
const DESC_EVENTS = '\n\nEvents are emitted for certain operations on the runtime. The following sections describe the events that are part of the default Substrate runtime.\n';
@@ -24,7 +24,7 @@ function sectionLink (sectionName: string) {
}
function generateSectionHeader (md: string, sectionName: string) {
return `${md}\n___\n${LINK_BACK_TO_TOP}\n\n### <a id='${sectionName}'></a>${sectionName}\n`;
return `${md}\n___\n${LINK_BACK_TO_TOP}\n\n### ${sectionName}\n`;
}
function addRpc () {