Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
957f3f404c | ||
|
|
d1105c899f | ||
|
|
df97bc2bf8 | ||
|
|
ebe77c25c7 | ||
|
|
eed83fa5d9 | ||
|
|
b62b1b248d | ||
|
|
3b30f1fd43 | ||
|
|
f28fff78b6 | ||
|
|
8b80ce7132 | ||
|
|
8accd2945e | ||
|
|
f8ec93adea | ||
|
|
f4fc3589cc | ||
|
|
4e52a2ec02 | ||
|
|
55fb391465 | ||
|
|
3e417f4f94 | ||
|
|
158410011e | ||
|
|
7383b1e1d7 | ||
|
|
ad9d21cae2 | ||
|
|
777007438c | ||
|
|
a1a52fbdb6 | ||
|
|
f9f5573abe | ||
|
|
bdd0cb9d9a | ||
|
|
0096d17f2f | ||
|
|
3e3d03693a | ||
|
|
d06f88d036 | ||
|
|
05d697ce3e | ||
|
|
0ea4d6934b | ||
|
|
aebe56f471 | ||
|
|
ee7225ca0e | ||
|
|
a0c6cd558e | ||
|
|
aa4dd63e2c | ||
|
|
fac0934c46 | ||
|
|
acf00d3bbe | ||
|
|
a3b0dde2cd | ||
|
|
af7a528a11 | ||
|
|
b889e56b34 | ||
|
|
fbf1f24f99 | ||
|
|
630b8318c0 | ||
|
|
208a2a17cf | ||
|
|
f66b2d00c4 | ||
|
|
ffe7201fbc | ||
|
|
00aca88519 | ||
|
|
6ec3815669 | ||
|
|
4b044799f6 | ||
|
|
7b844274fd | ||
|
|
7809ea5dad | ||
|
|
8d34d66386 | ||
|
|
9ffeda1749 | ||
|
|
1968380f89 | ||
|
|
bc3d21b66b | ||
|
|
20b835778d | ||
|
|
2f03cdfd4a | ||
|
|
096aa83dd7 | ||
|
|
08f6b14701 | ||
|
|
2dd7cc0e69 | ||
|
|
bd5c167c14 | ||
|
|
d905b4fef3 | ||
|
|
3fa8f9f89d | ||
|
|
35622a9037 | ||
|
|
eb0327880a | ||
|
|
417a9ffceb | ||
|
|
14100d319d | ||
|
|
a47b2ec1d2 | ||
|
|
911087f05c | ||
|
|
07d23d5675 | ||
|
|
b1e2befe74 | ||
|
|
a0194685b8 | ||
|
|
470698247d | ||
|
|
87f195df2f | ||
|
|
c2af5b3da9 | ||
|
|
098a7a0953 | ||
|
|
451dbf9eb3 | ||
|
|
9b1aa6aece | ||
|
|
2801290e92 | ||
|
|
9ffb4b8d91 | ||
|
|
b59be5a5af | ||
|
|
97a5b16320 |
@@ -2,6 +2,12 @@ const base = require('@polkadot/dev/config/eslint');
|
||||
|
||||
module.exports = {
|
||||
...base,
|
||||
parserOptions: {
|
||||
...base.parserOptions,
|
||||
project: [
|
||||
'./tsconfig.eslint.json'
|
||||
]
|
||||
},
|
||||
rules: {
|
||||
...base.rules,
|
||||
// add override for any (a metric ton of them, initial conversion)
|
||||
|
||||
@@ -1,3 +1,25 @@
|
||||
# 0.92.1
|
||||
|
||||
- The API now correctly sets the ss58 prefix as retrieved from the chain properties via `ss58Format`
|
||||
- Bump to `@polkadot/util` 1.4.1, removing use of `ExtError`
|
||||
- The `Keyring` from `@polkadot/keyring` is now exposed on the API as well. You can do `import { Keyring } from '@polkadot/api'` - this alleviates the need for extra dependencies (apart from `@polkadot/api`), and since the keyring is critical for signing operations, aligns everything in one bundle
|
||||
- Support the latest Polkadot & Substrate master branches (incl. metadata updates)
|
||||
- Getting started documentation has been made available
|
||||
|
||||
# 0.91.1
|
||||
|
||||
- This release was focussed on stability, with a number of cleanups and bug-fixes
|
||||
- Adjustments for Substrate 1.x chain detection (with auto-types) and Substrate 2.x support has been extended with all latest types
|
||||
- The `getRuntimeVersion` and `subscribeRuntimeVersion` RPCs are now only available on the `rpc.state.*` endpoints. This aligns with the Substrate implementation.
|
||||
- The `author_insertKey` RPC's last argument `publicKey` is now required, as to reflect Substrate implementation.
|
||||
- Support for extrinsics with versions that is not in the base Substrate implementation (V1-V3) can now be done by providing an implementation for `ExtrinsicUnknown`
|
||||
- Redeemed balance calculation if `api.derive` now returns the correct values again (bugfix)
|
||||
- added the `yarn chain:info [--ws URL]` utility to extract a calls-only metadata version
|
||||
- Missing types are now logged via a `console.warn`, not via `.error`
|
||||
- `Extrinsic`, `ExtrinsicPayload` & `SignerPayload` is registered in the type registry and can be overriden now
|
||||
- **Breaking change** `SignerPayload` is renamed to `SignerPayloadJSON`
|
||||
- **Breaking change** `SignerPayloadJSON`, `SignerPayloadRawBase` and `SignerPayloadRaw` are all moved to `@polkadot/types`
|
||||
|
||||
# 0.90.1
|
||||
|
||||
If you are upgrading form an older version, use the CHANGELOG hand-in-hand with the [migration guide](UPGRADING.md).
|
||||
|
||||
+5
-5
@@ -1,13 +1,13 @@
|
||||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
|
||||
function copy_folder () {
|
||||
SRC="packages/$1/build"
|
||||
DST="apps/node_modules/@polkadot/$2"
|
||||
DST="../apps/node_modules/@polkadot/$2"
|
||||
|
||||
echo "** Copying $SRC to apps/$DST"
|
||||
echo "** Copying $SRC to $DST"
|
||||
|
||||
rm -rf ../$DST
|
||||
cp -r $SRC ../$DST
|
||||
rm -rf $DST
|
||||
cp -r $SRC $DST
|
||||
}
|
||||
|
||||
yarn polkadot-dev-build-ts
|
||||
|
||||
@@ -22,11 +22,36 @@ module.exports = {
|
||||
],
|
||||
search: false,
|
||||
sidebar: [
|
||||
{
|
||||
title: 'Getting started',
|
||||
path: '/start/',
|
||||
collapsable: false,
|
||||
sidebarDepth: 0,
|
||||
children: [
|
||||
['start/install.md', 'Installation'],
|
||||
['start/basics.md', 'Basics & Metadata'],
|
||||
['start/create.md', 'Creating an instance'],
|
||||
['start/api.consts.md', 'Runtime Constants'],
|
||||
['start/api.query.md', 'State queries'],
|
||||
['start/api.rpc.md', 'RPC calls'],
|
||||
['start/api.query.subs.md', 'Query subscriptions'],
|
||||
['start/api.query.multi.md', 'Multi queries'],
|
||||
['start/api.query.other.md', 'Query extras'],
|
||||
['start/api.tx.md', 'Transactions'],
|
||||
['start/keyring.md', 'Keyring'],
|
||||
['start/api.tx.subs.md', 'Transaction subscriptions'],
|
||||
['start/api.tx.wrap.md', 'Complex transactions'],
|
||||
['start/types.basics.md', 'Type basics'],
|
||||
['start/types.extend.md', 'Extending types'],
|
||||
['start/typescript.md', 'TypeScript interfaces'],
|
||||
['start/FAQ.md', 'FAQ']
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Examples (Promise API)',
|
||||
path: '/examples/promise/',
|
||||
collapsable: false,
|
||||
sidebarDepth: 1,
|
||||
sidebarDepth: 0,
|
||||
children: [
|
||||
['examples/promise/01_simple_connect/', 'Simple connect'],
|
||||
['examples/promise/02_listen_to_blocks/', 'Listen to blocks'],
|
||||
@@ -40,15 +65,15 @@ module.exports = {
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Substrate interfaces',
|
||||
title: 'Substrate defaults',
|
||||
collapsable: false,
|
||||
sidebarDepth: 0,
|
||||
children: [
|
||||
['/METHODS_RPC.md', 'Substrate RPC'],
|
||||
['/METHODS_CONSTANTS.md', 'Constants (defaults)'],
|
||||
['/METHODS_STORAGE.md', 'State storage (defaults)'],
|
||||
['/METHODS_EXTRINSICS.md', 'Extrinsics (defaults)'],
|
||||
['/METHODS_EVENTS.md', 'System events (defaults)']
|
||||
['substrate/rpc.md', 'Substrate RPC'],
|
||||
['substrate/constants.md', 'Constants'],
|
||||
['substrate/storage.md', 'State storage'],
|
||||
['substrate/extrinsics.md', 'Extrinsics'],
|
||||
['substrate/events.md', 'System events']
|
||||
]
|
||||
},
|
||||
['/api/', '@polkadot/api'],
|
||||
|
||||
+2
-2
@@ -20,8 +20,8 @@ footer: Apache-2 Licensed | Copyright © 2017-2019 polkadot-js authors and contr
|
||||
|
||||
The API provides application developers the ability to query a node and interact with the Polkadot or Substrate chains using Javascript. Here you will find documentation and examples to get you started.
|
||||
|
||||
::: tip Examples
|
||||
In a rush and just want examples? [Jump right in](/examples/promise/) and get a handle on using the API in your projects.
|
||||
::: tip Getting started & Examples
|
||||
[Jump right in](/start/) and get an overview on using the API in your projects, from installation all the way through to making it do magic. Already understand how things work and just want the examples? [The ApiPromise examples](/examples/promise/) provide some basic recipies.
|
||||
:::
|
||||
|
||||
## Available packages
|
||||
|
||||
+27
-5
@@ -1,3 +1,23 @@
|
||||
## Getting started
|
||||
|
||||
- [Introduction](start/README.md)
|
||||
- [Installation](start/install.md)
|
||||
- [Basics](start/basics.md)
|
||||
- [Creating](start/create.md)
|
||||
- [Constant queries](start/api.consts.md)
|
||||
- [State queries](start/api.query.md)
|
||||
- [RPC queries](start/api.rpc.md)
|
||||
- [State subscriptions](start/api.query.subs.md)
|
||||
- [Multi state retrieval](start/api.query.multi.md)
|
||||
- [State query utilities](start/api.query.other.md)
|
||||
- [Transactions](start/api.tx.md)
|
||||
- [Keyring](start/keyring.md)
|
||||
- [Transaction subscriptions](start/api.tx.subs.md)
|
||||
- [Complex transactions](start/api.tx.wrap.md)
|
||||
- [Type basics](start/types.basics.md)
|
||||
- [Type extension](start/types.extend.md)
|
||||
- [TypeScript interfaces](start/typescript.md)
|
||||
|
||||
## Packages
|
||||
|
||||
- [api](api/README.md)
|
||||
@@ -10,13 +30,15 @@
|
||||
|
||||
## Interfaces
|
||||
|
||||
- [RPC](METHODS_RPC.md)
|
||||
- [Constants (runtime)](METHODS_CONSTANTS.md)
|
||||
- [Chain state (runtime)](METHODS_STORAGE.md)
|
||||
- [Extrinsics (runtime)](METHODS_EXTRINSICS.md)
|
||||
- [Events (runtime)](METHODS_EVENTS.md)
|
||||
- [Substrate](substrate/README.md)
|
||||
- [RPC](substrate/rpc.md)
|
||||
- [Constants (runtime)](substrate/constants.md)
|
||||
- [Chain state (runtime)](substrate/storage.md)
|
||||
- [Extrinsics (runtime)](substrate/extrinsics.md)
|
||||
- [Events (runtime)](substrate/events.md)
|
||||
|
||||
## Examples
|
||||
|
||||
- [ApiPromise](examples/promise/README.md)
|
||||
- [Simple connect](examples/promise/01_simple_connect/README.md)
|
||||
- [Listen to blocks](examples/promise/02_listen_to_blocks/README.md)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/require-await */
|
||||
/* eslint-disable @typescript-eslint/unbound-method */
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
// Required imports
|
||||
const { ApiPromise, WsProvider } = require('@polkadot/api');
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/require-await */
|
||||
/* eslint-disable @typescript-eslint/unbound-method */
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
// Import the API
|
||||
const { ApiPromise } = require('@polkadot/api');
|
||||
@@ -14,7 +16,7 @@ async function main () {
|
||||
// Subscribe to the new headers on-chain. The callback is fired when new headers
|
||||
// are found, the call itself returns a promise with a subscription that can be
|
||||
// used to unsubscribe from the newHead subscription
|
||||
const unsubscribe = await api.rpc.chain.subscribeNewHead((header) => {
|
||||
const unsubscribe = await api.rpc.chain.subscribeNewHeads((header) => {
|
||||
console.log(`Chain is at block: #${header.number}`);
|
||||
|
||||
if (++count === 256) {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/require-await */
|
||||
/* eslint-disable @typescript-eslint/unbound-method */
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
// Import the API
|
||||
const { ApiPromise } = require('@polkadot/api');
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/require-await */
|
||||
/* eslint-disable @typescript-eslint/unbound-method */
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
// Import the API
|
||||
const { ApiPromise } = require('@polkadot/api');
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/require-await */
|
||||
/* eslint-disable @typescript-eslint/unbound-method */
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
// Import the API
|
||||
const { ApiPromise } = require('@polkadot/api');
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/require-await */
|
||||
/* eslint-disable @typescript-eslint/unbound-method */
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
// Import the API
|
||||
const { ApiPromise } = require('@polkadot/api');
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/require-await */
|
||||
/* eslint-disable @typescript-eslint/unbound-method */
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
// Import the API
|
||||
const { ApiPromise } = require('@polkadot/api');
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/require-await */
|
||||
/* eslint-disable @typescript-eslint/unbound-method */
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
// Import the API, Keyring and some utility functions
|
||||
const { ApiPromise } = require('@polkadot/api');
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/require-await */
|
||||
/* eslint-disable @typescript-eslint/unbound-method */
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
// Import the API, Keyring and some utility functions
|
||||
const { ApiPromise } = require('@polkadot/api');
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/require-await */
|
||||
/* eslint-disable @typescript-eslint/unbound-method */
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
// Import the API
|
||||
const { ApiPromise } = require('@polkadot/api');
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/require-await */
|
||||
/* eslint-disable @typescript-eslint/unbound-method */
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
// Import the API & Provider and some utility functions
|
||||
const { ApiPromise } = require('@polkadot/api');
|
||||
@@ -35,8 +37,7 @@ async function main () {
|
||||
// Do the transfer and track the actual status
|
||||
api.tx.balances
|
||||
.transfer(recipient, AMOUNT)
|
||||
.sign(alicePair, { nonce })
|
||||
.send(({ events = [], status }) => {
|
||||
.signAndSend(alicePair, { nonce }, ({ events = [], status }) => {
|
||||
console.log('Transaction status:', status.type);
|
||||
|
||||
if (status.isFinalized) {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/require-await */
|
||||
/* eslint-disable @typescript-eslint/unbound-method */
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
// Import the API & Provider and some utility functions
|
||||
const { ApiPromise, WsProvider } = require('@polkadot/api');
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
/* eslint-disable @typescript-eslint/require-await */
|
||||
/* eslint-disable @typescript-eslint/unbound-method */
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
// Required imports
|
||||
const { zip } = require('rxjs');
|
||||
const { ApiRx } = require('@polkadot/api');
|
||||
const { WsProvider } = require('@polkadot/rpc-provider');
|
||||
|
||||
function main () {
|
||||
async function main () {
|
||||
// Initialise the provider to connect to the local node
|
||||
const provider = new WsProvider('ws://127.0.0.1:9944');
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/require-await */
|
||||
/* eslint-disable @typescript-eslint/unbound-method */
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
// Import the API
|
||||
const { ApiRx } = require('@polkadot/api');
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/require-await */
|
||||
/* eslint-disable @typescript-eslint/unbound-method */
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
// Import the API and operators from RxJs
|
||||
const { ApiRx } = require('@polkadot/api');
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/require-await */
|
||||
/* eslint-disable @typescript-eslint/unbound-method */
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
// Import the API
|
||||
const { ApiRx } = require('@polkadot/api');
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/require-await */
|
||||
/* eslint-disable @typescript-eslint/unbound-method */
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
// Import the API
|
||||
const { ApiRx } = require('@polkadot/api');
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/require-await */
|
||||
/* eslint-disable @typescript-eslint/unbound-method */
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
// Import the API, Keyring and some utility functions
|
||||
const { ApiRx } = require('@polkadot/api');
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/require-await */
|
||||
/* eslint-disable @typescript-eslint/unbound-method */
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
// Import the API and selected RxJs operators
|
||||
const { switchMap } = require('rxjs/operators');
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/require-await */
|
||||
/* eslint-disable @typescript-eslint/unbound-method */
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
// Import the API and some utility functions
|
||||
const { ApiRx } = require('@polkadot/api');
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/require-await */
|
||||
/* eslint-disable @typescript-eslint/unbound-method */
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
// Import the API & Provider and some utility functions
|
||||
const { ApiRx, WsProvider } = require('@polkadot/api');
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
... somewhere also need a signer injection section, something along the lines of the singlesigner or this https://github.com/polkadot-js/tools/blob/master/packages/signer-cli/src/cmdSubmit.ts#L12
|
||||
|
||||
... DoubleMap & LinkedMap examples
|
||||
|
||||
... api.derive
|
||||
@@ -0,0 +1,19 @@
|
||||
# FAQ
|
||||
|
||||
The list will be updated/expanded as questions come up, dealing with some common issues that API users find.
|
||||
|
||||
## I am getting a "Unknown types found, no types for ..." error
|
||||
|
||||
There are 2 caused for this, both related to the version of the API that you are using and the support of types. As explained in the elsewhere, types on Polkadot/Substrate are continuously evolving - the latest version of the API always tries to support types for the latest Polkadot networks, such as [Kusama](https://kusama.network/). So for Polkadot public chains, ensure that you are using the latest released API version.
|
||||
|
||||
If however you are running against a master branch of either Polkadot or Substrate, you may well be better suited running [a beta version, tracking master](install.md#betas). If you are connected to a customized chain, you would rather want to [register the types](types.extend.md) either on your own, or via packages that the chain vendor provides.
|
||||
|
||||
## I am getting a "Metadata:: failed on MagicNumber" error
|
||||
|
||||
Update your version of the API to the [latest version](install.md). Like types, the [metadata interfaces](basics.md) are continuously evolving. For instance with the Polkadot Alexander network, only metadata v3 is available. By the time Kusama launched, this has been bumped to v7. As these versions are added to the Polkadot/Substrate codebase, they are added to the API.
|
||||
|
||||
## I would like to sign transactions offline
|
||||
|
||||
The API itself is independent on where the signature comes from and how it is injected. Additionally it implements a signer interface, that can be used for external signing - an example of this is the [polkadot-js/apps](https://github.com/polkadot-js/apps) support for signing via extensions and even the [polkadot-js/extension](https://github.com/polkadot-js/extension) support for tools such as the [Parity Signer](https://github.com/paritytech/parity-signer).
|
||||
|
||||
As of this writing we don't have an explicit example of implementing the signer interface in these docs, although we do use one in [our tests](https://github.com/polkadot-js/api/blob/master/packages/api/test/util/SingleAccountSigner.ts). Additionally, the [polkadot-js/tools](https://github.com/polkadot-js/tools) has an implementation of [a very basic offline signer](https://github.com/polkadot-js/tools/tree/master/packages/signer-cli) where transactions are generated in one process and signatures in another non-connected process.
|
||||
@@ -0,0 +1,15 @@
|
||||
# Getting started
|
||||
|
||||
These sections should provide you with all the information needed to install the `@polkadot/api` package, understand the structure of the interfaces and allow you to start using it. For existing users this really should be titled "Things I wish I knew before I started using the api" - it really aims to close the gap to allow anybody to get to grips with using the packages.
|
||||
|
||||
## What this is not
|
||||
|
||||
This is not line-by-line documentation of all the extising function calls available, nor it is tied to a specific chain. (Although the examples do refer to the base Polkadot & Substrate chains). There will be some things in the API that are probably not covered, which brings us to the next point...
|
||||
|
||||
## Help us help others
|
||||
|
||||
If you spot gaps in the information provided, or are uncertain about any specific area, please do [log an issue](https://github.com/polkadot-js/api/issues) or if you are that way inclined, make a pull-request. We really want to have good documentation in these areas and allow people to be productive right from the start.
|
||||
|
||||
## Ready? Steady? Go!
|
||||
|
||||
If you already have a good grasp on the API and are just looking for a specific answer, you may want to take a look at the [Frequently Asked Questions](FAQ.md). With all that said, let's get started... [What should be installed, and how should we do it?](install.md)
|
||||
@@ -0,0 +1,34 @@
|
||||
# Runtime Constants
|
||||
|
||||
Constant queries will introduce you to the concepts behind the types and the interaction of the API with those types. The same concepts are implemented in the remainder of the API - the runtime constants is just the simplest starting point.
|
||||
|
||||
For some background: constants are values that are defined in the runtime and used as part of chain operations. These constants can be changed as part of an upgrade.
|
||||
|
||||
```js
|
||||
// initialize the API as per previous sections
|
||||
...
|
||||
|
||||
// the length of an epoch (session) in Babe
|
||||
console.log(api.consts.babe.epochDuration.toNumber());
|
||||
|
||||
// the amount required to create a new account
|
||||
console.log(api.consts.balances.creationFee.toNumber());
|
||||
|
||||
// the amount required per byte on an extrinsic
|
||||
console.log(api.consts.balances.transactionByteFee.toNumber());
|
||||
```
|
||||
|
||||
Since these are constants and defined by the metadata, it is not a call, but rather the values immediately available - as you'll see in subsequent sections, there is no need for `await` on these, it immediately returns the type and value for you to work with.
|
||||
|
||||
## The API and types
|
||||
|
||||
There is some magic applied by the API. For instance as the `createFee` result is returned, the API knows the expected type and makes a `Balance` object available, hence the `toNumber`. This result mapping is consistent in retrieving constants, making queries or even sending transactions:
|
||||
|
||||
- when values are passed to the API, the API will convert whatever is provided into the correct type as required by the call
|
||||
- when a value is retrieved, the API will provide an object of the correct type that wraps this value
|
||||
|
||||
From the last point, this means that a `Balance` will be returned as a number object extending [bn.js](https://github.com/indutny/bn.js/). In a later section we will go through a breakdown of all the commonly-used types and all [the basics available on types](types.basics.md).
|
||||
|
||||
## Making queries
|
||||
|
||||
In the next section we will take an initial dive into [chain state and state queries](api.query.md), allowing the use of the API to retrieve information contained in the chain state.
|
||||
@@ -0,0 +1,48 @@
|
||||
# State queries
|
||||
|
||||
In previous sections, we initialized the API and retrieved runtime constants. This section will walk through the concepts behind making queries to the chain to retrieve current state. The `api.query.<module>.<method>` interfaces, as already described earlier, is populated from the metadata. The API uses the metadata information provided to construct queries based on the location and parameters provided to generate state keys, and then queries these via RPC.
|
||||
|
||||
## Basic queries
|
||||
|
||||
Let's dive right in, connect to a general chain and retrieve some information on the current state. Of interest may be retrieving the nonce of a particular account as well as the current balance, this can be achieved via -
|
||||
|
||||
```js
|
||||
// initialize the API as in previous sections
|
||||
...
|
||||
|
||||
// the actual address that we will use
|
||||
const ADDR = '5DTestUPts3kjeXSTMyerHihn1uwMfLj8vU8sqF7qYrFabHE';
|
||||
|
||||
// retrieve the last timestamp
|
||||
const now = await api.query.timestamp.now();
|
||||
|
||||
// retrieve the account nonce via the system module
|
||||
const nonce = await api.query.system.accountNonce(ADDR);
|
||||
|
||||
// retrieve the account balance via the balances module
|
||||
const balance = await api.query.balance.freeBalance(ADDR);
|
||||
|
||||
console.log(`${now}: balance of ${balance} and a nonce of ${nonce}`);
|
||||
```
|
||||
|
||||
There have been some additions in the code above comparing with retrieving runtime constants. In these cases, since we are making a query to the actual chain, we use the `await` syntax to retrieve the information. Since the API is Promise-based, this means we can also rewrite the above to follow a Promise pattern,
|
||||
|
||||
```js
|
||||
...
|
||||
// retrieve last block timestamp, account nonce & balance
|
||||
const [now, nonce, balance] = await Promise.all([
|
||||
api.query.timestamp.now(),
|
||||
api.query.system.accountNonce(ADDR),
|
||||
api.query.balance.freeBalance(ADDR)
|
||||
]);
|
||||
```
|
||||
|
||||
## Parameters & return values
|
||||
|
||||
As indicated in previous sections, any return value is always an object with a consistent interface that reflects the type being returned. In the above example, the timestamp is a `Moment` (a `u64` value), the nonce is an `Index` (a `u32` value) and the `Balance` is an underlying `u128`.
|
||||
|
||||
Additionally we have provided some parameters for the query calls, specifically for the retrieval of the nonce and balance. It is important to note that the API will automatically convert any parameters into the correct type for encoding and making calls, in this case the `AccountId` parameter could be specified as a ss58 address (as it was), an actual `AccountId` (retrieved via another call) or just a plain `Uint8Array` (or even hex-string representation) for a publicKey.
|
||||
|
||||
## Exploring RPCs
|
||||
|
||||
Where all query functions use the underlying RPCs, together with metadata, to construct and retrieve information, the direct node RPCs can be seen as raw calls that enable these (slightly) higher-level operations. Next up we will take a dive into [making RPC calls via the API](api.rpc.md).
|
||||
@@ -0,0 +1,60 @@
|
||||
# Multi queries
|
||||
|
||||
In a number of applications, it is useful to monitor a number of like-queries at the same time. For instance, we may want to track the balances for a list of accounts we have. The `api.query` interfaces allows this via the `.multi` subscription call.
|
||||
|
||||
## Multi queries, same type
|
||||
|
||||
Where possible, the use of multi queries are encouraged since it tracks a number of state entries over a single RPC call, instead of making a call for each single item. In addition it allows you to have a single callback to track changes. For queries of the same type we can use `.multi`, for example to retrieve the balances of a number of accounts at once -
|
||||
|
||||
```js
|
||||
...
|
||||
|
||||
// subscribe to balance changes for 2 accounts, ADDR1 & ADDR2 (already defined)
|
||||
const unsub = await api.query.balances.freeBalance.multi([ADDR1, ADDR2], (balances) => {
|
||||
const [balance1, balance2] = balances;
|
||||
|
||||
console.log(`The balances are ${balance1} and ${balance2}`);
|
||||
});
|
||||
```
|
||||
|
||||
A couple of items to note in the example above: we don't call `freeBalance` directly, but rather `freeBalance.multi`. We pass the addresses we want to query as an array, and the length thereof would depend on the number of addresses we want to query. As an extended example, we can track the balances of a list of validators,
|
||||
|
||||
```js
|
||||
...
|
||||
|
||||
// retrieve a snapshot of the validators
|
||||
const validators = await api.query.session.validators();
|
||||
|
||||
// subscribe to the balances for these accounts
|
||||
const unsub = await api.query.balances.freeBalance.multi(validators, (balances) => {
|
||||
console.log(`The balances are: ${balances}`);
|
||||
});
|
||||
```
|
||||
|
||||
The above example does not subscribe to the validators explicitly, but only gets a snapshot and uses this into the future. It should be trivially extendable to subscribe to the validators, track which one have entered or left and then subscribe to balances as they change through the next blocks.
|
||||
|
||||
## Multi queries, distinct types
|
||||
|
||||
The previous `.multi` examples assumes that we do queries for the same types, i.e. we retrieve the balances for a number of accounts. However, there is also a need to retrieve various distinct types, as an example we would like to track the block timestamp in addition to the nonce and balance of a specific account. To cater for this, the api has a specific `api.queryMulti` interface that can be used to perform this query -
|
||||
|
||||
```js
|
||||
...
|
||||
|
||||
// subscribe to the timestamp, our index and balance
|
||||
const unsub = await api.queryMulti([
|
||||
api.query.timestamp.now,
|
||||
[api.query.system.accountNonce, ADDR],
|
||||
[api.query.balances.freeBalance, ADDR]
|
||||
], ([now, nonce, balance]) => {
|
||||
console.log(`${now}: balance of ${balance} and a nonce of ${nonce}`);
|
||||
});
|
||||
```
|
||||
|
||||
The above example certainly does not quite look as ergonomic and clean, but the API needs to understand (a) which are all the calls we need to make and (b) the calls and their params (if required). So breaking it down -
|
||||
|
||||
- `api.query.timestamp.now` - the timestamp is passed naked without any params. Also note that we do not call it while passing, but rather only provides a reference to the function, i.e. we do not have the expected `()` at the end. (This could also be of the form `[api.query.timestamp.now]`, aligning with subsequent entries)
|
||||
- `[api.query.system.accountNonce, ADDR]` - the nonce query is passed as an array containing the function (once again naked), followed by the parameters that apply.
|
||||
|
||||
## Rounding out queries
|
||||
|
||||
To round out our query introduction, there are a [number of other utilities and calls available](api.query.other.md) that allows the `api.query` user to perform certain tasks, such as querying state at a specific block. These are covered in the next section.
|
||||
@@ -0,0 +1,76 @@
|
||||
# Query extras
|
||||
|
||||
In previous sections we took a walk through queries, showing how to use one-shot queries, how to subscribe to results and how to combine multiple queries into one. This section will aim to extend that knowledge showing some other features and utilities that are available on the `api.query` interfaces.
|
||||
|
||||
## State at a specific block
|
||||
|
||||
Quite often is is useful (taking pruning into account, more on this later) to retrieve the state at a specific block. For instance we may wish to retrieve the current balance as well as the balance at a previous block for a specific account -
|
||||
|
||||
```js
|
||||
...
|
||||
|
||||
// retrieve the current block header
|
||||
const lastHdr = await api.rpc.chain.getHeader();
|
||||
|
||||
// retrieve the balance at both the current and the parent hashes
|
||||
const [balanceNow, balancePrev] = await Promise.all([
|
||||
api.query.balances.freeBalance.at(lastHdr.hash, ADDR),
|
||||
api.query.balances.freeBalance.at(lastHdr.parentHash, ADDR)
|
||||
]);
|
||||
|
||||
// display the difference
|
||||
console.log(`The delta was ${balanceNow.sub(balancePrev)}`);
|
||||
```
|
||||
|
||||
In the above example, we introduce the `.at(<hash>[, ...params])` query. For all `.at` queries, the first parameter is always the block hash at which we want to make the query, in our example we use both the last retrieved block and the parent thereof. The params are optional as per the type of query made, for instance to retrieve the timestamp for a previous block, it would be -
|
||||
|
||||
```js
|
||||
...
|
||||
|
||||
// retrieve the timestampt for the previous block
|
||||
const momentPrev = await api.query.timestamp.now.at(lastHdr.parentHash);
|
||||
```
|
||||
|
||||
The `.at` queries are all single-shot, i.e. there are no subscription option to these, since the state for a previous block should be static. (This is true to a certain extent, i.e. when blocks have been finalized).
|
||||
|
||||
An additional point to take care of (briefly mentioned above), is state pruning. By default a Polkadot/Substrate node will only keep state for the last 256 blocks, unless it is explicitly run in archive mode. This means that querying state further back than the pruning period will result in an error returned from the Node. (Generaly most public RPC nodes only run with default settings, which includes agressive state pruning)
|
||||
|
||||
## State entries
|
||||
|
||||
In addition to using `api.query` to make actual on-chain queries, it can also be used to retrieve some information on the state entries. For instance to retrieve both the hash and size of an existing entry, we can make the following calls -
|
||||
|
||||
```js
|
||||
...
|
||||
|
||||
// retrieve the hash & size of the entry as stored on-chain
|
||||
const [entryHash, entrySize] = await Promise.all([
|
||||
api.query.freeBalance.hash(ADDR),
|
||||
api.query.freeBalance.size(ADDR)
|
||||
]);
|
||||
|
||||
// output the info
|
||||
console.log(`The current size is ${entrySize} bytes with a hash of ${entryHash}`);
|
||||
```
|
||||
|
||||
As per the previous examples, the params here apply explicitly to the actual needed values to identify an entry. As with `.at` queries, there are no subscription versions for these queries, rather they are seen as one-shot values at a specific point in time.
|
||||
|
||||
## Entry metadata
|
||||
|
||||
It has been explained that the `api.query` interfaces are decorated from the metadata. This also means that there is some information that we can gather from the entry, as decorated -
|
||||
|
||||
```js
|
||||
// extract the info
|
||||
const { meta, method, section } = api.query.balances.freeBalance;
|
||||
|
||||
// display some info on a specific entry
|
||||
console.log(`${section}.${method}: ${meta.documentation.join(' ')}`);
|
||||
console.log(`query key: ${api.query.balances.freeBalance.key(ADDR)}`);
|
||||
```
|
||||
|
||||
The `section` & `method` is an indication of where it is exposed on the API. In addition the `meta` holds an array with the metadata documentation for the entry.
|
||||
|
||||
The `key` endpoint requires some explanation. In the chain state, the key values (identified by the module, method & params) are hashed and this is used as a lookup. So underlying a single-shot query would utilize the `api.rpc.state.getStorage` entry, passing the output of `key` (which is a hashed representation of the values). Apart from the hashing, the API also takes care of type formatting, handling optional values and merging results accross multiple subscriptions.
|
||||
|
||||
## Let's transact already!
|
||||
|
||||
At this point you are already burning to actually make some transactions. Making queries is cool, but just how do [you actually submit transactions on-chain](api.tx.md).
|
||||
@@ -0,0 +1,37 @@
|
||||
# Query subscriptions
|
||||
|
||||
Previously we explained the concepts between `api.query`. In this section we will expand on that knowledge to introduce subscriptions (akin to what we found in `api.rpc`) to stream results from the state, as it changes between blocks.
|
||||
|
||||
## Subcriptions
|
||||
|
||||
As in the case with `api.rpc` subscriptions, query subscriptions follow exactly the same form - an actual call is augmented with a callback to return the current state value that is updated as the underlying value changes. As an example, we can extend on what we had previously -
|
||||
|
||||
```js
|
||||
...
|
||||
|
||||
// retrieve the current timestamp via subscription
|
||||
const unsub = await api.query.timestamp.now((moment) => {
|
||||
console.log(`The last block has a timestamp of ${moment}`);
|
||||
});
|
||||
```
|
||||
|
||||
The form is exactly the same as the subscriptions we have seen previously, instead of the `await` returning the actual once-off value, it returns a subscription `unsub()` function that can be used to stop the subscription and clear up any underlying RPC connections. The supplied callback will contain the value as it changes, streamed from the node.
|
||||
|
||||
## Subscriptions with params
|
||||
|
||||
If we had a query with parameters, i.e. where we wish to perform a query for a specific account, the form is exactly the same - the last parameter contains the actual callback, after all other parameters. To retrieve the balances for an account as it changes, we could do the following -
|
||||
|
||||
```js
|
||||
...
|
||||
|
||||
// subscribe to balance changes for our account
|
||||
const unsub = await api.query.balances.freeBalance(ADDR, (balance) => {
|
||||
console.log(`Your account balance is ${balance}`);
|
||||
});
|
||||
```
|
||||
|
||||
By now this subscription form should be familiar to you, including the usage of `unsub`.
|
||||
|
||||
## Multiple queries
|
||||
|
||||
In most non-trivial applications, it is useful to optimize both our code in terms of callbacks as well as node resources, for instance by [performing multiple queries at once, over the same RPC call](api.query.multi.md).
|
||||
@@ -0,0 +1,70 @@
|
||||
# RPC queries
|
||||
|
||||
The RPC calls provide the backbone for the transmission of data to and from the node. This means that all API endpoints such as `api.query`, `api.tx` or `api.derive` just wrap RPC calls, providing information in the encoded format as expected by the node.
|
||||
|
||||
Since you are already familiar with the `api.query` interface, the `api.rpc` interface follows the same format, for instance -
|
||||
|
||||
```js
|
||||
...
|
||||
|
||||
// retrieve the chain name
|
||||
const chain = await api.rpc.system.chain();
|
||||
|
||||
// retrieve the latest header
|
||||
const lastHeader = await api.rpc.chain.getHeader();
|
||||
|
||||
// log the information
|
||||
console.log(`${chain}: last block #${lastHeader.number} has hash ${lastHeader.hash}`);
|
||||
```
|
||||
|
||||
In this example, you will see the same pattern as with queries: each result is a promise and a simple `await` makes the query and resolves with the result.
|
||||
|
||||
## Subscriptions
|
||||
|
||||
The RPCs lend themselves to using subscriptions, for instance in the above case you would assume that once connected, the chain won't change, however new blocks will come in at intervals and we probably want to keep track of those. We can adapt the previous example to start using subscriptions -
|
||||
|
||||
```js
|
||||
...
|
||||
|
||||
// subscribe to the new headers
|
||||
await api.rpc.chain.subscribeNewHeads((lastHeader) => {
|
||||
console.log(`${chain}: last block #${lastHeader.number} has hash ${lastHeader.hash}`);
|
||||
});
|
||||
```
|
||||
|
||||
Since we are dealing with a subscription, we now pass a callback into the `subscribeNewHeads` function, and this will be triggered on each header, as they are imported. The same pattern would apply to each of the `api.rpc.subscribe*` functions - as a last parameter a callback is to be provided that streams the latest data, as it becomes available.
|
||||
|
||||
In general, whenever we create a subscription, we would like to cleanup after ourselves and unsubscribe, so assuming we only want to log the first 10 headers, the above example can be adjusted in the following manner -
|
||||
|
||||
```js
|
||||
...
|
||||
let count = 0;
|
||||
|
||||
// subscribe to the new headers
|
||||
const unsubHeads = await api.rpc.chain.subscribeNewHeads((lastHeader) => {
|
||||
console.log(`${chain}: last block #${lastHeader.number} has hash ${lastHeader.hash}`);
|
||||
|
||||
if (++count === 10) {
|
||||
unsubHeads();
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Unlike single-shot queries, for subscriptions we are `await`-ing a function, taking no parameters (that also returns nothing) that can be used to unsubscribe for the subscription and clear the underlying RPC connection. So in the above example we set `unsubHeads` and then call it when we wish to cancel the subscription.
|
||||
|
||||
## Detour into derives
|
||||
|
||||
The `api.derive` interfaces will be covered in a follow-up section, but since the above example deals with new head subscriptions, a quick detour is warranted. The derives are just helpers that define certain functions and combine results from multiple sources. For new headers, the following information is useful in certain scenarios -
|
||||
|
||||
```js
|
||||
...
|
||||
const unsub = await api.derive.chain.subscribeNewHeads((lastHeader) => {
|
||||
console.log(`#${lastHeader.number} was authored by ${lastHeader.author}`);
|
||||
});
|
||||
```
|
||||
|
||||
In the above case the `subscribeNewHeads` derive augments the header retrieved with an `.author` getter. This is done by parsing the actual header and logs received and filling in the author from the `api.query.session.validators` call.
|
||||
|
||||
## Extended Queries
|
||||
|
||||
As a next step, now that we have understood subscription and RPC basics, we will circle back to the `api.query` interface, [extending our queries with subscriptions](api.query.subs.md).
|
||||
@@ -0,0 +1,40 @@
|
||||
# Transactions
|
||||
|
||||
Transaction endpoints are exposed, as determined by the metadata, on the `api.tx` endpoint. These allow you to submit transactions for inclusion in blocks, be it transfers, setting information or anything else your chain supports.
|
||||
|
||||
## Simple transactions
|
||||
|
||||
To start off, let's make a balance transfer from Alice to Bob.
|
||||
|
||||
```js
|
||||
...
|
||||
|
||||
// sign and send a transfer from Alice to Bob
|
||||
const txHash = await api.tx.balances
|
||||
.transfer(BOB, 12345)
|
||||
.signAndSend(alice);
|
||||
|
||||
// show the hash
|
||||
console.log(`Submitted with hash ${txHash}`);
|
||||
```
|
||||
|
||||
We have already become familiar with the `Promise` syntax that is used throughout the API, in this case it is no different. We construct a transaction by calling `balances.transfer(<accountId>, <value>)` with the required params and then as a next step we submit it to the node.
|
||||
|
||||
As with all other API operations, the `to` params just needs to be "account-like" and the value params needs to be "number-like", the API will take care of encoding and conversion into the correct format.
|
||||
|
||||
The result for this call (we will deal with subscriptions in a short while), is the transaction hash. This is a hash of the data and receiving this does not mean that transaction has been included, but rather only that it has been accepted for propagation by the node. (It can still fail on execution, we will handle this in some of our follow-up sections.)
|
||||
|
||||
## Under the hood
|
||||
|
||||
Despite the single-line format of `signAndSend`, there is a lot hapenning under the hood (and all of this can be manually provided) -
|
||||
|
||||
- Based on the sender, the API will retrieve the `system.accountNonce` to determine the next nonce to use
|
||||
- The API will retrieve the current block hash and use it to create a mortal transaction, i.e. the transaction will only be valid for a limited number of blocks (by default this is 5 mins at 6s blocktimes)
|
||||
- It will construct a payload and sign this, this includes the `genesisHash`, the `blockHash` for the start of the mortal era as well as the current chain `specVersion`
|
||||
- The transaction is submitted to the node
|
||||
|
||||
As suggested, you can override all of this, i.e. by retrieving the nonce yourself and passing that as an option, i.e. `signAndSend(alice, { nonce: aliceNonce })`, this could be useful when manually tracking and submitting transactions in bulk.
|
||||
|
||||
## Into the keyring we go
|
||||
|
||||
With the examples above, the variable `alice` seems to have appeared from thin air. To understand how transactions are signed, we will take a [brief diversion into the keyring](keyring.md) before returning to our regularly scheduled program.
|
||||
@@ -0,0 +1,65 @@
|
||||
# Transaction subscriptions
|
||||
|
||||
Previously we sent simple transactions using the `api.tx` endpoints, in this section we will extend that to monitor the actual transactions for inclusion and also extend the monitoring for transaction events.
|
||||
|
||||
## Transaction inclusion
|
||||
|
||||
To send a transaction and then waiting until it has been included in a block, we will use a subscription interface instead of just waiting for the transaction pool addition to yield the extrinsic hash. For the simplest form, we can do the following -
|
||||
|
||||
```js
|
||||
...
|
||||
|
||||
// create alice (carry-over from the keyring section)
|
||||
const alice = keyring.addFromUri('//Alice');
|
||||
|
||||
// make a transfer from Alice to BOB, waiting for inclusion
|
||||
const unsub = await api.tx.balances
|
||||
.transfer(BOB, 12345)
|
||||
.signAndSend(alice, (result) => {
|
||||
console.log(`Current status is ${result.status}`);
|
||||
|
||||
if (result.status.isFinalized) {
|
||||
console.log(`Transaction included at blockHash ${result.status.asFinalized}`);
|
||||
unsub();
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
As per all previous subscriptions, the transaction subscription returns in `unsub()` and the actual method has a subscription callback. The `result` object has 2 parts, `events` (to to covered in the next section) and the `status` enum.
|
||||
|
||||
When the `status` enum is in `Finalized` state (checked via `isFinalized`), the underlying value contains the block hash of the block where the transaction has been included. This does not mean the block is finalized, but rather applies to the transaction state, as no further updates will be received for this subscription.
|
||||
|
||||
## Transaction events
|
||||
|
||||
Any transaction will emit events, as a bare minimum this will always be either a `system.ExtrinsicSuccess` or `system.ExtrinsicFailed` event for the specific transaction. These provide the overall execution result for the transaction, i.e. execution has succeeded or failed.
|
||||
|
||||
Depending on the transaction sent, some other events may however be emitted, for instance for a `balances.transfer` this could include one or more of `Transfer`, `NewAccount` or `ReapedAccount`, as defined in the [substrate balances event defaults](../substrate/events.md#balances).
|
||||
|
||||
To display or act on these events, we can do the following -
|
||||
|
||||
```js
|
||||
...
|
||||
// make a transfer from Alice to BOB, waiting for inclusion
|
||||
const unsub = await api.tx.balances
|
||||
.transfer(BOB, 12345)
|
||||
.signAndSend(alice, ({ events = [], status }) => {
|
||||
console.log(`Current status is ${status.type}`);
|
||||
|
||||
if (status.isFinalized) {
|
||||
console.log(`Transaction included at blockHash ${status.asFinalized}`);
|
||||
|
||||
// loop through Vec<EventRecord> to display all events
|
||||
events.forEach(({ phase, event: { data, method, section } }) => {
|
||||
console.log(`\t' ${phase}: ${section}.${method}:: ${data}`);
|
||||
});
|
||||
|
||||
unsub();
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Be aware that when a transaction status is `isFinalized`, it means it is included, but it may still have failed - for instance if you try to send a larger amount that you have free, the transaction is included in a block, however from a end-user perspective the transaction failed since the transfer did not occur. In these cases a `system.ExtrinsicFailed` event will be available in the events array.
|
||||
|
||||
## Complex transactions
|
||||
|
||||
In many cases transactions can carry quite complex information, be it for passing objects or proposing changes. In the next section we will take a dive [into complex transactions, including those wrapped for sudo](api.tx.wrap.md).
|
||||
@@ -0,0 +1,44 @@
|
||||
# Complex transactions
|
||||
|
||||
Up till now we have focussed on the base operation of transactions. There are however some more complex operations that deserve some more information, for instance when doing either democracy proposals or excuting sudo calls, in both these cases the transaction wraps a call or proposal to be evaluated.
|
||||
|
||||
## Sudo use
|
||||
|
||||
When running a development chain (Polkadot/Substrate with a `--dev` flag), or in certain testnets a sudo module is available - just like the sudo command found on some systems, it allows root-level access to perform actions. For instance, we can perform a `setBalance(<accountId>, <free>, <reserved>)` on an account -
|
||||
|
||||
```js
|
||||
...
|
||||
// get the current sudo key in the system
|
||||
const sudoKey = await api.query.sudo.key();
|
||||
|
||||
// lookup from keyring (assuming we have added all, on --dev this would be `//Alice`)
|
||||
const sudoPair = keyring.getPair(sudoKey);
|
||||
|
||||
// send the actual sudo transaction
|
||||
const unsub = await api.tx.sudo
|
||||
.sudo(
|
||||
api.tx.balances.setBalance(ADDR, 12345, 678)
|
||||
)
|
||||
.signAndSend(sudoPair, (result) => { ... });
|
||||
```
|
||||
|
||||
The above is really quite straight-forward, the `sudo.sudo(<call>)` call takes 1 parameter, which is a `Call`. We construct this via the `api.tx` and pass it through. The only difference is that the nested call has no actual `.signAndSend` on it, rather it is only used as a container for data.
|
||||
|
||||
Exactly the same would apply to the standard `democracy.propose(<proposal>, <value>)`, for instance we can just swap the above sudo wrapper with a proposal and add the correct fees for the proposal.
|
||||
|
||||
## Complex types
|
||||
|
||||
As indicated in previous sections (we will cover types in more detail next), the API will format the inputs into the actual type required for submission. For primitives such as numbers, this is quite understandable, but it is worth spending at least one example on cases where an object is provided as an input. For instance, making a call to validate -
|
||||
|
||||
```js
|
||||
...
|
||||
const txHash = await api.tx.staking.validate({
|
||||
validatorPayment: 12345
|
||||
});
|
||||
```
|
||||
|
||||
In the above example, all we need to provide is a the fields for the `ValidatorPrefs` object. (Any fields not defined will be set to the default for that type, i.e. all zero). This object maps through to what is defined on the Polkadot/Substrate side, with the [@polkadot/types version](https://github.com/polkadot-js/api/blob/master/packages/types/src/interfaces/staking/definitions.ts) mapping all fields.
|
||||
|
||||
## Understanding types
|
||||
|
||||
As has been very apparent in all the preceding sections, the management of types is what allows the API to communicate with the node. Most values are in a [binary SCALE-encoded format](https://github.com/paritytech/parity-scale-codec) and it is the reponsibility of the API is to encode and decode these. In the next section we will [take a look at what interfaces the API provides around types](types.basics.md).
|
||||
@@ -0,0 +1,34 @@
|
||||
# Basics & Metadata
|
||||
|
||||
One of the most important things to understand about the `@polkadot/api` is that most interfaces are actually generated automatically when it connects to a running node. This is quite a departure from other APIs in projects where the interfaces are static. While sounding quite scary, it actually is a powerful concept that exists in both Polkadot and Substrate chains, and allows the API to be used in environments where the chain is customized.
|
||||
|
||||
To unpack this, we will start with the Metadata and explain what it actually provides, since it is critical for understanding how to interact with the API and any underlying chain.
|
||||
|
||||
## Metadata
|
||||
|
||||
When the API connects to a node, one of the first things it does is to retrieve the metadata. The metadata, effectively provides data in the form of `api.<type>.<module>.<section>` that fits into one of the following categories -
|
||||
|
||||
- [consts](../substrate/constants.md) - All runtime constants, e.g. `api.consts.balances.creationFee`. These are not functions, rather accessing the endpoint immediately yields the result as defined.
|
||||
- [query](../substrate/storage.md) - All chain state, e.g. `api.query.balances.freeBalance(<accountId>)`.
|
||||
- [tx](../substrate/extrinsics.md) - All extrinsics, e.g. `api.tx.balances.transfer(<accountId>, <value>)`.
|
||||
|
||||
Additionally the metadata also provides information on [events](../substrate/events.md), these are query-able via the `api.query.system.events()` interface and also appear on transactions... both these cases are detailed later.
|
||||
|
||||
## Types
|
||||
|
||||
The metadata defiends the calls with all the type names used in the various interfaces. At the moment (this is undergoing investigations and could improve in future versions of metadata), this also means that the types between the API and the node need to be aligned. For instance, by default Substrate defines a `BlockNumber` type as a `u32` and the API follows the Substrate defaults - if a chain has a different definitions, the API needs to be aware of this so it can actually decode (and encode) the type.
|
||||
|
||||
At this point just be aware of it, we will touch on types, custom chains and their impacts in a later section.
|
||||
|
||||
## Chain Defaults
|
||||
|
||||
In addition to the `api.[consts | query | tx]` detailed above, the API, upon connecting to a chain, fills in some information and makes it available directly on the API interface. These include -
|
||||
|
||||
- `api.genesisHash` - The genesisHash of the connected chain
|
||||
- `api.runtimeMetadata` - The metadata as retrieved from the chain
|
||||
- `api.runtimeVersion` - The chain runtime version (including spec/impl. versions and types)
|
||||
- `api.libraryInfo` - The version of the API, i.e. `@polkadot/api v0.90.1`
|
||||
|
||||
## Let's do something!
|
||||
|
||||
Now that we have covered what the API actually exposes, it is time to [dive in and actually use what we installed earlier](create.md).
|
||||
@@ -0,0 +1,79 @@
|
||||
# Create an instance
|
||||
|
||||
We have the API installed, we have an understanding of what will actually be exposed and how the API knows what to expose. So down the rabbit hole we go - let's create an actual API instance, and then take it from there -
|
||||
|
||||
```js
|
||||
// import
|
||||
import { ApiPromise, WsProvider } from '@polkadot/api';
|
||||
|
||||
...
|
||||
// construct
|
||||
const wsProvider = new WsProvider('wss://poc-3.polkadot.io');
|
||||
const api = await ApiPromise.create({ provider: wsProvider });
|
||||
|
||||
// do something
|
||||
console.log(api.genesisHash.toHex());
|
||||
```
|
||||
|
||||
We will have some explanation on the ES2015 syntax used next, but just a small note on the above - where other code is included (or just some previous boilerplate is used), you will see `...` in most of the examples. This is not due to lazyness, but rather just to keep things straight and to the point.
|
||||
|
||||
## ES2015 Usage and examples
|
||||
|
||||
Before we jump into an explanation of the above example, be aware that in all cases we are using ES2015, including using things like `async`/`await`, `import` and others. Depending on your environment, this may require some adjustments.
|
||||
|
||||
While we are using the `await` naked in all examples (this removes boilerplate), it will need to be wrapped in an `async` block, for we could warp all samples inside a `async function main () { ... }` and then just call `main()`.
|
||||
|
||||
In the case of Node.js you would change the `import` into `require`, i.e.
|
||||
|
||||
```js
|
||||
// import
|
||||
const { ApiPromise, WsProvider } = require('@polkadot/api');
|
||||
...
|
||||
```
|
||||
|
||||
We are basing all our examples on the [ApiPromise](../examples/promise/README.md) version of the API, however there is also an RxJS version available. Since Promises are a part of the ES2015 specification, it covers the greater amount of use and is the one that will be used in 95% of the cases and should be familiar to 100% of all developers. However if you are in an environment where RxJs is recommended or your have a great affinity ot it, you could take a look at the [RxJS examples](../examples/rx/README.md) once you are familiar with the base concepts introduced here.
|
||||
|
||||
For now... just ignore the various flavors and focus on understanding the concepts.
|
||||
|
||||
## Providers
|
||||
|
||||
Focusing on the construction, any API requires a provider and we create one via the `const wsProvider = new WsProvider(...)`. By default, if none is provided to the API it will construct a default `WsProvider` instance to connect to `ws://127.0.0.1:9944`.
|
||||
|
||||
We generally recommend always specifying the endpoint since in most cases we want to connect to an external node and even for local nodes, it is always better being explicit, less magic that can make you wonder in the future.
|
||||
|
||||
At this time the only provider type that is fully supported by the API is the WebSocket version. Polkadot/Substrate really comes alive with possibilities once you have access to bi-directional RPCs such as what WebSockets provide. (It is technically possible to have some limited capability via bare-HTTP, but at this point WebSockets is the only fully-operational and supported version - always remember that it is just "upgraded HTTP".)
|
||||
|
||||
## API Instance
|
||||
|
||||
The API creation is done via the `ApiPromise.create` interface which is a shortcut version for calling `new` and then waiting until the API is connected. Without the `async` syntax, this would be,
|
||||
|
||||
```js
|
||||
ApiPromise
|
||||
.create({ provider: wsProvider })
|
||||
.then((api) =>
|
||||
console.log(api.genesisHash.toHex())
|
||||
);
|
||||
```
|
||||
|
||||
In most cases we would suggest using the `.create` shortcut, which really just takes care of the following boilerplate that otherwise needs to be provided -
|
||||
|
||||
```js
|
||||
// create the instance
|
||||
const api = new ApiPromise({ provider: wsProvider });
|
||||
|
||||
// wait until we are ready and connected
|
||||
await api.isReady;
|
||||
|
||||
// do something
|
||||
console.log(api.genesisHash.toHex());
|
||||
```
|
||||
|
||||
## Advanced creation
|
||||
|
||||
There are more advanced cases where you would prefer to use the longer version, for instance: if you want to explicitly listen to events emitted, you probably want to attach to the API even before connecting to the chain. All API instances implement an `EventEmitter` interface, with `on` handlers, which emit `connected`, `disconnected`, `ready` and `error` events, allowing you to listen to events on the transport layer.
|
||||
|
||||
In these cases, create via `new`, attach listeners and then wait for the `isReady`.
|
||||
|
||||
## Do something
|
||||
|
||||
Now that we have the API initialized, the next step would be to start using it to interact and extract data [starting with chain constants](api.consts.md).
|
||||
@@ -0,0 +1,27 @@
|
||||
# Installation
|
||||
|
||||
Yes, it really is as simple as [installing from npm](https://www.npmjs.com/package/@polkadot/api), so we are not going to waste too much time with the bare basics, just install the API via
|
||||
|
||||
`yarn add @polkadot/api`
|
||||
|
||||
And it will be added and ready for use. The above will always install the latest stable release, which should allow you to connect to testsnets and local nodes that are tracking versioned releases for [Polkadot](https://github.com/paritytech/polkadot) and [Substrate](https://github.com/paritytech/substrate).
|
||||
|
||||
## Betas
|
||||
|
||||
For users who have a slightly higher appetite for risk, or are using bleeding-edge master branches of either Polkadot/Substrate, we also publish a beta version as soon as anything is merged into the API master branch. This version really contains all the latest fixes and features and is the version we actually use inside the polkadot-js projects - eating our own dogfood.
|
||||
|
||||
To install a beta version, either to test or for support of a feature that is available in Substrate master (and has not yet made it to a stable api release), you can install it via the `@beta` tag, i.e.
|
||||
|
||||
`yarn add @polkadot/api@beta`
|
||||
|
||||
## Other dependencies
|
||||
|
||||
In most cases, you don't need to do anything else apart from just installing `@polkadot/api` above. It has dependencies such as `@polkadot/types` which are installed automatically alongside. When using `yarn` the dependencies are installed, flattened, available for use and you will never run into issues with mismatched versions.
|
||||
|
||||
This means that by simply installing `@polkadot/api`, you will have access to utilities (crypto and normal), types, providers and even higher-order (derived) API functions. (We will get to all of these in follow-up sections)
|
||||
|
||||
If you do however decide to explicitly install other packages (even though they are dependencies), please make sure that the versions inside the api package always match with your versions, i.e. if you installed `@polkadot/api` `0.91.0-beta.22` and you have your own version of `@polkadot/types`, ensure that it is also `0.91.0-beta.22`.
|
||||
|
||||
## API basics
|
||||
|
||||
So we have it installed. Before we jump into actual real-world usage, [let's understand what the API gives us](basics.md).
|
||||
@@ -0,0 +1,102 @@
|
||||
# Keyring
|
||||
|
||||
This section will give a quick introduction into the Keyring, including the addition of accounts, retrieving pairs and the signing of any data. Unlike the rest of the API, only the core concepts will be covered with the most-used-functions. However, what is covered is enough for 99.9 of the use-cases ... or rather, that is the aim.
|
||||
|
||||
## Installation
|
||||
|
||||
They [@polkadot/keyring](https://github.com/polkadot-js/common/tree/master/packages/keyring) keyring is included directly with the API as a dependency, so it is directly importable (since the 0.92 version) alongside the API.
|
||||
|
||||
If you do opt to install it seperately, ensure that the version of `@polkadot/util-crypto` that is included with the API matches with the version of `@polkadot/keyring` installed. So if the API depends on `util-crypto 1.4.1`, it would make sense to include `keyring 1.4.1` as the installed version. (This helps in making sure extra versions of the libraries are not included as duplicates, especially in the case where bundles are created. Additionally, this makes sure that weird side-effects in the WASM initialization is avoided.)
|
||||
|
||||
## Creating a keyring instance
|
||||
|
||||
Once installed, you can create an instance by just creating an instance of the `Keyring` class -
|
||||
|
||||
```js
|
||||
// import the keyring as required
|
||||
import { Keyring } from '@polkadot/api';
|
||||
|
||||
// initialize the API as we would normally do
|
||||
...
|
||||
|
||||
// create a keyring instance
|
||||
const keyring = new Keyring({ type: 'sr25519' });
|
||||
```
|
||||
|
||||
In the above example, the import is self-explanatory. Upon creation we pass through a `type` which can have a value of either `ed25519` or `sr25519`, when not specified this would default to `ed25519`. This type parameter only applies to the default type of account created when no type is specified, it does not mean that the keyring can only store that type of account.
|
||||
|
||||
So effectively, when creating an account and not specifying a type, it will be `sr25519` by default based on the above construction params, however we can also add an `ed25519` account and use it transparently in the same keyring.
|
||||
|
||||
One "trick" that is done implictly in the above sample is that that keyring is only initialized after the API. In the case of `sr25519` the keyring relies on a [WASM build](https://github.com/polkadot-js/wasm) of the [schnorrkel libraries](https://github.com/w3f/schnorrkel). Since the API inlitialization is already async, it initializes the WASM libraries are part of the setup.
|
||||
|
||||
However, this initialization can also be done explicitly, mostly for more advances use-cases, or in cases where the API won't be attached until much later -
|
||||
|
||||
```js
|
||||
// crypto promise, package used by keyring internally
|
||||
import { cryptoWaitReady } from '@polkadot/util-crypto';
|
||||
|
||||
// wait for the promise to resolve, async WASM or `cryptoWaitReady().then(() => { ... })`
|
||||
await cryptoWaitReady();
|
||||
|
||||
// create a keyring instance
|
||||
const keyring = new Keyring({ type: 'sr25519' });
|
||||
```
|
||||
|
||||
## Adding accounts
|
||||
|
||||
The recommended catch-all approach to adding accounts is via `.addFromUri(<suri>, [meta], [type])` function, where only the `suri` param is required. For instance to add an account via mnemonic, you would do the following -
|
||||
|
||||
```js
|
||||
...
|
||||
|
||||
// some mnemonic phrase
|
||||
const PHRASE = 'entire material egg meadow latin bargain dutch coral blood melt acoustic thought';
|
||||
|
||||
// add an account, straight menemonic
|
||||
const newPair = keyring.addFromUri(PHRASE);
|
||||
|
||||
// (advanced) add an account with a derivation path (hard & soft)
|
||||
const newDeri = keyring.addFromUri(`${PHRASE}//hard-derived/soft-derived`);
|
||||
|
||||
// (advanced, development-only) add with an implied dev seed and hard derivation
|
||||
const alice = keyring.addFromUri('//Alice', { name: 'Alice default' });
|
||||
```
|
||||
|
||||
The above additions cater for most of the usecases and aligns with the you would find in the Substrate `subkey`. Be very wary of the last "dev-seed" option, it is explicitly added for `subkey` compatibility and implies using the "known-everywhere" dev seed. It is however useful when running Polkadot/Substrate with a `--dev` flag.
|
||||
|
||||
## Working with pairs
|
||||
|
||||
In the previous examples we added a pair to the keyring (and we actually immediately got access to the pair). From this pair there is some information we can retrieve -
|
||||
|
||||
```js
|
||||
...
|
||||
|
||||
// add our Alice dev account
|
||||
const alice = keyring.addFromUri('//Alice', { name: 'Alice default' });
|
||||
|
||||
// log some info
|
||||
console.log(`${alice.meta.name}: has address ${alice.address} with publicKey [${alice.publicKey}]`);
|
||||
```
|
||||
|
||||
Additionally you can sign and verify using the pairs. This is the same internally to the API when constructing transactions -
|
||||
|
||||
```js
|
||||
// some helper functions used here
|
||||
import { stringToU8a, u8aToHex } from '@polkadot/util';
|
||||
|
||||
...
|
||||
|
||||
// convert message, sign and then verify
|
||||
const message = stringToU8a('this is our message');
|
||||
const signature = alice.sign(message);
|
||||
const isValid = alice.verify(message, signature);
|
||||
|
||||
// log info
|
||||
console.log(`The signature ${u8aToHex(signature)}, is ${isValid ? '' : 'in'}valid`);
|
||||
```
|
||||
|
||||
This covers the keyring basics, however there are two additional functions here of interest, `keyring.getPairs()` to retrieve a list of all pairs in the keyring and `keyring.getPair(<address or publicKey>)` to retrieve a pair where we have an identifier.
|
||||
|
||||
## Back to transactions
|
||||
|
||||
Now that we have short introduction to the keyring, we can move back to API transactions and find out [how to subscribe and track events](api.tx.subs.md), taking our management of transactions to the next level.
|
||||
@@ -0,0 +1,67 @@
|
||||
# Type basics
|
||||
|
||||
We've touched upon types in most previous sections, i.e. that these are driven by metadata and that they are created and converted to/from automatically by the API. Since they appear in all results, we will divert a bit from the regularly scheduled program in explaining the API interfaces to giving some info on the base types.
|
||||
|
||||
## Everything is a type
|
||||
|
||||
Just to re-iterate from the above. Eveything returned by the API is a type and has a consistent interface. This means that a `Vec<u32>` (an array of `u32` values) as well as a `Struct` (an pre-defined object) or an `Enum` has the same consistent base interface. Specific types types will have values, based on the type - decorated and available.
|
||||
|
||||
As a minimum, anything returned by the API, be it a `Vec<...>`, `Option<...>`, `Struct` or any normal type will always have the following methods -
|
||||
|
||||
- `.eq(<other value>)` - checks for equality against the other value. In all cases, it will accept "like" values, i.e. in the case of a number you can pass a primitive (such as `1`), a hex value (such as `0x01`) or even an `Unit8Array`
|
||||
- `toHex()` - returns a hex-base representation of the value, always prefixed by `0x`
|
||||
- `toJSON()` - returns a JSON-like representation of the value, this is generally used when calling `JSON.stringify(...)` on the value
|
||||
- `toString()` - returns a string representation, in some cases this performs additional encoding, i.e. for `Address`, `AccountId` and `AccountIndex` it will encode to the ss58 address
|
||||
- `.toU8a()` - returns a `Uint8Array` representation of the encoded value (generally exactly as passed to the node, where values are SCALE encoded)
|
||||
|
||||
Additionally, the following getters and utilities are available -
|
||||
|
||||
- `.isEmpty` - `true` if the value is an all-empy value, i.e. `0` in for numbers, all-zero for Arrays (or anything `Uint8Array`), `false` is non-zero
|
||||
- `.hash` - a `Hash` (once again with all the methods above) that is a `blake2-256` representation of the contained value
|
||||
|
||||
## Working with numbers
|
||||
|
||||
All numbers wrap and extend an instance of [bn.js](https://github.com/indutny/bn.js/). This means that in addition to the interfaces defined above, they have some additional methods -
|
||||
|
||||
- `.toNumber()` - a JS number (limited to 2^53 - 1). This does mean that for large values, e.g. `Balance` (a `u128` extension), this can cause overflows
|
||||
- `.add(...)`, `.sub(...)`, ... - all the base methods available on the `BN` object
|
||||
|
||||
In cases where a `Compact` is returned, i.e. `Compact<Balance>`, the value is wrapped. This object should be `.unwrap()`-ed first to gain access to the underlying `Balance` object.
|
||||
|
||||
## Working with structures
|
||||
|
||||
All structures, a wrapping of an object containing a number of member variables, is an implementation of a standard JS `Map` object, so all the functions available on a `Map` such as `.entries()` are available. Additionally it is decorated with actual getters for the fields.
|
||||
|
||||
As an example, a `Header` will have getters for the `.parentHash`, `.number`, `.stateRoot`, `.extrinsicsRoot` and `.digest` fields. The same applies for all structures, as they are returned, each member will have an associated getter.
|
||||
|
||||
Be aware that in the JS version naming defaults to `camelCase` where names of fields in Substrate defaults to `snake_case`. (Each version aligning with conventions in the respective languages)
|
||||
|
||||
## Working with enums
|
||||
|
||||
Each enum has additional getters which are injected based on the fields wrapped. These take the form of `.is<Name>` and `.as<Name>` to allow you to check is the enum is a certain value or to retrieve the underlying value as a specific type.
|
||||
|
||||
As a real-world example, when an extrinsic is applied, the `Phase` enum has one of two states, `ApplyExtrinsic(u32)` or `Finalization`. In this case `.isApplyExtrinsic` would be `true` when an extrinsic is being applied, and `.asApplyExtrinsic` would return the value as a `u32` (which is the index of the extrinsic in the block, as it is being applied). When `isisApplyExtrinsic` is `false` and `asApplyExtrinsic` is called, the getter will throw.
|
||||
|
||||
## Working with Option<Type>
|
||||
|
||||
An `Option<Type>` attempts to mimic the Rust approach of having `None` and `Some` available. This means the following getters & methods are available on an `Option` -
|
||||
|
||||
- `.isNone` - is `true` if no underlying values is warpped, effectively the same as `.isEmpty`
|
||||
- `.isSome` - this is `true` is a value is wrapped, i.e. if a `Option<u32>` has an actual underlying `u32`
|
||||
- `.unwrap()` - when `isSome`, this will return the wrapped value, i.e. for `Option<u32>`, this would return the `u32`. When the value is `isNone`, this call will throw an exception.
|
||||
- `.unwrapOr(<default value>)` - this extends `unwrap()`, returning the wrapped value when `isSome` and in the case of `isNone` it will return the `<default value>` passed.
|
||||
|
||||
## Working with Tuples
|
||||
|
||||
A tuple is defined in the form of `(u32, AccountId)`. To access the individual values, you can access t via the index, i.e.
|
||||
|
||||
```js
|
||||
// assuming a tuple defined as `(32, AccountId)`
|
||||
const [count, accountId] = tuple;
|
||||
|
||||
console.log(`${accountId} has ${count.toNumber()} values`);
|
||||
```
|
||||
|
||||
## Extending types
|
||||
|
||||
For customized chains, the need exists to register types so the API is aware of how to decode values for those types. The next section will provide a [walk-through for the definition of custom types](types.extend.md) allowing the definition or re-definition of any type the API is aware of.
|
||||
@@ -0,0 +1,142 @@
|
||||
# Extending types
|
||||
|
||||
Circling back to metadata, by default the metadata information (at this point in time), only returns the type names as they apply to any section, be it a call, event or query. As an example, this means that transfers are defined as `balances.transfer(AccountId, Balance)` with no details as to the mapping of the `Balance` type to a `u128`. (The underlying Polkadot/Substrate default)
|
||||
|
||||
Therefore to cater for all types, a mapping in done on the [@polkadot/types library](https://github.com/polkadot-js/api/tree/master/packages/types/src/interfaces) to define each of the types and align with their underlying structures as it maps to a default Polkadot or Substrate chain.
|
||||
|
||||
Additionally, the API contains some logic for chain type detection, for instance in the case of Substrate 1.x based chains, it will define `BlockNumber` & `Index` (nonce) as a `u64`, while for current-generation chains, these will be defined as `u32`. Some of the work in maintaining the API for Polkadot/Substrate is the addition of types as they appear and gets used in the Rust codebases.
|
||||
|
||||
There is a the [recommentation](install.md#betas) to use a `@polkadot/api@beta` should you wish to track the master branches of Polkadot or Substrate, since master changes for the addition of new types do not make it into a stable release immediately.
|
||||
|
||||
## Extension
|
||||
|
||||
As a blockchain toolkit, Substrate makes it easy to add your own modules and types. In most non-trivial implementations, this would mean that developers are adding specific types for their implementation as well. The API will get to know the names of these types via the metadata, however it won't understand what they are, which means it cannot encode or decode them.
|
||||
|
||||
To close this gap, the API allows for the injection of types, i.e. you can explicitly define (or override) types for the node/chain you are connecting to. In the simplest example, assuming you have a chain where your `Balance` type is a `u64` (as opposed to the default `u128`), you need to let the API know -
|
||||
|
||||
```js
|
||||
...
|
||||
const api = await ApiPromise.create({
|
||||
provider: wsProvider,
|
||||
types: {
|
||||
Balance: 'u64'
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
The above introduces the `types` registry, effectively allowing overrides and the definition of new types. The override above would mean that immediately the API will treat all occurences of `Balance` not as the default, but rather as the defined size.
|
||||
|
||||
## User-defined types
|
||||
|
||||
Registration also applies to any type that can be found on a specific chain, i.e. we can add any types that is available on a specific node -
|
||||
|
||||
```js
|
||||
...
|
||||
const api = await ApiPromise.create({
|
||||
...,
|
||||
types: {
|
||||
TransactionInput: {
|
||||
parentOutput: 'Hash',
|
||||
signature: 'Signature'
|
||||
},
|
||||
TransactionOutput: {
|
||||
value: 'u128',
|
||||
pubkey: 'Hash',
|
||||
sale: 'u32'
|
||||
},
|
||||
Transaction: {
|
||||
inputs: 'Vec<TransactionInput>',
|
||||
outputs: 'Vec<TransactionOutput>'
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
The example above defines non-primitive types (as found in the specific implementation) as structs. Additionally it also shows the user-defined types can depend on other user-defined types with `Transaction` referencing both `TransactionInput` and `TransactionOutput`. Here you can reference any known types, i.e. in the above we have referenced primitives such as `u32` and `Signature` (itself an alias for `H512`).
|
||||
|
||||
One form of types that appear regularly is enums, these can be defined as follow -
|
||||
|
||||
```js
|
||||
...
|
||||
const api = await ApiPromise.create({
|
||||
...,
|
||||
types: {
|
||||
CLikeEnum: {
|
||||
_enum: ['One', 'Two', 'Three']
|
||||
},
|
||||
TypedEnum: {
|
||||
_enum: {
|
||||
One: 'Compact<u32>',
|
||||
Two: 'u64',
|
||||
Three: 'Option<Balance>',
|
||||
Four: null
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
As seen in these examples, types are built up in terms of primitives and aligns with the Rust-type definition model with `Compact`, `Option` and `Vec`.
|
||||
|
||||
## Node and chain-specific types
|
||||
|
||||
There are cases where a single API object can be used to connect to different types of nodes or chains, each including their own specific types. For these cases the `typesChain` and `typesSpec` injectors are made available.
|
||||
|
||||
As a real-world example, the [polkadot-js/apps UI](https://github.com/polkadot-js/apps) can connect to a variety of chains. To support [Edgeware](https://edgewa.re/) by default, the following node-type (`specName` as per the runtime version) overrides are made -
|
||||
|
||||
```js
|
||||
import { IdentityTypes } from 'edgeware-node-types/dist/identity';
|
||||
import { SignalingTypes } from 'edgeware-node-types/dist/signaling';
|
||||
import { VotingTypes } from 'edgeware-node-types/dist/voting';
|
||||
|
||||
...
|
||||
const api = await ApiPromise.create({
|
||||
...,
|
||||
typesSpec: {
|
||||
edgeware: {
|
||||
...IdentityTypes,
|
||||
...SignalingTypes,
|
||||
...VotingTypes
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
In the same way `typesChain` can be used to match on the actual chain name, i.e. for a chain such as Kusama, the following overrides can be made (as per example only - Kusama uses the Polkadot defaults, so no overrides are needed) -
|
||||
|
||||
```js
|
||||
...
|
||||
const api = await ApiPromise.create({
|
||||
...,
|
||||
typesChain: {
|
||||
'Kusama CC1': {
|
||||
BlockNumber: 'u32',
|
||||
Index: 'u32'
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
The `types`, `typesChain` and `typesSpec` overrides are all optional and all are applied, as applicable to a specific connection. From the options `types` are registered first, followed by `typesSpec` for node-specific overrides and finally `typesChain` for chain-specific overrides. The would mean is you have the following (contrived) example,
|
||||
|
||||
```js
|
||||
...
|
||||
const api = await ApiPromise.create({
|
||||
...,
|
||||
types: {
|
||||
Balance: 'u32',
|
||||
}
|
||||
typesChain: {
|
||||
Balance: 'u128'
|
||||
},
|
||||
typesSpec: {
|
||||
Balance: 'u64',
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
`Balance` would be defined as an `u128` at the end. Effectively based on the flow it is first registered as a `u32`, then overridden as a `u64` and finally overridden once more as a `u128` by the chain types.
|
||||
|
||||
## Using with TypeScript
|
||||
|
||||
The API is built with TypeScript (as are all projects in the [polkadot-js organization](https://github.com/polkadot-js/)) and as such allows developers using TS to have access to all the type interfaces defined on the chain, as well as having access to typings on interacting with the `api.*` namespaces. In the next section we will provide an overview of [what is available in terms of types and TypeScript](typescript.md).
|
||||
@@ -0,0 +1,56 @@
|
||||
# TypeScript interfaces
|
||||
|
||||
The API is written in TypeScript, and as such definitions for all actual exposed interfaces are available. In general terms, care has been taken to expose types via a `@polkadot/<package>/types` interface, for instance the `ApiOptions` type which is passed through on the `.create` interface is available under `@polkadot/api/types`.
|
||||
|
||||
## RPC interfaces
|
||||
|
||||
Before getting to the "hard things", i.e. methods as decorated based on metadata interfaces, let's take a look at more "static" interfaces such as RPC. (Be aware though that these can be customized on a per-chain basis as well - for now this functionality is not reflected in the API itself).
|
||||
|
||||
```js
|
||||
import { Header } from '@polkadot/types/interfaces';
|
||||
|
||||
...
|
||||
const firstHead = api.rpc.chain.getHeader();
|
||||
|
||||
api.rpc.chain.subscribeNewHeads((lastHead: Header): void => {
|
||||
console.log('current header:', JSON.stringify(header));
|
||||
});
|
||||
```
|
||||
|
||||
In the above example a couple of things are introduced - most of the chain definitions (the default types for both Polkadot & Substrate) can be imported as interfaces from the `@polkadot/types/interfaces` endpoint. These are not classes (since they are [generated from definitions](https://github.com/polkadot-js/api/tree/master/packages/types/src/interfaces)) but rather a combination of TypeScript `interfaces` (where structures are involved) and `type`, i.e. `type Balance = u128`.
|
||||
|
||||
In the subscription example, we explicitly define `lastHead: Header`, although the same definition is missing for `firstHead`. However, in both these cases the definitions for the `api.rpc` sections are such that TypeScript understands that `firstHead` and `lastHead` are of type `Header`. The `: Header` here is rather for our own understanding (and could be needed based on your eslint/tslint config).
|
||||
|
||||
As indicated, most of the Polkadot/Substrate default types are available via `types/interfaces`. However, for primitives types where there is an actual implementation, these are made available via `@polkadot/types` directly. For instance, `import { u32 } from '@polkadot/types` is valid in this context.
|
||||
|
||||
## Metadata injected
|
||||
|
||||
For any interface injected by metadata, the types are not available by default (although it may be in the future for default interfaces), but rather what the API understands is that all results need to comply to the `Codec` interface. (The bas of all out types)
|
||||
|
||||
However, to make this sane from a developer perspective the injected methods are generic, effectively making the following possible -
|
||||
|
||||
```js
|
||||
import { Balance, Index } from '@polkadot/types/interfaces';
|
||||
|
||||
...
|
||||
const nonce = await api.query.system.accountNonce<Index>(ADDR);
|
||||
const balance = await api.query.balances.freeBalance<Balance>(ADDR);
|
||||
```
|
||||
|
||||
In both these case we can instruct the TypeScript compiler that the type we are expecting in `Index` and `Balance` respectively, not just pure `Codec`. This means that functions like `.toNumber()` is available on both these types - as opposed to just the [general type defaults](types.basics.md#everything-is-a-type) with `.toHex()` and friends.
|
||||
|
||||
## Future work
|
||||
|
||||
As of this writing, there are still some gray areas to type detection, specifically around the following interfaces -
|
||||
|
||||
- `.at` & `.multi` on `api.query` does not (yet) have a `<TypeOverride>` interface. This means `as <TypeOverride>` casts are presently needed for these results
|
||||
- `api.queryMulti` does not (yet) allow you to provide a hint to the types returned, this ties to the previous point
|
||||
|
||||
In additiont to expanding the type covereage, we wish to make the actual generation script for the types from `@polkadot/types/interfaces` available in 2 ways -
|
||||
|
||||
- allowing you to point to a folder of types and auto-generate the TypeScript typings from those. (Which is akin to what we do internally). This would allow a reduction in type classes explicitly written and injected.
|
||||
- once the metadata itself supports full type definitions, the script can be used to generate interface definitions specifically tailored for a chain
|
||||
|
||||
## And it is a wrap
|
||||
|
||||
This brings us to the end of our overview and jump through the API. While the documentation is still very much and ever evolving item, we can encourage you to try out what you have learned with some [examples](../examples). As we [indicated right at the start of this journey](README.md#help-us-help-others), if there are areas for improvement, let us know.
|
||||
@@ -0,0 +1,3 @@
|
||||
# Substrate
|
||||
|
||||
As part of a running node, some information is exposed as part of the metadata.
|
||||
@@ -1,6 +1,8 @@
|
||||
## Constants
|
||||
|
||||
_The following sections contain the module constants, also known as parameter types.
|
||||
The following sections contain the module constants, also known as parameter types. These can only be changed as part of a runtime upgrade. On the api, these are exposed via `api.consts.<module>.<method>`.
|
||||
|
||||
(NOTE: These were generated from a static/snapshot view of a recent Substrate master node. Some items may not be available in older nodes, or in any customized implementations.)
|
||||
- **[babe](#babe)**
|
||||
|
||||
- **[balances](#balances)**
|
||||
@@ -1,6 +1,8 @@
|
||||
## Events
|
||||
|
||||
Events are emitted for certain operations on the runtime. The following sections describe the events that are part of the default Substrate runtime.
|
||||
Events are emitted for certain operations on the runtime. The following sections describe the events that are part of the default Substrate runtime.
|
||||
|
||||
(NOTE: These were generated from a static/snapshot view of a recent Substrate master node. Some items may not be available in older nodes, or in any customized implementations.)
|
||||
- **[balances](#balances)**
|
||||
|
||||
- **[contracts](#contracts)**
|
||||
@@ -143,7 +145,7 @@ ___
|
||||
|
||||
### grandpa
|
||||
|
||||
▸ **NewAuthorities**(`Vec<(AuthorityId,u64)>`)
|
||||
▸ **NewAuthorities**(`Vec<(AuthorityId,AuthorityWeight)>`)
|
||||
- **summary**: New authority set has been applied.
|
||||
|
||||
▸ **Paused**()
|
||||
@@ -214,7 +216,7 @@ ___
|
||||
|
||||
### system
|
||||
|
||||
▸ **ExtrinsicFailed**()
|
||||
▸ **ExtrinsicFailed**(`DispatchError`)
|
||||
- **summary**: An extrinsic failed.
|
||||
|
||||
▸ **ExtrinsicSuccess**()
|
||||
@@ -1,6 +1,8 @@
|
||||
## Extrinsics
|
||||
|
||||
_The following sections contain Extrinsics methods are part of the default Substrate runtime._
|
||||
The following sections contain Extrinsics methods are part of the default Substrate runtime. On the api, these are exposed via `api.tx.<module>.<method>`.
|
||||
|
||||
(NOTE: These were generated from a static/snapshot view of a recent Substrate master node. Some items may not be available in older nodes, or in any customized implementations.)
|
||||
- **[authorship](#authorship)**
|
||||
|
||||
- **[babe](#babe)**
|
||||
@@ -59,6 +61,9 @@ ___
|
||||
|
||||
### balances
|
||||
|
||||
▸ **forceTransfer**(source: `Address`, dest: `Address`, value: `Compact<Balance>`)
|
||||
- **summary**: Exactly as `transfer`, except the origin must be root and the source account may be specified.
|
||||
|
||||
▸ **setBalance**(who: `Address`, new_free: `Compact<Balance>`, new_reserved: `Compact<Balance>`)
|
||||
- **summary**: Set the balances of a given account. This will alter `FreeBalance` and `ReservedBalance` in storage. it will also decrease the total issuance of the system (`TotalIssuance`). If the new free or reserved balance is below the existential deposit, it will reset the account nonce (`system::AccountNonce`). The dispatch origin for this call is `root`. # <weight> - Independent of the arguments. - Contains a limited number of reads and writes. # </weight>
|
||||
|
||||
@@ -214,7 +219,7 @@ ___
|
||||
|
||||
### imOnline
|
||||
|
||||
▸ **heartbeat**(heartbeat: `Heartbeat`, signature: `AuthoritySignature`)
|
||||
▸ **heartbeat**(heartbeat: `Heartbeat`, signature: `Signature`)
|
||||
|
||||
___
|
||||
|
||||
@@ -230,7 +235,7 @@ ___
|
||||
### staking
|
||||
|
||||
▸ **bond**(controller: `Address`, value: `Compact<BalanceOf>`, payee: `RewardDestination`)
|
||||
- **summary**: Take the origin account as a stash and lock up `value` of its balance. `controller` will be the account that controls it. `value` must be more than the `existential_deposit` defined in the Balances module. The dispatch origin for this call must be _Signed_ by the stash account. # <weight> - Independent of the arguments. Moderate complexity. - O(1). - Three extra DB entries. NOTE: Two of the storage writes (`Self::bonded`, `Self::payee`) are _never_ cleaned unless the `origin` falls below _existential deposit_ and gets removed as dust. # </weight>
|
||||
- **summary**: Take the origin account as a stash and lock up `value` of its balance. `controller` will be the account that controls it. `value` must be more than the `minimum_balance` specified by `T::Currency`. The dispatch origin for this call must be _Signed_ by the stash account. # <weight> - Independent of the arguments. Moderate complexity. - O(1). - Three extra DB entries. NOTE: Two of the storage writes (`Self::bonded`, `Self::payee`) are _never_ cleaned unless the `origin` falls below _existential deposit_ and gets removed as dust. # </weight>
|
||||
|
||||
▸ **bondExtra**(max_additional: `Compact<BalanceOf>`)
|
||||
- **summary**: Add some extra amount that have appeared in the stash `free_balance` into the balance up for staking. Use this if there are additional funds in your stash account that you wish to bond. Unlike [`bond`] or [`unbond`] this function does not impose any limitation on the amount that can be added. The dispatch origin for this call must be _Signed_ by the stash, not the controller. # <weight> - Independent of the arguments. Insignificant complexity. - O(1). - One DB entry. # </weight>
|
||||
@@ -260,7 +265,7 @@ ___
|
||||
- **summary**: The ideal number of validators.
|
||||
|
||||
▸ **unbond**(value: `Compact<BalanceOf>`)
|
||||
- **summary**: Schedule a portion of the stash to be unlocked ready for transfer out after the bond period ends. If this leaves an amount actively bonded less than T::Currency::existential_deposit(), then it is increased to the full amount. Once the unlock period is done, you can call `withdraw_unbonded` to actually move the funds out of management ready for transfer. No more than a limited number of unlocking chunks (see `MAX_UNLOCKING_CHUNKS`) can co-exists at the same time. In that case, [`Call::withdraw_unbonded`] need to be called first to remove some of the chunks (if possible). The dispatch origin for this call must be _Signed_ by the controller, not the stash. See also [`Call::withdraw_unbonded`]. # <weight> - Independent of the arguments. Limited but potentially exploitable complexity. - Contains a limited number of reads. - Each call (requires the remainder of the bonded balance to be above `minimum_balance`) will cause a new entry to be inserted into a vector (`Ledger.unlocking`) kept in storage. The only way to clean the aforementioned storage item is also user-controlled via `withdraw_unbonded`. - One DB entry. </weight>
|
||||
- **summary**: Schedule a portion of the stash to be unlocked ready for transfer out after the bond period ends. If this leaves an amount actively bonded less than T::Currency::minimum_balance(), then it is increased to the full amount. Once the unlock period is done, you can call `withdraw_unbonded` to actually move the funds out of management ready for transfer. No more than a limited number of unlocking chunks (see `MAX_UNLOCKING_CHUNKS`) can co-exists at the same time. In that case, [`Call::withdraw_unbonded`] need to be called first to remove some of the chunks (if possible). The dispatch origin for this call must be _Signed_ by the controller, not the stash. See also [`Call::withdraw_unbonded`]. # <weight> - Independent of the arguments. Limited but potentially exploitable complexity. - Contains a limited number of reads. - Each call (requires the remainder of the bonded balance to be above `minimum_balance`) will cause a new entry to be inserted into a vector (`Ledger.unlocking`) kept in storage. The only way to clean the aforementioned storage item is also user-controlled via `withdraw_unbonded`. - One DB entry. </weight>
|
||||
|
||||
▸ **validate**(prefs: `ValidatorPrefs`)
|
||||
- **summary**: Declare the desire to validate for the origin controller. Effects will be felt at the beginning of the next era. The dispatch origin for this call must be _Signed_ by the controller, not the stash. # <weight> - Independent of the arguments. Insignificant complexity. - Contains a limited number of reads. - Writes are limited to the `origin` account key. # </weight>
|
||||
@@ -1,6 +1,7 @@
|
||||
## JSON-RPC
|
||||
|
||||
_The following sections contain RPC methods that are Remote Calls available by default and allow you to interact with the actual node, query, and submit. The RPCs are provided by Substrate itself._
|
||||
The following sections contain RPC methods that are Remote Calls available by default and allow you to interact with the actual node, query, and submit.
|
||||
|
||||
- **[author](#author)**
|
||||
|
||||
- **[chain](#chain)**
|
||||
@@ -17,7 +18,7 @@ ___
|
||||
|
||||
_Authoring of network items_
|
||||
|
||||
▸ **insertKey**(keyType: `Text`, suri: `Text`, maybePublic?: `Bytes`): `Bytes`
|
||||
▸ **insertKey**(keyType: `Text`, suri: `Text`, publicKey: `Bytes`): `Bytes`
|
||||
- **summary**: Insert a key into the keystore.
|
||||
|
||||
▸ **pendingExtrinsics**(): `Vec<Extrinsic>`
|
||||
@@ -54,18 +55,12 @@ _Retrieval of chain data_
|
||||
▸ **getHeader**(hash?: `Hash`): `Header`
|
||||
- **summary**: Retrieves the header for a specific block
|
||||
|
||||
▸ **getRuntimeVersion**(hash?: `Hash`): `RuntimeVersion`
|
||||
- **summary**: Get the runtime version (alias of state_getRuntimeVersion)
|
||||
|
||||
▸ **subscribeFinalizedHeads**(): `Header`
|
||||
- **summary**: Retrieves the best finalized header via subscription
|
||||
|
||||
▸ **subscribeNewHeads**(): `Header`
|
||||
- **summary**: Retrieves the best header via subscription
|
||||
|
||||
▸ **subscribeRuntimeVersion**(): `RuntimeVersion`
|
||||
- **summary**: Retrieves the runtime version via subscription
|
||||
|
||||
___
|
||||
|
||||
|
||||
@@ -109,6 +104,9 @@ _Query of state_
|
||||
▸ **queryStorage**(keys: `Vec<StorageKey>`, startBlock: `Hash`, block?: `Hash`): `Vec<StorageChangeSet>`
|
||||
- **summary**: Query historical storage entries (by key) starting from a start block
|
||||
|
||||
▸ **subscribeRuntimeVersion**(): `RuntimeVersion`
|
||||
- **summary**: Retrieves the runtime version via subscription
|
||||
|
||||
▸ **subscribeStorage**(keys: `Vec<StorageKey>`): `StorageChangeSet`
|
||||
- **summary**: Subscribes to storage changes for the provided keys
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
## Storage
|
||||
|
||||
_The following sections contain Storage methods are part of the default Substrate runtime._
|
||||
The following sections contain Storage methods are part of the default Substrate runtime. On the api, these are exposed via `api.query.<module>.<method>`.
|
||||
|
||||
(NOTE: These were generated from a static/snapshot view of a recent Substrate master node. Some items may not be available in older nodes, or in any customized implementations.)
|
||||
- **[authorship](#authorship)**
|
||||
|
||||
- **[babe](#babe)**
|
||||
@@ -270,12 +272,18 @@ ___
|
||||
▸ **authorities**(): `Vec<(AuthorityId,AuthorityWeight)>`
|
||||
- **summary**: The current authority set.
|
||||
|
||||
▸ **currentSetId**(): `SetId`
|
||||
- **summary**: The number of changes (both in terms of keys and underlying economic responsibilities) in the "set" of Grandpa validators from genesis.
|
||||
|
||||
▸ **nextForced**(): `Option<BlockNumber>`
|
||||
- **summary**: next block number where we can force a change.
|
||||
|
||||
▸ **pendingChange**(): `Option<StoredPendingChange>`
|
||||
- **summary**: Pending change: (signaled at, scheduled change).
|
||||
|
||||
▸ **setIdSession**(`SetId`): `Option<SessionIndex>`
|
||||
- **summary**: A mapping from grandpa set ID to the index of the *most recent* session for which its members were responsible.
|
||||
|
||||
▸ **stalled**(): `Option<(BlockNumber,BlockNumber)>`
|
||||
- **summary**: `true` if we are currently stalled.
|
||||
|
||||
@@ -326,9 +334,6 @@ ___
|
||||
|
||||
### session
|
||||
|
||||
▸ **changed**(): `bool`
|
||||
- **summary**: True if anything has changed in this session.
|
||||
|
||||
▸ **currentIndex**(): `SessionIndex`
|
||||
- **summary**: Current index of the session.
|
||||
|
||||
@@ -339,7 +344,7 @@ ___
|
||||
- **summary**: The next session keys for a validator. The first key is always `DEDUP_KEY_PREFIX` to have all the data in the same branch of the trie. Having all data in the same branch should prevent slowing down other queries.
|
||||
|
||||
▸ **queuedChanged**(): `bool`
|
||||
- **summary**: Queued keys changed.
|
||||
- **summary**: True if the underlying economic identities or weighting behind the validators has changed in the queued validator set.
|
||||
|
||||
▸ **queuedKeys**(): `Vec<(ValidatorId,Keys)>`
|
||||
- **summary**: The queued keys for the next session. When the next session begins, these keys will be used to determine the validator's session keys.
|
||||
@@ -364,7 +369,7 @@ ___
|
||||
▸ **currentEra**(): `EraIndex`
|
||||
- **summary**: The current era index.
|
||||
|
||||
▸ **currentEraRewards**(): `EraRewards`
|
||||
▸ **currentEraPointsEarned**(): `EraPoints`
|
||||
- **summary**: Rewards for the current era. Using indices of current elected set.
|
||||
|
||||
▸ **currentEraStart**(): `MomentOf`
|
||||
+1
-1
@@ -9,5 +9,5 @@
|
||||
"packages": [
|
||||
"packages/*"
|
||||
],
|
||||
"version": "0.90.1"
|
||||
"version": "0.92.1"
|
||||
}
|
||||
|
||||
+8
-7
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "0.90.1",
|
||||
"version": "0.92.1",
|
||||
"private": true,
|
||||
"engines": {
|
||||
"yarn": "^1.10.1"
|
||||
@@ -9,13 +9,14 @@
|
||||
],
|
||||
"resolutions": {
|
||||
"babel-core": "^7.0.0-bridge.0",
|
||||
"typescript": "^3.5.3"
|
||||
"typescript": "^3.6.3"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "yarn build:interfaces && polkadot-dev-build-ts && yarn run build:methodsdoc && polkadot-dev-build-docs",
|
||||
"build:htmldoc": "yarn clean && typedoc --theme default --out docs/html",
|
||||
"build:interfaces": "node packages/types/src/scripts/interfacesTsWrapper.js",
|
||||
"build:methodsdoc": "node packages/types/src/scripts/MetadataMdWrapper.js",
|
||||
"chain:info": "node packages/types/src/scripts/extractChainWrapper.js",
|
||||
"check": "yarn lint",
|
||||
"lint": "eslint --ext .js,.jsx,.ts,.tsx . && tsc --noEmit --pretty",
|
||||
"clean": "polkadot-dev-clean-build",
|
||||
@@ -29,11 +30,11 @@
|
||||
"test:watch": "jest --coverage --watch"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.5.5",
|
||||
"@babel/register": "^7.5.5",
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"@polkadot/dev": "^0.31.0-beta.3",
|
||||
"@polkadot/ts": "^0.1.64",
|
||||
"@babel/core": "^7.6.0",
|
||||
"@babel/register": "^7.6.0",
|
||||
"@babel/runtime": "^7.6.0",
|
||||
"@polkadot/dev": "^0.31.0-beta.8",
|
||||
"@polkadot/ts": "^0.1.72",
|
||||
"gh-pages": "^2.1.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@polkadot/api-contract",
|
||||
"version": "0.90.1",
|
||||
"version": "0.92.1",
|
||||
"description": "Interfaces for interacting with contracts and contract ABIs",
|
||||
"main": "index.js",
|
||||
"keywords": [
|
||||
@@ -26,7 +26,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/polkadot-js/api/tree/master/packages/api-contract#readme",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"@polkadot/types": "^0.90.1"
|
||||
"@babel/runtime": "^7.6.0",
|
||||
"@polkadot/types": "^0.92.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,6 @@ export default abstract class RxBase implements ContractBase<'rxjs'> {
|
||||
// cater for substrate 2.x & 1.x (in order)
|
||||
this.apiContracts = api.tx.contracts || api.tx.contract;
|
||||
|
||||
assert(this.apiContracts && this.apiContracts.putCode, `You need to connect to a node with the contracts module, the metadata does not enable api.tx.contracts on this instance`);
|
||||
assert(this.apiContracts && this.apiContracts.putCode, 'You need to connect to a node with the contracts module, the metadata does not enable api.tx.contracts on this instance');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { ISubmittableResult, SubmittableResult } from '@polkadot/api/SubmittableExtrinsic';
|
||||
import { SubmittableResultImpl } from '@polkadot/api/types';
|
||||
import { AccountId, Address, Hash } from '@polkadot/types/interfaces';
|
||||
import { IKeyringPair } from '@polkadot/types/types';
|
||||
import { ContractABI } from './types';
|
||||
@@ -10,7 +10,7 @@ import { ContractABI } from './types';
|
||||
import BN from 'bn.js';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { ApiRx } from '@polkadot/api';
|
||||
import { ApiRx, SubmittableResult } from '@polkadot/api';
|
||||
import { createType } from '@polkadot/types';
|
||||
|
||||
import Abi from './Abi';
|
||||
@@ -26,7 +26,7 @@ export interface BlueprintCreate {
|
||||
class BlueprintCreateResult extends SubmittableResult {
|
||||
public readonly contract?: RxContract;
|
||||
|
||||
public constructor (result: ISubmittableResult, contract?: RxContract) {
|
||||
public constructor (result: SubmittableResultImpl, contract?: RxContract) {
|
||||
super(result);
|
||||
|
||||
this.contract = contract;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { ISubmittableResult, SubmittableResult } from '@polkadot/api/SubmittableExtrinsic';
|
||||
import { SubmittableResultImpl } from '@polkadot/api/types';
|
||||
import { AccountId, Address, Hash } from '@polkadot/types/interfaces';
|
||||
import { IKeyringPair } from '@polkadot/types/types';
|
||||
import { ContractABI } from './types';
|
||||
@@ -10,7 +10,7 @@ import { ContractABI } from './types';
|
||||
import BN from 'bn.js';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { ApiRx } from '@polkadot/api';
|
||||
import { ApiRx, SubmittableResult } from '@polkadot/api';
|
||||
import { compactAddLength, u8aToU8a } from '@polkadot/util';
|
||||
|
||||
import Abi from './Abi';
|
||||
@@ -39,7 +39,7 @@ export interface CodePutCode {
|
||||
class CodePutCodeResult extends SubmittableResult {
|
||||
public readonly blueprint?: RxBlueprint;
|
||||
|
||||
public constructor (result: ISubmittableResult, blueprint?: RxBlueprint) {
|
||||
public constructor (result: SubmittableResultImpl, blueprint?: RxBlueprint) {
|
||||
super(result);
|
||||
|
||||
this.blueprint = blueprint;
|
||||
@@ -67,7 +67,7 @@ export default class RxCode extends RxBase {
|
||||
return { signAndSend };
|
||||
}
|
||||
|
||||
private createResult = (result: ISubmittableResult): CodePutCodeResult => {
|
||||
private createResult = (result: SubmittableResultImpl): CodePutCodeResult => {
|
||||
let blueprint: RxBlueprint | undefined;
|
||||
|
||||
if (result.isFinalized) {
|
||||
|
||||
@@ -8,8 +8,7 @@ import { ContractABI, ContractABIFn, InterfaceContract, InterfaceContractCalls }
|
||||
|
||||
import BN from 'bn.js';
|
||||
import { Observable } from 'rxjs';
|
||||
import { ApiRx } from '@polkadot/api';
|
||||
import { SubmittableResult } from '@polkadot/api/SubmittableExtrinsic';
|
||||
import { ApiRx, SubmittableResult } from '@polkadot/api';
|
||||
import { createType } from '@polkadot/types';
|
||||
import Abi from './Abi';
|
||||
import RxBase from './RxBase';
|
||||
|
||||
@@ -16,7 +16,7 @@ export function typeToString (type: ContractABITypes): string {
|
||||
} else if (type['Option<T>']) {
|
||||
return `Option<${typeToString(type['Option<T>'].T)}>`;
|
||||
} else if (type['Result<T,E>']) {
|
||||
return `()`; // Result is not supported, but only applicable for returns
|
||||
return '()'; // Result is not supported, but only applicable for returns
|
||||
} else if (type['Vec<T>']) {
|
||||
return `Vec<${typeToString(type['Vec<T>'].T)}>`;
|
||||
} else if (type['[T;n]']) {
|
||||
|
||||
@@ -2,8 +2,8 @@ A breaking change was introduced by substrate runtime spec_version 97. https://g
|
||||
|
||||
The change had to be implemented in ink! which changed the structure of the Wasm files.
|
||||
|
||||
The Polkadot JS API is only supporting srml-contract and INK! versions after than spec_version 97.
|
||||
The Polkadot JS API is only supporting srml-contract and ink! versions after than spec_version 97.
|
||||
|
||||
**Compatibility**
|
||||
If the substrate version is older than this https://github.com/paritytech/substrate/pull/2911 it will only work
|
||||
with contracts generated by an ink! version prior to https://github.com/paritytech/ink/pull/129 and vice versa.
|
||||
with contracts generated by an ink! version prior to https://github.com/paritytech/ink/pull/129 and vice versa.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@polkadot/api-derive",
|
||||
"version": "0.90.1",
|
||||
"version": "0.92.1",
|
||||
"description": "Common functions used across Polkadot, derived from RPC calls and storage queries.",
|
||||
"main": "index.js",
|
||||
"keywords": [
|
||||
@@ -27,11 +27,11 @@
|
||||
},
|
||||
"homepage": "https://github.com/polkadot-js/api/tree/master/packages/api-derive#readme",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"@polkadot/api": "^0.90.1",
|
||||
"@polkadot/types": "^0.90.1"
|
||||
"@babel/runtime": "^7.6.0",
|
||||
"@polkadot/api": "^0.92.1",
|
||||
"@polkadot/types": "^0.92.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@polkadot/keyring": "^1.1.1"
|
||||
"@polkadot/keyring": "^1.4.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ export function getHeader (api: ApiInterfaceRx): (hash: Uint8Array | string) =>
|
||||
// where rpc.chain.getHeader throws, we will land here - it can happen that
|
||||
// we supplied an invalid hash. (Due to defaults, storeage will have an
|
||||
// empty value, so only the RPC is affected). So return undefined
|
||||
of() as Observable<undefined>
|
||||
of()
|
||||
),
|
||||
drr()
|
||||
);
|
||||
|
||||
@@ -21,8 +21,8 @@ export type HeaderAndValidators = [Header, AccountId[]];
|
||||
* <BR>
|
||||
*
|
||||
* ```javascript
|
||||
* api.derive.chain.subscribeNewHeads(({ author, blockNumber }) => {
|
||||
* console.log(`block #${blockNumber} was authored by ${author}`);
|
||||
* api.derive.chain.subscribeNewHeads((header) => {
|
||||
* console.log(`block #${header.number} was authored by ${header.author}`);
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
|
||||
@@ -23,6 +23,7 @@ export function controllers (api: ApiInterfaceRx): () => Observable<[AccountId[]
|
||||
switchMap(([stashIds]): Observable<[AccountId[], Option<AccountId>[]]> =>
|
||||
combineLatest([
|
||||
of(stashIds),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||
api.query.staking.bonded.multi(stashIds) as Observable<Option<AccountId>[]>
|
||||
])
|
||||
),
|
||||
|
||||
@@ -48,19 +48,14 @@ function remainingBlocks (era: BN, eraLength: BN, bestNumber: BlockNumber): BN {
|
||||
: remaining;
|
||||
}
|
||||
|
||||
// select the Unlockchunks that can't be redeemed yet
|
||||
function calcChunks (stakingLedger: StakingLedger, eraLength: BN, bestNumber: BlockNumber): UnlockChunk[] {
|
||||
return stakingLedger.unlocking.filter((chunk): boolean =>
|
||||
remainingBlocks(chunk.era.unwrap(), eraLength, bestNumber).gtn(0)
|
||||
);
|
||||
}
|
||||
|
||||
function calculateUnlocking (stakingLedger: StakingLedger | undefined, eraLength: BN, bestNumber: BlockNumber): DerivedUnlocking | undefined {
|
||||
if (isUndefined(stakingLedger)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const unlockingChunks = calcChunks(stakingLedger, eraLength, bestNumber);
|
||||
const unlockingChunks = stakingLedger.unlocking.filter(({ era }): boolean =>
|
||||
remainingBlocks(era.unwrap(), eraLength, bestNumber).gtn(0)
|
||||
);
|
||||
|
||||
if (!unlockingChunks.length) {
|
||||
return undefined;
|
||||
@@ -81,9 +76,11 @@ function redeemableSum (stakingLedger: StakingLedger | undefined, eraLength: BN,
|
||||
return new BN(0);
|
||||
}
|
||||
|
||||
return calcChunks(stakingLedger, eraLength, bestNumber).reduce((curr, prev): BN =>
|
||||
curr.add(prev.value.unwrap()), new BN(0)
|
||||
);
|
||||
return stakingLedger.unlocking.reduce((total, { era, value }): BN => {
|
||||
return remainingBlocks(era.unwrap(), eraLength, bestNumber).eqn(0)
|
||||
? total.add(value.unwrap())
|
||||
: total;
|
||||
}, new BN(0));
|
||||
}
|
||||
|
||||
function unwrapSessionIds (stashId: AccountId, queuedKeys: Option<AccountId> | Vec<[AccountId, Keys] & Codec>, nextKeys: Option<Keys>): { nextSessionIds: AccountId[]; nextSessionId?: AccountId; sessionIds: AccountId[]; sessionId?: AccountId } {
|
||||
|
||||
@@ -8,8 +8,8 @@ import { AnyJsonObject, Constructor } from '@polkadot/types/types';
|
||||
import runtimeTypes from '@polkadot/types/interfaces/runtime/definitions';
|
||||
import { Struct } from '@polkadot/types';
|
||||
|
||||
// @ts-ignore We can ignore the properties, added via Struct.with
|
||||
const _Header: Constructor<Header> = Struct.with(runtimeTypes.types.Header);
|
||||
// We can ignore the properties, added via Struct.with
|
||||
const _Header: Constructor<Header> = Struct.with(runtimeTypes.types.Header as any) as any;
|
||||
|
||||
/**
|
||||
* @name HeaderExtended
|
||||
|
||||
@@ -9,8 +9,8 @@ import BN from 'bn.js';
|
||||
import democracyTypes from '@polkadot/types/interfaces/democracy/definitions';
|
||||
import { Struct, createType } from '@polkadot/types';
|
||||
|
||||
// @ts-ignore We can ignore the properties, added via Struct.with
|
||||
const _ReferendumInfo: Constructor<ReferendumInfo> = Struct.with(democracyTypes.types.ReferendumInfo);
|
||||
// We can ignore the properties, added via Struct.with
|
||||
const _ReferendumInfo: Constructor<ReferendumInfo> = Struct.with(democracyTypes.types.ReferendumInfo as any) as any;
|
||||
|
||||
/**
|
||||
* @name ReferendumInfoExtended
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@polkadot/api-metadata",
|
||||
"version": "0.90.1",
|
||||
"version": "0.92.1",
|
||||
"description": "Helpers to extract information from runtime metadata",
|
||||
"main": "index.js",
|
||||
"publishConfig": {
|
||||
@@ -26,12 +26,12 @@
|
||||
},
|
||||
"homepage": "https://github.com/polkadot-js/api/tree/master/packages/type-metadata#readme",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"@polkadot/types": "^0.90.1",
|
||||
"@polkadot/util": "^1.1.1",
|
||||
"@polkadot/util-crypto": "^1.1.1"
|
||||
"@babel/runtime": "^7.6.0",
|
||||
"@polkadot/types": "^0.92.1",
|
||||
"@polkadot/util": "^1.4.1",
|
||||
"@polkadot/util-crypto": "^1.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@polkadot/keyring": "^1.1.1"
|
||||
"@polkadot/keyring": "^1.4.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,6 @@ const consts = fromMetadata(metadata);
|
||||
describe('fromMetadata', (): void => {
|
||||
it('should return constants with the correct type and value', (): void => {
|
||||
expect(consts.democracy.cooloffPeriod).toBeInstanceOf(ClassOf('BlockNumber'));
|
||||
expect(consts.democracy.cooloffPeriod.toHex()).toEqual('0x00062700');
|
||||
expect(consts.democracy.cooloffPeriod.toHex()).toEqual('0x000c4e00');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -95,7 +95,7 @@ describe('createFunction', (): void => {
|
||||
it('needs two arguments', (): void => {
|
||||
expect(
|
||||
(): Uint8Array => storageFn(['5DXUeE5N5LtkW97F2PzqYPyqNkxqSWESdGSPTX6AvkUAhwKP'])
|
||||
).toThrow(/metaName expects two arguments/);
|
||||
).toThrow(/requires two arguments/);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -27,75 +27,75 @@ export interface CreateItemFn {
|
||||
|
||||
type CreateArgType = boolean | string | number | null | BN | Uint8Array | Codec;
|
||||
|
||||
/**
|
||||
* From the schema of a function in the module's storage, generate the function
|
||||
* that will return the correct storage key.
|
||||
*
|
||||
* @param schema - The function's definition schema to create the function from.
|
||||
* The schema is taken from state_getMetadata.
|
||||
* @param options - Additional options when creating the function. These options
|
||||
* are not known at runtime (from state_getMetadata), they need to be supplied
|
||||
* by us manually at compile time.
|
||||
*/
|
||||
export default function createFunction ({ meta, method, prefix, section }: CreateItemFn, options: CreateItemOptions = {}): StorageEntry {
|
||||
const NULL_HASHER = (value: Uint8Array): Uint8Array => value;
|
||||
|
||||
// with the prefix, method & options, create both the string & raw keys
|
||||
function createKeys ({ method, prefix }: CreateItemFn, options: CreateItemOptions): [string, Uint8Array] {
|
||||
const stringKey = options.key
|
||||
? options.key
|
||||
: `${prefix} ${method}`;
|
||||
const rawKey = stringToU8a(stringKey);
|
||||
|
||||
// Get the hashing function
|
||||
let hasher: HasherFunction;
|
||||
let key2Hasher: HasherFunction;
|
||||
return [
|
||||
stringKey,
|
||||
stringToU8a(stringKey)
|
||||
];
|
||||
}
|
||||
|
||||
if (meta.type.isDoubleMap) {
|
||||
hasher = getHasher(meta.type.asDoubleMap.hasher);
|
||||
key2Hasher = getHasher(meta.type.asDoubleMap.key2Hasher);
|
||||
} else if (meta.type.isMap) {
|
||||
hasher = getHasher(meta.type.asMap.hasher);
|
||||
} else {
|
||||
hasher = getHasher();
|
||||
// get the hashers, the base (and in the case of DoubleMap), the second key
|
||||
function getHashers ({ meta: { type } }: CreateItemFn): [HasherFunction, HasherFunction?] {
|
||||
if (type.isDoubleMap) {
|
||||
return [
|
||||
getHasher(type.asDoubleMap.hasher),
|
||||
getHasher(type.asDoubleMap.key2Hasher)
|
||||
];
|
||||
} else if (type.isMap) {
|
||||
return [getHasher(type.asMap.hasher)];
|
||||
}
|
||||
|
||||
// Can only have zero or one argument:
|
||||
// - storage.balances.freeBalance(address)
|
||||
// - storage.timestamp.blockPeriod()
|
||||
// For doublemap queries the params is passed in as an tuple, [key1, key2]
|
||||
const _storageFn = (arg?: CreateArgType | [CreateArgType?, CreateArgType?]): Uint8Array => {
|
||||
let key = rawKey;
|
||||
// the default
|
||||
return [getHasher()];
|
||||
}
|
||||
|
||||
if (meta.type.isDoubleMap) {
|
||||
assert(Array.isArray(arg) && !isUndefined(arg[0]) && !isNull(arg[0]) && !isUndefined(arg[1]) && !isNull(arg[1]), `${meta.name} expects two arguments`);
|
||||
// create a key for a DoubleMap type
|
||||
function createKeyDoubleMap ({ meta: { name, type } }: CreateItemFn, rawKey: Uint8Array, args: [CreateArgType, CreateArgType], [hasher, key2Hasher]: [HasherFunction, HasherFunction?]): Uint8Array {
|
||||
// since we are passing an almost-unknown through, trust, but verify
|
||||
assert(
|
||||
Array.isArray(args) && !isUndefined(args[0]) && !isNull(args[0]) && !isUndefined(args[1]) && !isNull(args[1]),
|
||||
`${name} is a DoubleMap and requires two arguments`
|
||||
);
|
||||
|
||||
// we have checked that it is an array in the assert, so all ok
|
||||
const [key1, key2] = arg as [CreateArgType, CreateArgType];
|
||||
const type1 = meta.type.asDoubleMap.key1.toString();
|
||||
const type2 = meta.type.asDoubleMap.key2.toString();
|
||||
const param1Encoded = u8aConcat(key, createTypeUnsafe(type1, [key1]).toU8a(true));
|
||||
const param1Hashed = hasher(param1Encoded);
|
||||
const param2Hashed = key2Hasher(createTypeUnsafe(type2, [key2]).toU8a(true));
|
||||
const [key1, key2] = args;
|
||||
const type1 = type.asDoubleMap.key1.toString();
|
||||
const type2 = type.asDoubleMap.key2.toString();
|
||||
const param1Encoded = u8aConcat(rawKey, createTypeUnsafe(type1, [key1]).toU8a(true));
|
||||
const param1Hashed = hasher(param1Encoded);
|
||||
|
||||
return Compact.addLengthPrefix(u8aConcat(param1Hashed, param2Hashed));
|
||||
}
|
||||
// If this fails it means the getHashers function failed - and we have much bigger issues
|
||||
const param2Hashed = (key2Hasher as HasherFunction)(createTypeUnsafe(type2, [key2]).toU8a(true));
|
||||
|
||||
if (meta.type.isMap) {
|
||||
assert(!isUndefined(arg) && !isNull(arg), `${meta.name} expects one argument`);
|
||||
// as per createKey, always add the length prefix (underlying it is Bytes)
|
||||
return Compact.addLengthPrefix(u8aConcat(param1Hashed, param2Hashed));
|
||||
}
|
||||
|
||||
const type = meta.type.asMap.key.toString();
|
||||
const param = createTypeUnsafe(type, [arg]).toU8a();
|
||||
// create a key for either a map or a plain value
|
||||
function createKey ({ meta: { name, type } }: CreateItemFn, rawKey: Uint8Array, arg: CreateArgType, hasher: (value: Uint8Array) => Uint8Array): Uint8Array {
|
||||
let key = rawKey;
|
||||
|
||||
key = u8aConcat(key, param);
|
||||
}
|
||||
if (type.isMap) {
|
||||
assert(!isUndefined(arg) && !isNull(arg), `${name} is a Map and requires one argument`);
|
||||
|
||||
// StorageKey is a Bytes, so is length-prefixed
|
||||
return Compact.addLengthPrefix(
|
||||
options.skipHashing
|
||||
? key
|
||||
: hasher(key)
|
||||
);
|
||||
};
|
||||
const mapType = type.asMap.key.toString();
|
||||
const param = createTypeUnsafe(mapType, [arg]).toU8a();
|
||||
|
||||
const storageFn = _storageFn as StorageEntry;
|
||||
key = u8aConcat(key, param);
|
||||
}
|
||||
|
||||
// StorageKey is a Bytes, so is length-prefixed
|
||||
return Compact.addLengthPrefix(hasher(key));
|
||||
}
|
||||
|
||||
// attach the metadata to expsnd to a StorageFunction
|
||||
function expandWithMeta ({ meta, method, prefix, section }: CreateItemFn, storageFn: StorageEntry): StorageEntry {
|
||||
storageFn.meta = meta;
|
||||
storageFn.method = stringLowerFirst(method);
|
||||
storageFn.prefix = prefix;
|
||||
@@ -103,28 +103,68 @@ export default function createFunction ({ meta, method, prefix, section }: Creat
|
||||
|
||||
// explicitly add the actual method in the toJSON, this gets used to determine caching and without it
|
||||
// instances (e.g. collective) will not work since it is only matched on param meta
|
||||
storageFn.toJSON = (): any => ({ ...(meta.toJSON() as any), storage: { method, prefix, section } });
|
||||
storageFn.toJSON = (): any => ({
|
||||
...(meta.toJSON() as any),
|
||||
storage: { method, prefix, section }
|
||||
});
|
||||
|
||||
if (meta.type.isMap && meta.type.asMap.linked.isTrue) {
|
||||
const headHash = new U8a(hasher(`head of ${stringKey}`));
|
||||
const headFn: any = (): U8a => headHash;
|
||||
return storageFn;
|
||||
}
|
||||
|
||||
// metadata with a fallback value using the type of the key, the normal
|
||||
// meta fallback only applies to actual entry values, create one for head
|
||||
headFn.meta = new StorageEntryMetadata({
|
||||
name: meta.name,
|
||||
modifier: createType('StorageEntryModifierV7', 1), // required
|
||||
type: new StorageEntryType(createType('PlainTypeV7', meta.type.asMap.key), 0),
|
||||
fallback: new Bytes(createTypeUnsafe(meta.type.asMap.key.toString()).toHex()),
|
||||
documentation: meta.documentation
|
||||
});
|
||||
// attch the head key hashing for linked maps
|
||||
function extendLinkedMap ({ meta: { documentation, name, type } }: CreateItemFn, storageFn: StorageEntry, stringKey: string, hasher: HasherFunction): StorageEntry {
|
||||
const headHash = new U8a(hasher(`head of ${stringKey}`));
|
||||
const headFn: any = (): U8a =>
|
||||
headHash;
|
||||
|
||||
// here we pass the section/method through as well - these are not on
|
||||
// the function itself, so specify these explicitly to the constructor
|
||||
storageFn.headKey = new StorageKey(headFn, {
|
||||
method: storageFn.method,
|
||||
section: `head of ${storageFn.section}`
|
||||
});
|
||||
// metadata with a fallback value using the type of the key, the normal
|
||||
// meta fallback only applies to actual entry values, create one for head
|
||||
headFn.meta = new StorageEntryMetadata({
|
||||
name,
|
||||
modifier: createType('StorageEntryModifierV7', 1), // required
|
||||
type: new StorageEntryType(createType('PlainTypeV7', type.asMap.key), 0),
|
||||
fallback: new Bytes(createTypeUnsafe(type.asMap.key.toString()).toHex()),
|
||||
documentation
|
||||
});
|
||||
|
||||
// here we pass the section/method through as well - these are not on
|
||||
// the function itself, so specify these explicitly to the constructor
|
||||
storageFn.headKey = new StorageKey(headFn, {
|
||||
method: storageFn.method,
|
||||
section: `head of ${storageFn.section}`
|
||||
});
|
||||
|
||||
return storageFn;
|
||||
}
|
||||
|
||||
/**
|
||||
* From the schema of a function in the module's storage, generate the function
|
||||
* that will return the correct storage key.
|
||||
*
|
||||
* @param item - The function's definition schema to create the function from.
|
||||
* The schema is taken from state_getMetadata.
|
||||
* @param options - Additional options when creating the function. These options
|
||||
* are not known at runtime (from state_getMetadata), they need to be supplied
|
||||
* by us manually at compile time.
|
||||
*/
|
||||
export default function createFunction (item: CreateItemFn, options: CreateItemOptions = {}): StorageEntry {
|
||||
const { meta: { type } } = item;
|
||||
const [stringKey, rawKey] = createKeys(item, options);
|
||||
const [hasher, key2Hasher] = getHashers(item);
|
||||
|
||||
// Can only have zero or one argument:
|
||||
// - storage.balances.freeBalance(address)
|
||||
// - storage.timestamp.blockPeriod()
|
||||
// For doublemap queries the params is passed in as an tuple, [key1, key2]
|
||||
const _storageFn = (arg?: CreateArgType | [CreateArgType?, CreateArgType?]): Uint8Array =>
|
||||
type.isDoubleMap
|
||||
? createKeyDoubleMap(item, rawKey, arg as [CreateArgType, CreateArgType], [hasher, key2Hasher])
|
||||
: createKey(item, rawKey, arg as CreateArgType, options.skipHashing ? NULL_HASHER : hasher);
|
||||
|
||||
const storageFn = expandWithMeta(item, _storageFn as StorageEntry);
|
||||
|
||||
if (type.isMap && type.asMap.linked.isTrue) {
|
||||
extendLinkedMap(item, storageFn, stringKey, hasher);
|
||||
}
|
||||
|
||||
return storageFn;
|
||||
|
||||
@@ -19,7 +19,7 @@ const storage = fromMetadata(metadata);
|
||||
|
||||
describe('fromMetadata', (): void => {
|
||||
it('should throw if the storage function expects an argument', (): void => {
|
||||
expect((): any => storage.balances.freeBalance()).toThrowError(/expects one argument/);
|
||||
expect((): any => storage.balances.freeBalance()).toThrowError(/requires one argument/);
|
||||
});
|
||||
|
||||
it('should return a value if the storage function does not expect an argument', (): void => {
|
||||
|
||||
@@ -6,11 +6,12 @@ import { StorageHasher } from '@polkadot/types/primitive';
|
||||
import { u8aConcat, u8aToU8a } from '@polkadot/util';
|
||||
import { blake2AsU8a, xxhashAsU8a } from '@polkadot/util-crypto';
|
||||
|
||||
type HasherInput = string | Buffer | Uint8Array;
|
||||
type HasherCheck = 'isBlake2128' | 'isBlake2256' | 'isTwox128' | 'isTwox256' | 'isTwox64Concat';
|
||||
export type HasherInput = string | Buffer | Uint8Array;
|
||||
|
||||
export type HasherFunction = (data: HasherInput) => Uint8Array;
|
||||
|
||||
type HasherCheck = 'isBlake2128' | 'isBlake2256' | 'isTwox128' | 'isTwox256' | 'isTwox64Concat';
|
||||
|
||||
const DEFAULT = (data: HasherInput): Uint8Array => xxhashAsU8a(data, 128);
|
||||
|
||||
const map: Record<HasherCheck, HasherFunction> = {
|
||||
|
||||
@@ -7,13 +7,13 @@ The API wrappers provide a standard interface for use -
|
||||
- A static `.create(<optional ApiOptions>)` that returns an API instance when connected, decorated and ready-to use. ApiOptions can include an optional WsProvider and optional custom type definitions `{ provider: <Optional WsProvider>, types: <Optional RegistryTypes> }`.
|
||||
- The above is just a wrapper for `new Api(<optional ApiOptions>) `, exposing the `isReady` getter
|
||||
- `api.rpc.<section>.<method>` provides access to actual RPC calls, be it for queries, submission or retrieving chain information
|
||||
- [RPC (node interface)](../METHODS_RPC.md)
|
||||
- [RPC (node interface)](../substrate/rpc.md)
|
||||
- `api.query.<section>.<method>` provides access to chain state queries. These are dynamically populated based on what the runtime provides
|
||||
- [Storage chain state (runtime node interface)](../METHODS_STORAGE.md)
|
||||
- [Storage chain state (runtime node interface)](../substrate/storage.md)
|
||||
- `api.tx.<section>.<method>` provides the ability to create a transaction, like chain state, this list is populated from a runtime query
|
||||
- [Extrinsics (runtime node interface)](../METHODS_EXTRINSICS.md)
|
||||
- [Extrinsics (runtime node interface)](../substrate/extrinsics.md)
|
||||
- `api.consts.<section>.<constant>` provides access to the module constants (parameter types).
|
||||
- [Constants (runtime node interface)](../METHODS_CONSTANTS.md)
|
||||
- [Constants (runtime node interface)](../substrate/constants.md)
|
||||
|
||||
## API Selection
|
||||
|
||||
@@ -46,7 +46,7 @@ const api = await ApiPromise.create();
|
||||
|
||||
// make a call to retrieve the current network head
|
||||
api.rpc.chain.subscribeNewHeads((header) => {
|
||||
console.log(`Chain is at #${header.blockNumber}`);
|
||||
console.log(`Chain is at #${header.number}`);
|
||||
});
|
||||
```
|
||||
|
||||
@@ -60,7 +60,7 @@ const api = await ApiRx.create().toPromise();
|
||||
|
||||
// make a call to retrieve the current network head
|
||||
api.rpc.chain.subscribeNewHeads().subscribe((header) => {
|
||||
console.log(`Chain is at #${header.blockNumber}`);
|
||||
console.log(`Chain is at #${header.number}`);
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@polkadot/api",
|
||||
"version": "0.90.1",
|
||||
"version": "0.92.1",
|
||||
"description": "Promise and RxJS wrappers around the Polkadot JS RPC",
|
||||
"main": "index.js",
|
||||
"keywords": [
|
||||
@@ -26,15 +26,16 @@
|
||||
},
|
||||
"homepage": "https://github.com/polkadot-js/api/tree/master/packages/api#readme",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"@polkadot/api-derive": "^0.90.1",
|
||||
"@polkadot/api-metadata": "^0.90.1",
|
||||
"@polkadot/rpc-core": "^0.90.1",
|
||||
"@polkadot/rpc-provider": "^0.90.1",
|
||||
"@polkadot/types": "^0.90.1",
|
||||
"@polkadot/util-crypto": "^1.1.1"
|
||||
"@babel/runtime": "^7.6.0",
|
||||
"@polkadot/api-derive": "^0.92.1",
|
||||
"@polkadot/api-metadata": "^0.92.1",
|
||||
"@polkadot/keyring": "^1.4.1",
|
||||
"@polkadot/rpc-core": "^0.92.1",
|
||||
"@polkadot/rpc-provider": "^0.92.1",
|
||||
"@polkadot/types": "^0.92.1",
|
||||
"@polkadot/util-crypto": "^1.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@polkadot/keyring": "^1.1.1"
|
||||
"@polkadot/keyring": "^1.4.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,315 +0,0 @@
|
||||
// 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 { AccountId, Address, Call, ExtrinsicEra, ExtrinsicStatus, EventRecord, Hash, Header, Index } from '@polkadot/types/interfaces';
|
||||
import { AnyNumber, AnyU8a, Callback, Codec, IExtrinsic, IExtrinsicEra, IKeyringPair, SignatureOptions } from '@polkadot/types/types';
|
||||
import { ApiInterfaceRx, ApiTypes } from './types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import { Observable, combineLatest, of } from 'rxjs';
|
||||
import { first, map, mergeMap, switchMap, tap } from 'rxjs/operators';
|
||||
import { createType, Vec } from '@polkadot/types';
|
||||
import { isBn, isFunction, isNumber, isUndefined } from '@polkadot/util';
|
||||
|
||||
import filterEvents from './util/filterEvents';
|
||||
import ApiBase from './base';
|
||||
import SignerPayload from './SignerPayload';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/interface-name-prefix
|
||||
export interface ISubmittableResult {
|
||||
readonly events: EventRecord[];
|
||||
readonly status: ExtrinsicStatus;
|
||||
readonly isCompleted: boolean;
|
||||
readonly isError: boolean;
|
||||
readonly isFinalized: boolean;
|
||||
|
||||
findRecord(section: string, method: string): EventRecord | undefined;
|
||||
}
|
||||
|
||||
export type SumbitableResultResult<ApiType> =
|
||||
ApiType extends 'rxjs'
|
||||
? Observable<ISubmittableResult>
|
||||
: Promise<Hash>;
|
||||
|
||||
export type SumbitableResultSubscription<ApiType> =
|
||||
ApiType extends 'rxjs'
|
||||
? Observable<ISubmittableResult>
|
||||
: Promise<() => void>;
|
||||
|
||||
interface SubmittableResultValue {
|
||||
events?: EventRecord[];
|
||||
status: ExtrinsicStatus;
|
||||
}
|
||||
|
||||
interface SignerOptions {
|
||||
blockHash: AnyU8a;
|
||||
era?: IExtrinsicEra | number;
|
||||
nonce: AnyNumber;
|
||||
tip?: AnyNumber;
|
||||
}
|
||||
|
||||
// The default for 6s allowing for 5min eras. When translating this to faster blocks -
|
||||
// - 4s = (10 / 15) * 5 = 3.33m
|
||||
// - 2s = (10 / 30) * 5 = 1.66m
|
||||
const BLOCKTIME = 6;
|
||||
const ONE_MINUTE = 60 / BLOCKTIME;
|
||||
const DEFAULT_MORTAL_LENGTH = 5 * ONE_MINUTE;
|
||||
|
||||
function isKeyringPair (account: string | IKeyringPair | AccountId | Address): account is IKeyringPair {
|
||||
return isFunction((account as IKeyringPair).sign);
|
||||
}
|
||||
|
||||
export class SubmittableResult implements ISubmittableResult {
|
||||
public readonly events: EventRecord[];
|
||||
|
||||
public readonly status: ExtrinsicStatus;
|
||||
|
||||
public constructor ({ events, status }: SubmittableResultValue) {
|
||||
this.events = events || [];
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public get isCompleted (): boolean {
|
||||
return this.isError || this.isFinalized;
|
||||
}
|
||||
|
||||
public get isError (): boolean {
|
||||
return this.status.isDropped || this.status.isInvalid || this.status.isUsurped;
|
||||
}
|
||||
|
||||
public get isFinalized (): boolean {
|
||||
return this.status.isFinalized;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Finds an EventRecord for the specified method & section
|
||||
*/
|
||||
public findRecord (section: string, method: string): EventRecord | undefined {
|
||||
return this.events.find(({ event }): boolean =>
|
||||
event.section === section && event.method === method
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export interface SubmittableExtrinsic<ApiType> extends IExtrinsic {
|
||||
send(): SumbitableResultResult<ApiType>;
|
||||
|
||||
send(statusCb: Callback<ISubmittableResult>): SumbitableResultSubscription<ApiType>;
|
||||
|
||||
sign(account: IKeyringPair, _options: Partial<SignatureOptions>): this;
|
||||
|
||||
signAndSend(account: IKeyringPair | string | AccountId | Address, options?: Partial<SignerOptions>): SumbitableResultResult<ApiType>;
|
||||
|
||||
signAndSend(account: IKeyringPair | string | AccountId | Address, statusCb: Callback<ISubmittableResult>): SumbitableResultSubscription<ApiType>;
|
||||
|
||||
signAndSend(account: IKeyringPair | string | AccountId | Address, options: Partial<SignerOptions>, statusCb?: Callback<ISubmittableResult>): SumbitableResultSubscription<ApiType>;
|
||||
}
|
||||
|
||||
export default function createSubmittableExtrinsic<ApiType> (
|
||||
type: ApiTypes,
|
||||
api: ApiInterfaceRx,
|
||||
decorateMethod: ApiBase<ApiType>['decorateMethod'],
|
||||
extrinsic: Call | Uint8Array | string
|
||||
): SubmittableExtrinsic<ApiType> {
|
||||
const _extrinsic = createType('Extrinsic', extrinsic, { version: api.extrinsicType }) as unknown as SubmittableExtrinsic<ApiType>;
|
||||
const _noStatusCb = type === 'rxjs';
|
||||
|
||||
function updateSigner (updateId: number, status: Hash | ISubmittableResult): void {
|
||||
if ((updateId !== -1) && api.signer && api.signer.update) {
|
||||
api.signer.update(updateId, status);
|
||||
}
|
||||
}
|
||||
|
||||
function statusObservable (status: ExtrinsicStatus): Observable<ISubmittableResult> {
|
||||
if (!status.isFinalized) {
|
||||
return of(new SubmittableResult({ status }));
|
||||
}
|
||||
|
||||
const blockHash = status.asFinalized;
|
||||
|
||||
return combineLatest([
|
||||
api.rpc.chain.getBlock(blockHash),
|
||||
api.query.system.events.at(blockHash) as Observable<Vec<EventRecord>>
|
||||
]).pipe(
|
||||
map(([signedBlock, allEvents]): SubmittableResult =>
|
||||
new SubmittableResult({
|
||||
events: filterEvents(_extrinsic.hash, signedBlock, allEvents),
|
||||
status
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function sendObservable (updateId: number = -1): Observable<Hash> {
|
||||
return api.rpc.author
|
||||
.submitExtrinsic(_extrinsic)
|
||||
.pipe(
|
||||
tap((hash): void => {
|
||||
updateSigner(updateId, hash);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function subscribeObservable (updateId: number = -1): Observable<ISubmittableResult> {
|
||||
return api.rpc.author
|
||||
.submitAndWatchExtrinsic(_extrinsic)
|
||||
.pipe(
|
||||
switchMap((status): Observable<ISubmittableResult> =>
|
||||
statusObservable(status)
|
||||
),
|
||||
tap((status): void => {
|
||||
updateSigner(updateId, status);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function expandOptions (options: Partial<SignerOptions>, extras: { blockHash?: Hash; era?: ExtrinsicEra; nonce?: Index }): SignatureOptions {
|
||||
return {
|
||||
blockHash: api.genesisHash,
|
||||
...options,
|
||||
...extras,
|
||||
genesisHash: api.genesisHash,
|
||||
runtimeVersion: api.runtimeVersion,
|
||||
version: api.extrinsicType
|
||||
} as unknown as SignatureOptions;
|
||||
}
|
||||
|
||||
function expandEraOptions (options: Partial<SignerOptions>, { header, nonce }: { header: Header | null; nonce: Index }): SignatureOptions {
|
||||
if (!header) {
|
||||
if (isNumber(options.era)) {
|
||||
// since we have no header, it is immortal, remove any option overrides
|
||||
// so we only supply the genesisHash and no era to the construction
|
||||
delete options.era;
|
||||
delete options.blockHash;
|
||||
}
|
||||
|
||||
return expandOptions(options, { nonce });
|
||||
}
|
||||
|
||||
return expandOptions(options, {
|
||||
blockHash: header.hash,
|
||||
era: createType('ExtrinsicEra', {
|
||||
current: header.number,
|
||||
period: options.era || DEFAULT_MORTAL_LENGTH
|
||||
}),
|
||||
nonce
|
||||
});
|
||||
}
|
||||
|
||||
function getPrelimState (address: string, options: Partial<SignerOptions>): Observable<[Index, Header | null]> {
|
||||
return combineLatest([
|
||||
// if we have a nonce already, don't retrieve the latest, use what is there
|
||||
isUndefined(options.nonce)
|
||||
? api.query.system.accountNonce<Index>(address)
|
||||
: of(createType('Index', options.nonce)),
|
||||
// if we have an era provided already or eraLength is <= 0 (immortal)
|
||||
// don't get the latest block, just pass null, handle in mergeMap
|
||||
(isUndefined(options.era) || (isNumber(options.era) && options.era > 0))
|
||||
? api.rpc.chain.getHeader()
|
||||
: of(null)
|
||||
]);
|
||||
}
|
||||
|
||||
const signOrigin = _extrinsic.sign;
|
||||
|
||||
Object.defineProperties(
|
||||
_extrinsic,
|
||||
{
|
||||
send: {
|
||||
value: function (statusCb?: Callback<ISubmittableResult>): SumbitableResultResult<ApiType> | SumbitableResultSubscription<ApiType> {
|
||||
const isSubscription = _noStatusCb || !!statusCb;
|
||||
|
||||
return decorateMethod(isSubscription ? subscribeObservable : sendObservable)(statusCb);
|
||||
}
|
||||
},
|
||||
sign: {
|
||||
value: function (account: IKeyringPair, optionOrNonce: Partial<SignerOptions>): SubmittableExtrinsic<ApiType> {
|
||||
// HACK here we actually override nonce if it was specified (backwards compat for
|
||||
// the previous signature - don't let userspace break, but allow then time to upgrade)
|
||||
const options: Partial<SignerOptions> = isBn(optionOrNonce) || isNumber(optionOrNonce)
|
||||
? { nonce: optionOrNonce }
|
||||
: optionOrNonce;
|
||||
|
||||
signOrigin.apply(_extrinsic, [account, expandOptions(options, {})]);
|
||||
|
||||
return this;
|
||||
}
|
||||
},
|
||||
signAndSend: {
|
||||
value: function (account: IKeyringPair | string | AccountId | Address, optionsOrStatus?: Partial<SignerOptions> | Callback<ISubmittableResult>, statusCb?: Callback<ISubmittableResult>): SumbitableResultResult<ApiType> | SumbitableResultSubscription<ApiType> {
|
||||
let options: Partial<SignerOptions> = {};
|
||||
|
||||
if (isFunction(optionsOrStatus)) {
|
||||
statusCb = optionsOrStatus;
|
||||
} else {
|
||||
options = { ...optionsOrStatus };
|
||||
}
|
||||
|
||||
const isSubscription = _noStatusCb || !!statusCb;
|
||||
const address = isKeyringPair(account) ? account.address : account.toString();
|
||||
let updateId: number | undefined;
|
||||
|
||||
return decorateMethod(
|
||||
(): Observable<Codec> => (
|
||||
getPrelimState(address, options).pipe(
|
||||
first(),
|
||||
mergeMap(async ([nonce, header]): Promise<void> => {
|
||||
const eraOptions = expandEraOptions(options, { header, nonce });
|
||||
|
||||
// FIXME This is becoming real messy with all the options - way past
|
||||
// "a method should fit on a single screen" stage. (Probably want to
|
||||
// clean this when we remove `api.signer.sign` in the next beta cycle)
|
||||
if (isKeyringPair(account)) {
|
||||
this.sign(account, eraOptions);
|
||||
} else if (api.signer) {
|
||||
const payload = new SignerPayload({
|
||||
...eraOptions,
|
||||
address,
|
||||
method: _extrinsic.method,
|
||||
blockNumber: header ? header.number : 0
|
||||
});
|
||||
|
||||
if (api.signer.signPayload) {
|
||||
const { id, signature } = await api.signer.signPayload(payload.toPayload());
|
||||
|
||||
// Here we explicitly call `toPayload()` again instead of working with an object
|
||||
// (reference) as passed to the signer. This means that we are sure that the
|
||||
// payload data is not modified from our inputs, but the signer
|
||||
_extrinsic.addSignature(address, signature, payload.toPayload());
|
||||
updateId = id;
|
||||
} else if (api.signer.signRaw) {
|
||||
const { id, signature } = await api.signer.signRaw(payload.toRaw());
|
||||
|
||||
// as above, always trust our payload as the signle sourec of truth
|
||||
_extrinsic.addSignature(address, signature, payload.toPayload());
|
||||
updateId = id;
|
||||
} else if (api.signer.sign) {
|
||||
console.warn('The Signer.sign interface is deprecated and will be removed in a future version, Swap to using the Signer.signPayload interface instead.');
|
||||
|
||||
updateId = await api.signer.sign(_extrinsic, address, {
|
||||
...eraOptions,
|
||||
blockNumber: header ? header.number.toBn() : new BN(0),
|
||||
genesisHash: api.genesisHash
|
||||
});
|
||||
} else {
|
||||
throw new Error('Invalid signer interface');
|
||||
}
|
||||
} else {
|
||||
throw new Error('no signer exists');
|
||||
}
|
||||
}),
|
||||
switchMap((): Observable<ISubmittableResult> | Observable<Hash> => {
|
||||
return isSubscription
|
||||
? subscribeObservable(updateId)
|
||||
: sendObservable(updateId);
|
||||
})
|
||||
) as Observable<Codec>) // FIXME This is wrong, SubmittableResult is _not_ a codec
|
||||
)(statusCb);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return _extrinsic;
|
||||
}
|
||||
@@ -3,9 +3,10 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { RpcInterface } from '@polkadot/rpc-core/jsonrpc.types';
|
||||
import { Hash, RuntimeVersion } from '@polkadot/types/interfaces';
|
||||
import { Call, Hash, RuntimeVersion } from '@polkadot/types/interfaces';
|
||||
import { AnyFunction, CallFunction, Codec, CodecArg as Arg, ModulesWithCalls } from '@polkadot/types/types';
|
||||
import { ApiInterfaceRx, ApiOptions, ApiTypes, DecorateMethodOptions, DecoratedRpc, DecoratedRpcSection, QueryableModuleStorage, QueryableStorage, QueryableStorageEntry, QueryableStorageMulti, QueryableStorageMultiArg, QueryableStorageMultiArgs, SubmittableExtrinsicFunction, SubmittableExtrinsics, SubmittableModuleExtrinsics } from '../types';
|
||||
import { SubmittableExtrinsic } from '../submittable/types';
|
||||
import { ApiInterfaceRx, ApiOptions, ApiTypes, DecorateMethod, DecoratedRpc, DecoratedRpcSection, QueryableModuleStorage, QueryableStorage, QueryableStorageEntry, QueryableStorageMulti, QueryableStorageMultiArg, QueryableStorageMultiArgs, SubmittableExtrinsicFunction, SubmittableExtrinsics, SubmittableModuleExtrinsics } from '../types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
@@ -21,7 +22,7 @@ import { DEFAULT_VERSION as EXTRINSIC_DEFAULT_VERSION } from '@polkadot/types/pr
|
||||
import { StorageEntry } from '@polkadot/types/primitive/StorageKey';
|
||||
import { compactStripLength, u8aToHex } from '@polkadot/util';
|
||||
|
||||
import createSubmittable, { SubmittableExtrinsic } from '../SubmittableExtrinsic';
|
||||
import { createSubmittable } from '../submittable';
|
||||
import { decorateSections } from '../util/decorate';
|
||||
import Events from './Events';
|
||||
|
||||
@@ -49,7 +50,7 @@ export default abstract class Decorate<ApiType> extends Events {
|
||||
|
||||
protected _isConnected: BehaviorSubject<boolean>;
|
||||
|
||||
protected _isReady: boolean = false;
|
||||
protected _isReady = false;
|
||||
|
||||
protected readonly _options: ApiOptions;
|
||||
|
||||
@@ -69,6 +70,24 @@ export default abstract class Decorate<ApiType> extends Events {
|
||||
|
||||
protected _type: ApiTypes;
|
||||
|
||||
/**
|
||||
* This is the one and only method concrete children classes need to implement.
|
||||
* It's a higher-order function, which takes one argument
|
||||
* `method: Method extends (...args: any[]) => Observable<any>`
|
||||
* (and one optional `options`), and should return the user facing method.
|
||||
* For example:
|
||||
* - For ApiRx, `decorateMethod` should just be identity, because the input
|
||||
* function is already an Observable
|
||||
* - For ApiPromise, `decorateMethod` should return a function that takes all
|
||||
* the parameters from `method`, adds an optional `callback` argument, and
|
||||
* returns a Promise.
|
||||
*
|
||||
* We could easily imagine other user-facing interfaces, which are simply
|
||||
* implemented by transforming the Observable to Stream/Iterator/Kefir/Bacon
|
||||
* via `deocrateMethod`.
|
||||
*/
|
||||
protected decorateMethod: DecorateMethod;
|
||||
|
||||
/**
|
||||
* @description Create an instance of the class
|
||||
*
|
||||
@@ -87,37 +106,20 @@ export default abstract class Decorate<ApiType> extends Events {
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
public constructor (options: ApiOptions, type: ApiTypes) {
|
||||
public constructor (options: ApiOptions, type: ApiTypes, decorateMethod: DecorateMethod) {
|
||||
super();
|
||||
|
||||
const thisProvider = options.source
|
||||
? options.source._rpcCore.provider.clone()
|
||||
: (options.provider || new WsProvider());
|
||||
|
||||
this.decorateMethod = decorateMethod;
|
||||
this._options = options;
|
||||
this._type = type;
|
||||
this._rpcCore = new RpcCore(thisProvider);
|
||||
this._isConnected = new BehaviorSubject(this._rpcCore.provider.isConnected());
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the one and only method concrete children classes need to implement.
|
||||
* It's a higher-order function, which takes one argument
|
||||
* `method: Method extends (...args: any[]) => Observable<any>`
|
||||
* (and one optional `options`), and should return the user facing method.
|
||||
* For example:
|
||||
* - For ApiRx, `decorateMethod` should just be identity, because the input
|
||||
* function is already an Observable
|
||||
* - For ApiPromise, `decorateMethod` should return a function that takes all
|
||||
* the parameters from `method`, adds an optional `callback` argument, and
|
||||
* returns a Promise.
|
||||
*
|
||||
* We could easily imagine other user-facing interfaces, which are simply
|
||||
* implemented by transforming the Observable to Stream/Iterator/Kefir/Bacon
|
||||
* via `deocrateMethod`.
|
||||
*/
|
||||
protected abstract decorateMethod(method: (...args: any[]) => Observable<any>, options?: DecorateMethodOptions): any;
|
||||
|
||||
private decorateFunctionMeta (input: MetaDecoration, output: MetaDecoration): MetaDecoration {
|
||||
output.meta = input.meta;
|
||||
output.method = input.method;
|
||||
@@ -135,10 +137,9 @@ export default abstract class Decorate<ApiType> extends Events {
|
||||
return ['author', 'chain', 'state', 'system'].reduce((out, _sectionName): DecoratedRpc<ApiType, RpcInterface> => {
|
||||
const sectionName = _sectionName as keyof DecoratedRpc<ApiType, RpcInterface>;
|
||||
|
||||
// @ts-ignore Hard to type these correctly, I don't understand the TS errors
|
||||
out[sectionName] = Object.entries(rpc[sectionName]).reduce((section, [methodName, method]): DecoratedRpcSection<ApiType, RpcInterface[typeof sectionName]> => {
|
||||
// @ts-ignore
|
||||
section[methodName] = decorateMethod(method, { methodName });
|
||||
// out and section here are horrors to get right from a typing perspective :()
|
||||
(out as any)[sectionName] = Object.entries(rpc[sectionName]).reduce((section, [methodName, method]): DecoratedRpcSection<ApiType, RpcInterface[typeof sectionName]> => {
|
||||
(section as any)[methodName] = decorateMethod(method, { methodName });
|
||||
|
||||
return section;
|
||||
}, {} as unknown as DecoratedRpcSection<ApiType, RpcInterface[typeof sectionName]>);
|
||||
@@ -158,12 +159,11 @@ export default abstract class Decorate<ApiType> extends Events {
|
||||
}
|
||||
|
||||
protected decorateExtrinsics<ApiType> (extrinsics: ModulesWithCalls, decorateMethod: Decorate<ApiType>['decorateMethod']): SubmittableExtrinsics<ApiType> {
|
||||
const creator = (value: Uint8Array | string): SubmittableExtrinsic<ApiType> =>
|
||||
createSubmittable(this._type, this._rx as ApiInterfaceRx, decorateMethod, value);
|
||||
const creator = createSubmittable(this._type, this._rx as ApiInterfaceRx, decorateMethod);
|
||||
|
||||
return Object.entries(extrinsics).reduce((out, [name, section]): SubmittableExtrinsics<ApiType> => {
|
||||
out[name] = Object.entries(section).reduce((out, [name, method]): SubmittableModuleExtrinsics<ApiType> => {
|
||||
out[name] = this.decorateExtrinsicEntry(method, decorateMethod);
|
||||
out[name] = this.decorateExtrinsicEntry(method, creator);
|
||||
|
||||
return out;
|
||||
}, {} as unknown as SubmittableModuleExtrinsics<ApiType>);
|
||||
@@ -172,9 +172,9 @@ export default abstract class Decorate<ApiType> extends Events {
|
||||
}, creator as unknown as SubmittableExtrinsics<ApiType>);
|
||||
}
|
||||
|
||||
private decorateExtrinsicEntry<ApiType> (method: CallFunction, decorateMethod: Decorate<ApiType>['decorateMethod']): SubmittableExtrinsicFunction<ApiType> {
|
||||
private decorateExtrinsicEntry<ApiType> (method: CallFunction, creator: (value: Call | Uint8Array | string) => SubmittableExtrinsic<ApiType>): SubmittableExtrinsicFunction<ApiType> {
|
||||
const decorated = (...params: Arg[]): SubmittableExtrinsic<ApiType> =>
|
||||
createSubmittable(this._type, this._rx as ApiInterfaceRx, decorateMethod, method(...params));
|
||||
creator(method(...params));
|
||||
|
||||
return this.decorateFunctionMeta(method, decorated as any) as SubmittableExtrinsicFunction<ApiType>;
|
||||
}
|
||||
@@ -310,7 +310,7 @@ export default abstract class Decorate<ApiType> extends Events {
|
||||
* Put the `this.onCall` function of ApiRx here, because it is needed by
|
||||
* `api._rx`.
|
||||
*/
|
||||
protected rxDecorateMethod<Method extends AnyFunction> (method: Method): Method {
|
||||
protected rxDecorateMethod = <Method extends AnyFunction> (method: Method): Method => {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,21 +2,24 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { RuntimeVersion, SignedBlock } from '@polkadot/types/interfaces';
|
||||
import { Prefix } from '@polkadot/util-crypto/address/types';
|
||||
import { SignedBlock } from '@polkadot/types/interfaces';
|
||||
import { RegistryTypes } from '@polkadot/types/types';
|
||||
import { ApiInterfaceRx, ApiOptions, ApiTypes } from '../types';
|
||||
import { ApiInterfaceRx, ApiOptions, ApiTypes, DecorateMethod } from '../types';
|
||||
|
||||
import constantsFromMeta from '@polkadot/api-metadata/consts/fromMetadata';
|
||||
import extrinsicsFromMeta from '@polkadot/api-metadata/extrinsics/fromMetadata';
|
||||
import storageFromMeta from '@polkadot/api-metadata/storage/fromMetadata';
|
||||
import { GenericCall, GenericEvent, Metadata } from '@polkadot/types';
|
||||
import { GenericCall, GenericEvent, Metadata, u32 as U32 } from '@polkadot/types';
|
||||
import { LATEST_VERSION as EXTRINSIC_LATEST_VERSION } from '@polkadot/types/primitive/Extrinsic/constants';
|
||||
import { assert, logger } from '@polkadot/util';
|
||||
import { cryptoWaitReady } from '@polkadot/util-crypto';
|
||||
import { cryptoWaitReady, setSS58Format } from '@polkadot/util-crypto';
|
||||
import addressDefaults from '@polkadot/util-crypto/address/defaults';
|
||||
|
||||
import Decorate from './Decorate';
|
||||
|
||||
const KEEPALIVE_INTERVAL = 15000;
|
||||
const DEFAULT_SS58 = new U32(addressDefaults.prefix);
|
||||
|
||||
// these are override types for polkadot chains
|
||||
// NOTE The SessionKeys definition for Polkadot and Substrate (OpaqueKeys
|
||||
@@ -35,8 +38,8 @@ const TYPES_SUBSTRATE_1 = {
|
||||
ValidatorPrefs: 'ValidatorPrefs0to145'
|
||||
};
|
||||
|
||||
// Type overrides for specific node types
|
||||
const SPEC_TYPES: Record<string, Record<string, string>> = {
|
||||
// Type overrides for specific spec types as given in runtimeVersion
|
||||
const TYPES_SPEC: Record<string, Record<string, string>> = {
|
||||
kusama: TYPES_FOR_POLKADOT,
|
||||
polkadot: TYPES_FOR_POLKADOT
|
||||
};
|
||||
@@ -46,8 +49,8 @@ const l = logger('api/decorator');
|
||||
export default abstract class Init<ApiType> extends Decorate<ApiType> {
|
||||
private _healthTimer: NodeJS.Timeout | null = null;
|
||||
|
||||
public constructor (options: ApiOptions, type: ApiTypes) {
|
||||
super(options, type);
|
||||
public constructor (options: ApiOptions, type: ApiTypes, decorateMethod: DecorateMethod) {
|
||||
super(options, type, decorateMethod);
|
||||
|
||||
assert(this._rpcCore.provider.hasSubscriptions, 'Api can only be used with a provider supporting subscriptions');
|
||||
|
||||
@@ -89,21 +92,35 @@ export default abstract class Init<ApiType> extends Decorate<ApiType> {
|
||||
}
|
||||
|
||||
private async metaFromChain (optMetadata: Record<string, string>): Promise<Metadata> {
|
||||
[this._genesisHash, this._runtimeVersion] = await Promise.all([
|
||||
const { typesChain = {}, typesSpec = {} } = this._options;
|
||||
const [genesisHash, runtimeVersion, chain, chainProps] = await Promise.all([
|
||||
this._rpcCore.chain.getBlockHash(0).toPromise(),
|
||||
this._rpcCore.chain.getRuntimeVersion().toPromise()
|
||||
this._rpcCore.state.getRuntimeVersion().toPromise(),
|
||||
this._rpcCore.system.chain().toPromise(),
|
||||
this._rpcCore.system.properties().toPromise()
|
||||
]);
|
||||
const specName = runtimeVersion.specName.toString();
|
||||
|
||||
// based on the node, inject specific types
|
||||
this.registerTypes(
|
||||
SPEC_TYPES[this._runtimeVersion.specName.toString()]
|
||||
);
|
||||
// based on the node spec & chain, inject specific type overrides
|
||||
this.registerTypes({
|
||||
...(TYPES_SPEC[specName] || {}),
|
||||
...(typesSpec[specName] || {}),
|
||||
...(typesChain[chain.toString()] || {})
|
||||
});
|
||||
|
||||
const metadataKey = `${this._genesisHash}-${(this._runtimeVersion as RuntimeVersion).specVersion}`;
|
||||
// retrieve metadata, either from chain or as pass-in via options
|
||||
const metadataKey = `${genesisHash}-${runtimeVersion.specVersion}`;
|
||||
const metadata = metadataKey in optMetadata
|
||||
? new Metadata(optMetadata[metadataKey])
|
||||
: await this._rpcCore.state.getMetadata().toPromise();
|
||||
|
||||
// set our chain version & genesisHash as returned
|
||||
this._genesisHash = genesisHash;
|
||||
this._runtimeVersion = runtimeVersion;
|
||||
|
||||
// set the global ss58Format as detected by the chain
|
||||
setSS58Format(chainProps.ss58Format.unwrapOr(DEFAULT_SS58).toNumber() as Prefix);
|
||||
|
||||
// get unique types & validate
|
||||
metadata.getUniqTypes(false);
|
||||
|
||||
@@ -111,8 +128,10 @@ export default abstract class Init<ApiType> extends Decorate<ApiType> {
|
||||
}
|
||||
|
||||
private async initFromMeta (metadata: Metadata): Promise<boolean> {
|
||||
// HACK-ish Old EventRecord format for e.g. Alex, based on \metadata format
|
||||
if (metadata.version <= 3) {
|
||||
// HACK-ish Old EventRecord, BlockNumber & Indexes for e.g. Alex, based on metadata version
|
||||
// v3 = Alex
|
||||
// v4 = v1.0 branch
|
||||
if (metadata.version <= 4) {
|
||||
this.registerTypes(TYPES_SUBSTRATE_1);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,8 @@
|
||||
|
||||
import { RpcInterface } from '@polkadot/rpc-core/jsonrpc.types';
|
||||
import { Hash, RuntimeVersion } from '@polkadot/types/interfaces';
|
||||
import { CallFunction, RegistryTypes } from '@polkadot/types/types';
|
||||
|
||||
import { ApiOptions, ApiTypes, DecoratedRpc, QueryableStorage, QueryableStorageMulti, SignerPayloadRawBase, SubmittableExtrinsics, Signer } from '../types';
|
||||
import { CallFunction, RegistryTypes, SignerPayloadRawBase } from '@polkadot/types/types';
|
||||
import { ApiOptions, ApiTypes, DecoratedRpc, DecorateMethod, QueryableStorage, QueryableStorageMulti, SubmittableExtrinsics, Signer } from '../types';
|
||||
|
||||
import { Constants } from '@polkadot/api-metadata/consts/types';
|
||||
import { GenericCall, Metadata, getTypeRegistry } from '@polkadot/types';
|
||||
@@ -28,7 +27,7 @@ try {
|
||||
}
|
||||
|
||||
function assertResult <T> (value: T | undefined): T {
|
||||
assert(!isUndefined(value), `Api needs to be initialised before using, listen on 'ready'`);
|
||||
assert(!isUndefined(value), 'Api needs to be initialised before using, listen on \'ready\'');
|
||||
|
||||
return value as T;
|
||||
}
|
||||
@@ -52,8 +51,8 @@ export default abstract class ApiBase<ApiType> extends Init<ApiType> {
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
public constructor (options: ApiOptions = {}, type: ApiTypes) {
|
||||
super(options, type);
|
||||
public constructor (options: ApiOptions = {}, type: ApiTypes, decorateMethod: DecorateMethod) {
|
||||
super(options, type, decorateMethod);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,7 +88,7 @@ export default abstract class ApiBase<ApiType> extends Init<ApiType> {
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Returns th version of extrinsics in-use on this chain
|
||||
* @description Returns the version of extrinsics in-use on this chain
|
||||
*/
|
||||
public get extrinsicVersion (): number {
|
||||
return this._extrinsicType;
|
||||
|
||||
@@ -15,7 +15,7 @@ import { createType, createTypeUnsafe } from '@polkadot/types/codec';
|
||||
|
||||
import { SubmittableResult } from './';
|
||||
|
||||
async function consts (api: ApiPromise): Promise<void> {
|
||||
function consts (api: ApiPromise): void {
|
||||
// constants has actual value & metadata
|
||||
console.log(
|
||||
api.consts.balances.creationFee.toHex(),
|
||||
@@ -118,4 +118,5 @@ async function main (): Promise<void> {
|
||||
tx(api, keyring);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||
main().catch(console.error);
|
||||
|
||||
@@ -6,8 +6,9 @@ import { assertSingletonPackage } from '@polkadot/util';
|
||||
|
||||
assertSingletonPackage('@polkadot/api');
|
||||
|
||||
export { Keyring } from '@polkadot/keyring';
|
||||
export { WsProvider } from '@polkadot/rpc-provider';
|
||||
|
||||
export { default as ApiPromise } from './promise';
|
||||
export { default as ApiRx } from './rx';
|
||||
export { default as SubmittableExtrinsic, SubmittableResult } from './SubmittableExtrinsic';
|
||||
export * from './submittable';
|
||||
|
||||
@@ -57,6 +57,37 @@ function promiseTracker (resolve: (value: () => void) => void, reject: (value: E
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Decorate method for ApiPromise, where the results are converted to the Promise equivalent
|
||||
*/
|
||||
function decorateMethod<Method extends AnyFunction> (method: Method, options?: DecorateMethodOptions): StorageEntryPromiseOverloads {
|
||||
const needsCallback = options && options.methodName && options.methodName.includes('subscribe');
|
||||
|
||||
return function (...args: any[]): Promise<ObsInnerType<ReturnType<Method>>> | UnsubscribePromise {
|
||||
const [actualArgs, callback] = extractArgs(args, !!needsCallback);
|
||||
|
||||
if (!callback) {
|
||||
return method(...actualArgs).pipe(first()).toPromise() as Promise<ObsInnerType<ReturnType<Method>>>;
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject): void => {
|
||||
const tracker = promiseTracker(resolve, reject);
|
||||
const subscription = method(...actualArgs)
|
||||
.pipe(
|
||||
// if we find an error (invalid params, etc), reject the promise
|
||||
catchError((error): Observable<never> =>
|
||||
tracker.reject(error)
|
||||
),
|
||||
// upon the first result, resolve the with the unsub function
|
||||
tap((): void =>
|
||||
tracker.resolve((): void => subscription.unsubscribe())
|
||||
)
|
||||
)
|
||||
.subscribe(callback);
|
||||
}) as any; // ???
|
||||
} as StorageEntryPromiseOverloads;
|
||||
}
|
||||
|
||||
/**
|
||||
* # @polkadot/api/promise
|
||||
*
|
||||
@@ -183,7 +214,7 @@ export default class ApiPromise extends ApiBase<'promise'> {
|
||||
* ```
|
||||
*/
|
||||
public constructor (options?: ApiOptions) {
|
||||
super(options, 'promise');
|
||||
super(options, 'promise', decorateMethod);
|
||||
|
||||
this._isReadyPromise = new Promise((resolve): void => {
|
||||
super.once('ready', (): void => {
|
||||
@@ -229,6 +260,7 @@ export default class ApiPromise extends ApiBase<'promise'> {
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
public async combineLatest (fns: (CombinatorFunction | [CombinatorFunction, ...any[]])[], callback: CombinatorCallback): UnsubscribePromise {
|
||||
const combinator = new Combinator(fns, callback);
|
||||
|
||||
@@ -236,35 +268,4 @@ export default class ApiPromise extends ApiBase<'promise'> {
|
||||
combinator.unsubscribe();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Decorate method for ApiPromise, where the results are converted to the Promise equivalent
|
||||
*/
|
||||
protected decorateMethod<Method extends AnyFunction> (method: Method, options?: DecorateMethodOptions): StorageEntryPromiseOverloads {
|
||||
const needsCallback = options && options.methodName && options.methodName.includes('subscribe');
|
||||
|
||||
return function (...args: any[]): Promise<ObsInnerType<ReturnType<Method>>> | UnsubscribePromise {
|
||||
const [actualArgs, callback] = extractArgs(args, !!needsCallback);
|
||||
|
||||
if (!callback) {
|
||||
return method(...actualArgs).pipe(first()).toPromise() as Promise<ObsInnerType<ReturnType<Method>>>;
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject): void => {
|
||||
const tracker = promiseTracker(resolve, reject);
|
||||
const subscription = method(...actualArgs)
|
||||
.pipe(
|
||||
// if we find an error (invalid params, etc), reject the promise
|
||||
catchError((error): Observable<never> =>
|
||||
tracker.reject(error)
|
||||
),
|
||||
// upon the first result, resolve the with the unsub function
|
||||
tap((): void =>
|
||||
tracker.resolve((): void => subscription.unsubscribe())
|
||||
)
|
||||
)
|
||||
.subscribe(callback);
|
||||
}) as UnsubscribePromise;
|
||||
} as StorageEntryPromiseOverloads;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ export interface CombinatorFunction {
|
||||
}
|
||||
|
||||
export default class Combinator {
|
||||
protected _allHasFired: boolean = false;
|
||||
protected _allHasFired = false;
|
||||
|
||||
protected _callback: CombinatorCallback;
|
||||
|
||||
@@ -21,7 +21,7 @@ export default class Combinator {
|
||||
|
||||
protected _fns: CombinatorFunction[] = [];
|
||||
|
||||
protected _isActive: boolean = true;
|
||||
protected _isActive = true;
|
||||
|
||||
protected _results: any[] = [];
|
||||
|
||||
@@ -29,6 +29,8 @@ export default class Combinator {
|
||||
|
||||
public constructor (fns: (CombinatorFunction | [CombinatorFunction, ...any[]])[], callback: CombinatorCallback) {
|
||||
this._callback = callback;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
this._subscriptions = fns.map(async (input, index): UnsubscribePromise => {
|
||||
const [fn, ...args] = Array.isArray(input)
|
||||
? input
|
||||
@@ -37,8 +39,8 @@ export default class Combinator {
|
||||
this._fired.push(false);
|
||||
this._fns.push(fn);
|
||||
|
||||
// @ts-ignore Not quite 100% how to have a variable number at the front here
|
||||
return fn(...args, this.createCallback(index));
|
||||
// Not quite 100% how to have a variable number at the front here
|
||||
return (fn as Function)(...args, this.createCallback(index));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -78,6 +80,7 @@ export default class Combinator {
|
||||
|
||||
this._isActive = false;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
this._subscriptions.forEach(async (subscription): Promise<void> => {
|
||||
try {
|
||||
const unsubscribe = await subscription;
|
||||
|
||||
@@ -7,6 +7,8 @@ import Combinator from './Combinator';
|
||||
|
||||
describe('Combinator', (): void => {
|
||||
let fns: ((value: any) => void)[] = [];
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
const storeFn = async (cb: (value: any) => void): UnsubscribePromise => {
|
||||
fns.push(cb);
|
||||
|
||||
@@ -81,10 +83,12 @@ describe('Combinator', (): void => {
|
||||
});
|
||||
|
||||
it('unsubscribes as required', (done): void => {
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
const mocker = async (): Promise<any> => done;
|
||||
const combinator = new Combinator([
|
||||
mocker,
|
||||
async (): UnsubscribePromise => (): void => void 0
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
async (): UnsubscribePromise => (): void => {}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
], (value: any[]): void => {
|
||||
// ignore
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
import { QueryableStorageEntry as QueryableStorageEntryBase, SubmittableExtrinsicFunction as SubmittableExtrinsicFunctionBase } from '../types';
|
||||
|
||||
import { SubmittableExtrinsic as SubmittableExtrinsicBase } from '../SubmittableExtrinsic';
|
||||
import { SubmittableExtrinsic as SubmittableExtrinsicBase } from '../submittable/types';
|
||||
|
||||
export type QueryableStorageEntry = QueryableStorageEntryBase<'promise'>;
|
||||
export type SubmittableExtrinsic = SubmittableExtrinsicBase<'promise'>;
|
||||
|
||||
@@ -9,6 +9,10 @@ import { from, Observable } from 'rxjs';
|
||||
|
||||
import ApiBase from '../base';
|
||||
|
||||
function decorateMethod <Method extends AnyFunction> (method: Method): Method {
|
||||
return method;
|
||||
}
|
||||
|
||||
/**
|
||||
* # @polkadot/api/rx
|
||||
*
|
||||
@@ -157,7 +161,7 @@ export default class ApiRx extends ApiBase<'rxjs'> {
|
||||
* ```
|
||||
*/
|
||||
public constructor (options?: ApiOptions) {
|
||||
super(options, 'rxjs');
|
||||
super(options, 'rxjs', decorateMethod);
|
||||
|
||||
this._isReadyRx = from(
|
||||
// You can create an observable from an event, however my mind groks this form better
|
||||
@@ -192,8 +196,4 @@ export default class ApiRx extends ApiBase<'rxjs'> {
|
||||
source: this
|
||||
});
|
||||
}
|
||||
|
||||
protected decorateMethod<Method extends AnyFunction> (method: Method): Method {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
// 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 { ExtrinsicStatus, EventRecord } from '@polkadot/types/interfaces';
|
||||
import { SubmittableResultImpl, SubmittableResultValue } from './types';
|
||||
|
||||
export default class SubmittableResult implements SubmittableResultImpl {
|
||||
public readonly events: EventRecord[];
|
||||
|
||||
public readonly status: ExtrinsicStatus;
|
||||
|
||||
public constructor ({ events, status }: SubmittableResultValue) {
|
||||
this.events = events || [];
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public get isCompleted (): boolean {
|
||||
return this.isError || this.isFinalized;
|
||||
}
|
||||
|
||||
public get isError (): boolean {
|
||||
return this.status.isDropped || this.status.isInvalid || this.status.isUsurped;
|
||||
}
|
||||
|
||||
public get isFinalized (): boolean {
|
||||
return this.status.isFinalized;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Finds an EventRecord for the specified method & section
|
||||
*/
|
||||
public findRecord (section: string, method: string): EventRecord | undefined {
|
||||
return this.events.find(({ event }): boolean =>
|
||||
event.section === section && event.method === method
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
/* eslint-disable no-dupe-class-members */
|
||||
// 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 { AccountId, Address, Call, Extrinsic, ExtrinsicEra, ExtrinsicStatus, EventRecord, Hash, Header, Index } from '@polkadot/types/interfaces';
|
||||
import { Callback, Codec, Constructor, IKeyringPair, SignatureOptions } from '@polkadot/types/types';
|
||||
import { ApiInterfaceRx, ApiTypes, SignerResult } from '../types';
|
||||
import { SignerOptions, SubmittableExtrinsic, SubmittableResultImpl, SubmitableResultResult, SubmitableResultSubscription } from './types';
|
||||
|
||||
import { Observable, combineLatest, of } from 'rxjs';
|
||||
import { first, map, mergeMap, switchMap, tap } from 'rxjs/operators';
|
||||
import { createType, ClassOf, Vec } from '@polkadot/types';
|
||||
import { isBn, isFunction, isNumber, isUndefined } from '@polkadot/util';
|
||||
|
||||
import { filterEvents, isKeyringPair } from '../util';
|
||||
import ApiBase from '../base';
|
||||
import SubmittableResult from './Result';
|
||||
|
||||
interface SubmittableOptions<ApiType> {
|
||||
api: ApiInterfaceRx;
|
||||
decorateMethod: ApiBase<ApiType>['decorateMethod'];
|
||||
type: ApiTypes;
|
||||
}
|
||||
|
||||
// The default for 6s allowing for 5min eras. When translating this to faster blocks -
|
||||
// - 4s = (10 / 15) * 5 = 3.33m
|
||||
// - 2s = (10 / 30) * 5 = 1.66m
|
||||
const BLOCKTIME = 6;
|
||||
const ONE_MINUTE = 60 / BLOCKTIME;
|
||||
const DEFAULT_MORTAL_LENGTH = 5 * ONE_MINUTE;
|
||||
|
||||
const _Extrinsic: Constructor<Extrinsic> = ClassOf('Extrinsic');
|
||||
|
||||
export default class Submittable<ApiType> extends _Extrinsic implements SubmittableExtrinsic<ApiType> {
|
||||
private readonly _api: ApiInterfaceRx;
|
||||
|
||||
private readonly _decorateMethod: ApiBase<ApiType>['decorateMethod'];
|
||||
|
||||
private readonly _ignoreStatusCb: boolean;
|
||||
|
||||
public constructor (extrinsic: Call | Uint8Array | string, { api, decorateMethod, type }: SubmittableOptions<ApiType>) {
|
||||
super(extrinsic, { version: api.extrinsicType });
|
||||
|
||||
this._api = api;
|
||||
this._decorateMethod = decorateMethod;
|
||||
this._ignoreStatusCb = type === 'rxjs';
|
||||
}
|
||||
|
||||
// sign a transaction, returning the this to allow cianing, i.e. .sign(...).send()
|
||||
public sign (account: IKeyringPair, optionsOrNonce: Partial<SignerOptions>): this {
|
||||
// NOTE here we actually override nonce if it was specified (backwards compat for
|
||||
// the previous signature - don't let userspace break, but allow then time to upgrade)
|
||||
const options: Partial<SignerOptions> = isBn(optionsOrNonce) || isNumber(optionsOrNonce)
|
||||
? { nonce: optionsOrNonce }
|
||||
: optionsOrNonce;
|
||||
|
||||
super.sign(account, this._makeSignOptions(options, {}));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// signAndSend with an immediate Hash result
|
||||
public signAndSend(account: IKeyringPair | string | AccountId | Address, options?: Partial<SignerOptions>): SubmitableResultResult<ApiType>;
|
||||
|
||||
// signAndSend with a subscription, i.e. callback provided
|
||||
public signAndSend(account: IKeyringPair | string | AccountId | Address, statusCb: Callback<SubmittableResultImpl>): SubmitableResultSubscription<ApiType>;
|
||||
|
||||
// signAndSend with options and a callback
|
||||
public signAndSend(account: IKeyringPair | string | AccountId | Address, options: Partial<SignerOptions>, statusCb?: Callback<SubmittableResultImpl>): SubmitableResultSubscription<ApiType>;
|
||||
|
||||
// signAndSend implementation for all 3 cases above
|
||||
public signAndSend (account: IKeyringPair | string | AccountId | Address, optionsOrStatus?: Partial<SignerOptions> | Callback<SubmittableResultImpl>, optionalStatusCb?: Callback<SubmittableResultImpl>): SubmitableResultResult<ApiType> | SubmitableResultSubscription<ApiType> {
|
||||
const [options, statusCb] = this._makeSignAndSendOptions(optionsOrStatus, optionalStatusCb);
|
||||
const isSubscription = this._ignoreStatusCb || !!statusCb;
|
||||
const address = isKeyringPair(account) ? account.address : account.toString();
|
||||
let updateId: number | undefined;
|
||||
|
||||
return this._decorateMethod(
|
||||
(): Observable<Codec> => (
|
||||
this._getPrelimState(address, options).pipe(
|
||||
first(),
|
||||
mergeMap(async ([nonce, header]): Promise<void> => {
|
||||
const eraOptions = this._makeEraOptions(options, { header, nonce });
|
||||
|
||||
if (isKeyringPair(account)) {
|
||||
this.sign(account, eraOptions);
|
||||
} else {
|
||||
updateId = await this._signViaSigner(address, eraOptions, header);
|
||||
}
|
||||
}),
|
||||
switchMap((): Observable<SubmittableResultImpl> | Observable<Hash> => {
|
||||
return isSubscription
|
||||
? this._subscribeObservable(updateId)
|
||||
: this._sendObservable(updateId);
|
||||
})
|
||||
) as Observable<Codec>) // FIXME This is wrong, SubmittableResult is _not_ a codec
|
||||
)(statusCb) as SubmitableResultResult<ApiType> | SubmitableResultSubscription<ApiType>;
|
||||
}
|
||||
|
||||
// send with an immediate Hash result
|
||||
public send (): SubmitableResultResult<ApiType>;
|
||||
|
||||
// send with a status callback
|
||||
public send (statusCb: Callback<SubmittableResultImpl>): SubmitableResultSubscription<ApiType>;
|
||||
|
||||
// send implementation for both immediate Hash and statusCb variants
|
||||
public send (statusCb?: Callback<SubmittableResultImpl>): SubmitableResultResult<ApiType> | SubmitableResultSubscription<ApiType> {
|
||||
const isSubscription = this._ignoreStatusCb || !!statusCb;
|
||||
|
||||
return this._decorateMethod(
|
||||
isSubscription
|
||||
? this._subscribeObservable
|
||||
: this._sendObservable
|
||||
)(statusCb);
|
||||
}
|
||||
|
||||
private _makeSignAndSendOptions (optionsOrStatus?: Partial<SignerOptions> | Callback<SubmittableResultImpl>, statusCb?: Callback<SubmittableResultImpl>): [Partial<SignerOptions>, Callback<SubmittableResultImpl>?] {
|
||||
let options: Partial<SignerOptions> = {};
|
||||
|
||||
if (isFunction(optionsOrStatus)) {
|
||||
statusCb = optionsOrStatus;
|
||||
} else {
|
||||
options = { ...optionsOrStatus };
|
||||
}
|
||||
|
||||
return [options, statusCb];
|
||||
}
|
||||
|
||||
private async _signViaSigner (address: string, optionsWithEra: SignatureOptions, header: Header | null): Promise<number> {
|
||||
if (!this._api.signer) {
|
||||
throw new Error('no signer attached');
|
||||
}
|
||||
|
||||
const payload = createType('SignerPayload', {
|
||||
...optionsWithEra,
|
||||
address,
|
||||
method: this.method,
|
||||
blockNumber: header ? header.number : 0
|
||||
});
|
||||
let result: SignerResult;
|
||||
|
||||
if (this._api.signer.signPayload) {
|
||||
result = await this._api.signer.signPayload(payload.toPayload());
|
||||
} else if (this._api.signer.signRaw) {
|
||||
result = await this._api.signer.signRaw(payload.toRaw());
|
||||
} else {
|
||||
throw new Error('Invalid signer interface, it should implement either signPayload or signRaw (or both)');
|
||||
}
|
||||
|
||||
// Here we explicitly call `toPayload()` again instead of working with an object
|
||||
// (reference) as passed to the signer. This means that we are sure that the
|
||||
// payload data is not modified from our inputs, but the signer
|
||||
super.addSignature(address, result.signature, payload.toPayload());
|
||||
|
||||
return result.id;
|
||||
}
|
||||
|
||||
private _makeSignOptions (options: Partial<SignerOptions>, extras: { blockHash?: Hash; era?: ExtrinsicEra; nonce?: Index }): SignatureOptions {
|
||||
return {
|
||||
blockHash: this._api.genesisHash,
|
||||
...options,
|
||||
...extras,
|
||||
genesisHash: this._api.genesisHash,
|
||||
runtimeVersion: this._api.runtimeVersion,
|
||||
version: this._api.extrinsicType
|
||||
} as unknown as SignatureOptions;
|
||||
}
|
||||
|
||||
private _makeEraOptions (options: Partial<SignerOptions>, { header, nonce }: { header: Header | null; nonce: Index }): SignatureOptions {
|
||||
if (!header) {
|
||||
if (isNumber(options.era)) {
|
||||
// since we have no header, it is immortal, remove any option overrides
|
||||
// so we only supply the genesisHash and no era to the construction
|
||||
delete options.era;
|
||||
delete options.blockHash;
|
||||
}
|
||||
|
||||
return this._makeSignOptions(options, { nonce });
|
||||
}
|
||||
|
||||
return this._makeSignOptions(options, {
|
||||
blockHash: header.hash,
|
||||
era: createType('ExtrinsicEra', {
|
||||
current: header.number,
|
||||
period: options.era || DEFAULT_MORTAL_LENGTH
|
||||
}),
|
||||
nonce
|
||||
});
|
||||
}
|
||||
|
||||
private _getPrelimState (address: string, options: Partial<SignerOptions>): Observable<[Index, Header | null]> {
|
||||
return combineLatest([
|
||||
// if we have a nonce already, don't retrieve the latest, use what is there
|
||||
isUndefined(options.nonce)
|
||||
? this._api.query.system.accountNonce<Index>(address)
|
||||
: of(createType('Index', options.nonce)),
|
||||
// if we have an era provided already or eraLength is <= 0 (immortal)
|
||||
// don't get the latest block, just pass null, handle in mergeMap
|
||||
(isUndefined(options.era) || (isNumber(options.era) && options.era > 0))
|
||||
? this._api.rpc.chain.getHeader()
|
||||
: of(null)
|
||||
]);
|
||||
}
|
||||
|
||||
private _updateSigner (updateId: number, status: Hash | SubmittableResultImpl): void {
|
||||
if ((updateId !== -1) && this._api.signer && this._api.signer.update) {
|
||||
this._api.signer.update(updateId, status);
|
||||
}
|
||||
}
|
||||
|
||||
private _statusObservable (status: ExtrinsicStatus): Observable<SubmittableResultImpl> {
|
||||
if (!status.isFinalized) {
|
||||
return of(new SubmittableResult({ status }));
|
||||
}
|
||||
|
||||
const blockHash = status.asFinalized;
|
||||
|
||||
return combineLatest([
|
||||
this._api.rpc.chain.getBlock(blockHash),
|
||||
this._api.query.system.events.at(blockHash) as Observable<Vec<EventRecord>>
|
||||
]).pipe(
|
||||
map(([signedBlock, allEvents]): SubmittableResultImpl =>
|
||||
new SubmittableResult({
|
||||
events: filterEvents(this.hash, signedBlock, allEvents),
|
||||
status
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private _sendObservable = (updateId = -1): Observable<Hash> => {
|
||||
return this._api.rpc.author
|
||||
.submitExtrinsic(this)
|
||||
.pipe(
|
||||
tap((hash): void => {
|
||||
this._updateSigner(updateId, hash);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private _subscribeObservable = (updateId = -1): Observable<SubmittableResultImpl> => {
|
||||
return this._api.rpc.author
|
||||
.submitAndWatchExtrinsic(this)
|
||||
.pipe(
|
||||
switchMap((status): Observable<SubmittableResultImpl> =>
|
||||
this._statusObservable(status)
|
||||
),
|
||||
tap((status): void => {
|
||||
this._updateSigner(updateId, status);
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// 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 { Call } from '@polkadot/types/interfaces';
|
||||
import { Constructor } from '@polkadot/types/types';
|
||||
import { ApiInterfaceRx, ApiTypes } from '../types';
|
||||
import { SubmittableExtrinsic } from './types';
|
||||
|
||||
import ApiBase from '../base';
|
||||
|
||||
type Creator<ApiType> = (extrinsic: Call | Uint8Array | string) => SubmittableExtrinsic<ApiType>;
|
||||
|
||||
let Submittable: Constructor<SubmittableExtrinsic<any>>;
|
||||
|
||||
export default function createSubmittable<ApiType> (type: ApiTypes, api: ApiInterfaceRx, decorateMethod: ApiBase<ApiType>['decorateMethod']): Creator<ApiType> {
|
||||
return (extrinsic: Call | Uint8Array | string): SubmittableExtrinsic<ApiType> => {
|
||||
// HACK This is not great, but basically what we do here is to lazily only require the class
|
||||
// right at the point it is actually needed - delaying initialization
|
||||
if (!Submittable) {
|
||||
Submittable = require('./Submittable').default;
|
||||
}
|
||||
|
||||
return new Submittable(extrinsic, { api, decorateMethod, type });
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// 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.
|
||||
|
||||
export { default as createSubmittable } from './createSubmittable';
|
||||
export { default as SubmittableResult } from './Result';
|
||||
@@ -0,0 +1,54 @@
|
||||
// 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 { AccountId, Address, ExtrinsicStatus, EventRecord, Hash } from '@polkadot/types/interfaces';
|
||||
import { AnyNumber, AnyU8a, Callback, IExtrinsic, IExtrinsicEra, IKeyringPair, SignatureOptions } from '@polkadot/types/types';
|
||||
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
export interface SubmittableResultImpl {
|
||||
readonly events: EventRecord[];
|
||||
readonly status: ExtrinsicStatus;
|
||||
readonly isCompleted: boolean;
|
||||
readonly isError: boolean;
|
||||
readonly isFinalized: boolean;
|
||||
|
||||
findRecord(section: string, method: string): EventRecord | undefined;
|
||||
}
|
||||
|
||||
export interface SubmittableResultValue {
|
||||
events?: EventRecord[];
|
||||
status: ExtrinsicStatus;
|
||||
}
|
||||
|
||||
export type SubmitableResultResult<ApiType> =
|
||||
ApiType extends 'rxjs'
|
||||
? Observable<SubmittableResultImpl>
|
||||
: Promise<Hash>;
|
||||
|
||||
export type SubmitableResultSubscription<ApiType> =
|
||||
ApiType extends 'rxjs'
|
||||
? Observable<SubmittableResultImpl>
|
||||
: Promise<() => void>;
|
||||
|
||||
export interface SignerOptions {
|
||||
blockHash: AnyU8a;
|
||||
era?: IExtrinsicEra | number;
|
||||
nonce: AnyNumber;
|
||||
tip?: AnyNumber;
|
||||
}
|
||||
|
||||
export interface SubmittableExtrinsic<ApiType> extends IExtrinsic {
|
||||
send(): SubmitableResultResult<ApiType>;
|
||||
|
||||
send(statusCb: Callback<SubmittableResultImpl>): SubmitableResultSubscription<ApiType>;
|
||||
|
||||
sign(account: IKeyringPair, _options: Partial<SignatureOptions>): this;
|
||||
|
||||
signAndSend(account: IKeyringPair | string | AccountId | Address, options?: Partial<SignerOptions>): SubmitableResultResult<ApiType>;
|
||||
|
||||
signAndSend(account: IKeyringPair | string | AccountId | Address, statusCb: Callback<SubmittableResultImpl>): SubmitableResultSubscription<ApiType>;
|
||||
|
||||
signAndSend(account: IKeyringPair | string | AccountId | Address, options: Partial<SignerOptions>, statusCb?: Callback<SubmittableResultImpl>): SubmitableResultSubscription<ApiType>;
|
||||
}
|
||||
+16
-86
@@ -3,7 +3,8 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { Hash, RuntimeVersion } from '@polkadot/types/interfaces';
|
||||
import { AnyFunction, Callback, CallFunction, Codec, CodecArg, IExtrinsic, RegistryTypes, SignatureOptions } from '@polkadot/types/types';
|
||||
import { AnyFunction, Callback, CallFunction, Codec, CodecArg, RegistryTypes, SignatureOptions, SignerPayloadJSON, SignerPayloadRaw } from '@polkadot/types/types';
|
||||
import { SubmittableResultImpl, SubmittableExtrinsic } from './submittable/types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import { Observable } from 'rxjs';
|
||||
@@ -15,7 +16,8 @@ import { Metadata, u64 } from '@polkadot/types';
|
||||
import { StorageEntry } from '@polkadot/types/primitive/StorageKey';
|
||||
|
||||
import ApiBase from './base';
|
||||
import { ISubmittableResult, SubmittableExtrinsic } from './SubmittableExtrinsic';
|
||||
|
||||
export * from './submittable/types';
|
||||
|
||||
// Prepend an element V onto the beginning of a tuple T.
|
||||
// Cons<1, [2,3,4]> is [1,2,3,4]
|
||||
@@ -48,6 +50,8 @@ export interface DecorateMethodOptions {
|
||||
methodName?: string;
|
||||
}
|
||||
|
||||
export type DecorateMethod = (method: (...args: any[]) => Observable<any>, options?: DecorateMethodOptions) => any;
|
||||
|
||||
// Here are the return types of these parts of the api:
|
||||
// - api.query.*.*: no exact typings
|
||||
// - api.tx.*.*: SubmittableExtrinsic<ApiType>
|
||||
@@ -184,6 +188,14 @@ export interface ApiOptions {
|
||||
* uses types not available in the base Substrate runtime.
|
||||
*/
|
||||
types?: RegistryTypes;
|
||||
/**
|
||||
* @description Additional types that are injected based on the chain we are connecting to. There are keyed by the chain, i.e. `{ 'Kusama CC1': { ... } }`
|
||||
*/
|
||||
typesChain?: Record<string, RegistryTypes>;
|
||||
/**
|
||||
* @description Additional types that are injected based on the type of node we are connecting to, as set via specName in the runtime version. There are keyed by the node, i.e. `{ 'edgeware': { ... } }`
|
||||
*/
|
||||
typesSpec?: Record<string, RegistryTypes>;
|
||||
}
|
||||
|
||||
// A smaller interface of ApiRx, used in derive and in SubmittableExtrinsic
|
||||
@@ -210,82 +222,6 @@ export interface SignerOptions extends SignatureOptions {
|
||||
genesisHash: Hash;
|
||||
}
|
||||
|
||||
export interface SignerPayload {
|
||||
/**
|
||||
* @description The ss-58 encoded address
|
||||
*/
|
||||
address: string;
|
||||
|
||||
/**
|
||||
* @description The checkpoint hash of the block, in hex
|
||||
*/
|
||||
blockHash: string;
|
||||
|
||||
/**
|
||||
* @description The checkpoint block number, in hex
|
||||
*/
|
||||
blockNumber: string;
|
||||
|
||||
/**
|
||||
* @description The era for this transaction, in hex
|
||||
*/
|
||||
era: string;
|
||||
|
||||
/**
|
||||
* @description The genesis hash of the chain, in hex
|
||||
*/
|
||||
genesisHash: string;
|
||||
|
||||
/**
|
||||
* @description The encoded method (with arguments) in hex
|
||||
*/
|
||||
method: string;
|
||||
|
||||
/**
|
||||
* @description The nonce for this transaction, in hex
|
||||
*/
|
||||
nonce: string;
|
||||
|
||||
/**
|
||||
* @description The current spec version for the runtime
|
||||
*/
|
||||
specVersion: string;
|
||||
|
||||
/**
|
||||
* @description The tip for this transaction, in hex
|
||||
*/
|
||||
tip: string;
|
||||
|
||||
/**
|
||||
* @description The version of the extrinsic we are dealing with
|
||||
*/
|
||||
version: number;
|
||||
}
|
||||
|
||||
export interface SignerPayloadRawBase {
|
||||
/**
|
||||
* @description The hex-encoded data for this request
|
||||
*/
|
||||
data: string;
|
||||
|
||||
/**
|
||||
* @description The type of the contained data
|
||||
*/
|
||||
type?: 'bytes' | 'payload';
|
||||
}
|
||||
|
||||
export interface SignerPayloadRaw extends SignerPayloadRawBase {
|
||||
/**
|
||||
* @description The ss-58 encoded address
|
||||
*/
|
||||
address: string;
|
||||
|
||||
/**
|
||||
* @description The type of the contained data
|
||||
*/
|
||||
type: 'bytes' | 'payload';
|
||||
}
|
||||
|
||||
export interface SignerResult {
|
||||
/**
|
||||
* @description The id for this request
|
||||
@@ -299,16 +235,10 @@ export interface SignerResult {
|
||||
}
|
||||
|
||||
export interface Signer {
|
||||
/**
|
||||
* @deprecated Implement and use signPayload and/or signRaw instead
|
||||
* @description Signs an extrinsic, returning an id (>0) that can be used to retrieve updates
|
||||
*/
|
||||
sign?: (extrinsic: IExtrinsic, address: string, options: SignerOptions) => Promise<number>;
|
||||
|
||||
/**
|
||||
* @description signs an extrinsic payload from a serialized form
|
||||
*/
|
||||
signPayload?: (payload: SignerPayload) => Promise<SignerResult>;
|
||||
signPayload?: (payload: SignerPayloadJSON) => Promise<SignerResult>;
|
||||
|
||||
/**
|
||||
* @description signs a raw payload, only the bytes data as supplied
|
||||
@@ -318,5 +248,5 @@ export interface Signer {
|
||||
/**
|
||||
* @description Receives an update for the extrinsic signed by a `signer.sign`
|
||||
*/
|
||||
update?: (id: number, status: Hash | ISubmittableResult) => void;
|
||||
update?: (id: number, status: Hash | SubmittableResultImpl) => void;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
// 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.
|
||||
|
||||
export * from './decorate';
|
||||
export { default as filterEvents } from './filterEvents';
|
||||
export { default as isKeyringPair } from './isKeyringPair';
|
||||
export { default as l } from './logging';
|
||||
@@ -0,0 +1,12 @@
|
||||
// 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 { AccountId, Address } from '@polkadot/types/interfaces';
|
||||
import { IKeyringPair } from '@polkadot/types/types';
|
||||
|
||||
import { isFunction } from '@polkadot/util';
|
||||
|
||||
export default function isKeyringPair (account: string | IKeyringPair | AccountId | Address): account is IKeyringPair {
|
||||
return isFunction((account as IKeyringPair).sign);
|
||||
}
|
||||
@@ -73,7 +73,7 @@ describeE2E({
|
||||
}
|
||||
|
||||
expect(info.accountId.eq(accountId)).toBe(true);
|
||||
expect(info.controllerId!.eq('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY')).toBe(true);
|
||||
expect(info.controllerId.eq('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY')).toBe(true);
|
||||
expect(info.stashId.eq(accountId)).toBe(true);
|
||||
expect(info.stashId.eq(info.stakingLedger.stash)).toBe(true);
|
||||
|
||||
@@ -142,17 +142,19 @@ describeE2E({
|
||||
let count = 0; // The # of times we got a callback response from api.derive.staking.info
|
||||
|
||||
// Subscribe to staking.info
|
||||
api.derive.staking.info(stashId, (result): void => {
|
||||
++count;
|
||||
api.derive.staking
|
||||
.info(stashId, (result): void => {
|
||||
++count;
|
||||
|
||||
console.error('***', count, JSON.stringify(result));
|
||||
console.error('***', count, JSON.stringify(result));
|
||||
|
||||
if (count >= 2 && result.rewardDestination!.toString() === 'Stash') {
|
||||
done();
|
||||
}
|
||||
}).catch(console.error);
|
||||
if (count >= 2 && result.rewardDestination!.toString() === 'Stash') {
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
// Wait a bit, and change reward destination
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
setTimeout(async (): Promise<void> => {
|
||||
await api.tx.staking
|
||||
.setPayee('Stash')
|
||||
|
||||
@@ -44,8 +44,7 @@ describeE2E({
|
||||
// (and it is assuming it sent at least 1 tx)
|
||||
describe('derive.accounts', (): void => {
|
||||
describe('idAndIndex', (): void => {
|
||||
it('looks up AccountId & AccountIndex from AccountId', async (done): Promise<void> => {
|
||||
// @ts-ignore silence warning until we have static types here
|
||||
it('looks up AccountId & AccountIndex from AccountId', (done): void => {
|
||||
api.derive.accounts.idAndIndex(ID).subscribe(([accountId, accountIndex]): void => {
|
||||
expect(accountId!.toString()).toEqual(ID);
|
||||
// The first emitted value for ix is undefined when passing the ID
|
||||
@@ -58,8 +57,7 @@ describeE2E({
|
||||
});
|
||||
});
|
||||
|
||||
it('looks up AccountId & AccountIndex from AccountIndex', async (done): Promise<void> => {
|
||||
// @ts-ignore silence warning until we have static types here
|
||||
it('looks up AccountId & AccountIndex from AccountIndex', (done): void => {
|
||||
api.derive.accounts.idAndIndex(IX).subscribe(([accountId, accountIndex]): void => {
|
||||
// The first emitted value for id is undefined when passing the IX
|
||||
if (accountId) {
|
||||
@@ -74,8 +72,7 @@ describeE2E({
|
||||
});
|
||||
|
||||
describe('indexToId', (): void => {
|
||||
it('looks up AccountId from AccountIndex', async (done): Promise<void> => {
|
||||
// @ts-ignore silence warning until we have static types here
|
||||
it('looks up AccountId from AccountIndex', (done): void => {
|
||||
api.derive.accounts.indexToId(IX).subscribe((accountId): void => {
|
||||
// The first emitted value for accountId is undefined when passing the IX
|
||||
if (accountId) {
|
||||
@@ -90,8 +87,7 @@ describeE2E({
|
||||
});
|
||||
|
||||
describe('idToIndex', (): void => {
|
||||
it('looks up AccountIndex from AccountId', async (done): Promise<void> => {
|
||||
// @ts-ignore silence warning until we have static types here
|
||||
it('looks up AccountIndex from AccountId', (done): void => {
|
||||
api.derive.accounts.idToIndex(ID).subscribe((accountIndex): void => {
|
||||
// The first emitted value for AccountIndex is undefined when passing the ID
|
||||
if (accountIndex) {
|
||||
@@ -106,8 +102,7 @@ describeE2E({
|
||||
});
|
||||
|
||||
describe('indexes', (): void => {
|
||||
it('looks up all AccountIndexes', async (done): Promise<void> => {
|
||||
// @ts-ignore silence warning until we have static types here
|
||||
it('looks up all AccountIndexes', (done): void => {
|
||||
api.derive.accounts.indexes().subscribe((accountIndexes): void => {
|
||||
// A local dev chain should have the AccountIndex of Alice
|
||||
expect(accountIndexes).toHaveProperty(
|
||||
@@ -124,7 +119,7 @@ describeE2E({
|
||||
// (and it is assuming it sent at least 1 tx)
|
||||
describe('derive.balances', (): void => {
|
||||
describe('all', (): void => {
|
||||
it('It returns an object with all relevant balance information of an account', async (done): Promise<void> => {
|
||||
it('It returns an object with all relevant balance information of an account', (done): void => {
|
||||
api.derive.balances.all(ID).subscribe((balances: DerivedBalances): void => {
|
||||
expect(balances).toEqual(expect.objectContaining({
|
||||
accountId: expect.any(ClassOf('AccountId')),
|
||||
@@ -142,7 +137,7 @@ describeE2E({
|
||||
});
|
||||
|
||||
describe('fees', (): void => {
|
||||
it('fees: It returns an object with all relevant fees of type BN', async (done): Promise<void> => {
|
||||
it('fees: It returns an object with all relevant fees of type BN', (done): void => {
|
||||
api.derive.balances.fees().subscribe((fees: DerivedFees): void => {
|
||||
expect(fees).toEqual(expect.objectContaining({
|
||||
creationFee: expect.any(BN),
|
||||
@@ -159,7 +154,7 @@ describeE2E({
|
||||
|
||||
describe('derive.chain', (): void => {
|
||||
describe('bestNumber', (): void => {
|
||||
it('Get the latest block number', async (done): Promise<void> => {
|
||||
it('Get the latest block number', (done): void => {
|
||||
api.derive.chain.bestNumber().subscribe((blockNumber: BlockNumber): void => {
|
||||
expect(blockNumber instanceof ClassOf('BlockNumber')).toBe(true);
|
||||
expect(blockNumber.gten(0)).toBe(true);
|
||||
@@ -169,7 +164,7 @@ describeE2E({
|
||||
});
|
||||
|
||||
describe('bestNumberFinalized', (): void => {
|
||||
it('Get the latest finalised block number', async (done): Promise<void> => {
|
||||
it('Get the latest finalised block number', (done): void => {
|
||||
api.derive.chain.bestNumberFinalized().subscribe((blockNumber: BlockNumber): void => {
|
||||
expect(blockNumber instanceof ClassOf('BlockNumber')).toBe(true);
|
||||
expect(blockNumber.gten(0)).toBe(true);
|
||||
@@ -179,7 +174,7 @@ describeE2E({
|
||||
});
|
||||
|
||||
describe('bestNumberLag', (): void => {
|
||||
it('lag between finalised head and best head', async (done): Promise<void> => {
|
||||
it('lag between finalised head and best head', (done): void => {
|
||||
api.derive.chain.bestNumberLag().subscribe((numberLag: BlockNumber): void => {
|
||||
expect(numberLag instanceof ClassOf('BlockNumber')).toBe(true);
|
||||
expect(numberLag.gten(0)).toBe(true);
|
||||
@@ -190,7 +185,7 @@ describeE2E({
|
||||
|
||||
// FIXME https://github.com/polkadot-js/api/issues/868
|
||||
describe('getHeader', (): void => {
|
||||
it('gets a specific block header and extended with it`s author', async (done): Promise<void> => {
|
||||
it('gets a specific block header and extended with it`s author', (done): void => {
|
||||
api.derive.chain.getHeader('TODO').subscribe((headerExtended: HeaderExtended | undefined): void => {
|
||||
// WIP
|
||||
expect(headerExtended).toEqual(expect.arrayContaining([]));
|
||||
@@ -200,7 +195,7 @@ describeE2E({
|
||||
});
|
||||
|
||||
describe('subscribeNewHeads', (): void => {
|
||||
it('gets an observable of the current block header and it\'s author', async (done): Promise<void> => {
|
||||
it('gets an observable of the current block header and it\'s author', (done): void => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
api.derive.chain.subscribeNewHeads().subscribe((headerExtended: HeaderExtended): void => {
|
||||
// WIP https://github.com/polkadot-js/api/issues/868
|
||||
@@ -212,7 +207,7 @@ describeE2E({
|
||||
|
||||
describe('derive.contracts', (): void => {
|
||||
describe('fees', (): void => {
|
||||
it('fees: It returns an object with all relevant constract fees of type Balance', async (done): Promise<void> => {
|
||||
it('fees: It returns an object with all relevant constract fees of type Balance', (done): void => {
|
||||
api.derive.contracts.fees().subscribe((fees: DerivedContractFees): void => {
|
||||
expect(fees).toEqual(expect.objectContaining({
|
||||
callBaseFee: expect.any(BN),
|
||||
@@ -234,7 +229,7 @@ describeE2E({
|
||||
|
||||
describe('derive.elections', (): void => {
|
||||
describe('info', (): void => {
|
||||
it('It returns an object with all relevant elections properties', async (done): Promise<void> => {
|
||||
it('It returns an object with all relevant elections properties', (done): void => {
|
||||
api.derive.elections.info().subscribe((info: DerivedElectionsInfo): void => {
|
||||
expect(info).toEqual(expect.objectContaining({
|
||||
members: expect.anything(),
|
||||
@@ -252,7 +247,7 @@ describeE2E({
|
||||
|
||||
describe('derive.session', (): void => {
|
||||
describe('sessionProgress', (): void => {
|
||||
it('derive.session.sessionProgress', async (done): Promise<void> => {
|
||||
it('derive.session.sessionProgress', (done): void => {
|
||||
api.derive.session.sessionProgress().subscribe((progress: BN): void => {
|
||||
expect(progress instanceof BN).toBe(true);
|
||||
done();
|
||||
@@ -261,7 +256,7 @@ describeE2E({
|
||||
});
|
||||
|
||||
describe('session.info', (): void => {
|
||||
it('retrieves all session info', async (done): Promise<void> => {
|
||||
it('retrieves all session info', (done): void => {
|
||||
api.derive.session.info().subscribe((info: DerivedSessionInfo): void => {
|
||||
expect(info).toEqual(expect.objectContaining({
|
||||
currentEra: expect.anything(),
|
||||
@@ -280,7 +275,7 @@ describeE2E({
|
||||
});
|
||||
|
||||
describe('session.eraLength', (): void => {
|
||||
it('derive.session.eraLength', async (done): Promise<void> => {
|
||||
it('derive.session.eraLength', (done): void => {
|
||||
api.derive.session.eraLength().subscribe((length: BN): void => {
|
||||
expect(length instanceof BN).toBe(true);
|
||||
done();
|
||||
@@ -289,7 +284,7 @@ describeE2E({
|
||||
});
|
||||
|
||||
describe('session.eraProgress', (): void => {
|
||||
it('derive.session.eraProgress', async (done): Promise<void> => {
|
||||
it('derive.session.eraProgress', (done): void => {
|
||||
api.derive.session.eraProgress().subscribe((progress: BN): void => {
|
||||
expect(progress instanceof BN).toBe(true);
|
||||
done();
|
||||
|
||||
@@ -36,26 +36,24 @@ describeE2E({
|
||||
});
|
||||
|
||||
// https://github.com/polkadot-js/api/issues/846
|
||||
it('handles toJSON with no issues', async (done): Promise<() => void> => {
|
||||
return (
|
||||
api.rpc.chain.getBlock('0x85c62b581f38cb81c3e443d34392672beb1fb877017fd7237cc87704113259dc', (result: SignedBlock): void => {
|
||||
const failed: Extrinsic[] = result.block.extrinsics.filter((extrinsic: Extrinsic): boolean => {
|
||||
try {
|
||||
const json = extrinsic.method.toJSON();
|
||||
it('handles toJSON with no issues', (done): Promise<() => void> => {
|
||||
return api.rpc.chain.getBlock('0x85c62b581f38cb81c3e443d34392672beb1fb877017fd7237cc87704113259dc', (result: SignedBlock): void => {
|
||||
const failed: Extrinsic[] = result.block.extrinsics.filter((extrinsic: Extrinsic): boolean => {
|
||||
try {
|
||||
const json = extrinsic.method.toJSON();
|
||||
|
||||
console.error(json);
|
||||
console.error(json);
|
||||
|
||||
return false;
|
||||
} catch (error) {
|
||||
console.log(extrinsic.method);
|
||||
console.log(extrinsic.method.keys());
|
||||
console.error(error);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
expect(failed).toBeTruthy();
|
||||
done();
|
||||
})
|
||||
);
|
||||
return false;
|
||||
} catch (error) {
|
||||
console.log(extrinsic.method);
|
||||
console.log(extrinsic.method.keys());
|
||||
console.error(error);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
expect(failed).toBeTruthy();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { AccountId, EventRecord, Header } from '@polkadot/types/interfaces';
|
||||
import { AccountId, EventRecord } from '@polkadot/types/interfaces';
|
||||
|
||||
import WsProvider from '@polkadot/rpc-provider/ws';
|
||||
import { ClassOf, Option, Vec } from '@polkadot/types';
|
||||
@@ -73,7 +73,7 @@ describeE2E({
|
||||
});
|
||||
|
||||
it('makes a query at a latest block (specified)', async (): Promise<void> => {
|
||||
const header = await api.rpc.chain.getHeader() as Header;
|
||||
const header = await api.rpc.chain.getHeader();
|
||||
const events = await api.query.system.events.at(header.hash) as Vec<EventRecord>;
|
||||
|
||||
expect(events.length).not.toEqual(0);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user