Add .eq() usage to docs and fix docs typos (#1405)

* Use `eq` for example

* Use the base `eq` function rather than `===`, so that comparisons are made in a consistent manner in docs

* Add `eq` example

* Add an example of using the `eq` method to the base docs

* Adjust docs/examples

* Fix a number of typos in the static docs and examples
* Adjust various comments in docs examples such that they are written in a consistent case

* Fix metadata docs spacing

* Adjust the metadata documentation generation such that unnecessary spaces are trimmed
This commit is contained in:
James Lefrère
2019-09-19 11:43:47 +02:00
committed by Jaco Greeff
parent 229fd72526
commit 73d7a57d38
29 changed files with 152 additions and 130 deletions
@@ -10,7 +10,7 @@ async function main () {
// the API has connected to the node and completed the initialisation process
const api = await ApiPromise.create();
// we only display a couple, then unsubscribe
// We only display a couple, then unsubscribe
let count = 0;
// Subscribe to the new headers on-chain. The callback is fired when new headers
@@ -8,7 +8,7 @@ async function main () {
// Create a new instance of the api
const api = await ApiPromise.create();
// Subscribe to chain updates and log the current block number on update.
// Subscribe to chain updates and log the current block number on update.
const unsubscribe = await api.rpc.chain.subscribeNewHead((header) => {
console.log(`Chain is at block: #${header.number}`);
});
@@ -12,18 +12,18 @@ async function main () {
// Create our API with a default connection to the local node
const api = await ApiPromise.create();
// retrieve the last block header, extracting the hash and parentHash
// Retrieve the last block header, extracting the hash and parentHash
const { hash, parentHash } = await api.rpc.chain.getHeader();
console.log(`last header hash ${hash.toHex()}`);
// retrieve the balance at the preceding block for Alice. For at queries
// Retrieve the balance at the preceding block for Alice. For at queries
// the format is always `.at(<blockhash>, ...params)`
const balance = await api.query.balances.freeBalance.at(parentHash, ALICE);
console.log(`Alice's balance at ${parentHash.toHex()} was ${balance}`);
// now perform a multi query, returning multiple balances at once
// Now perform a multi query, returning multiple balances at once
const balances = await api.query.balances.freeBalance.multi([ALICE, BOB]);
console.log(`Current balances for Alice and Bob are ${balances[0]} and ${balances[1]}`);
@@ -14,7 +14,7 @@ async function main () {
// Constuct the keying after the API (crypto has an async init)
const keyring = new Keyring({ type: 'sr25519' });
// Add alice to our keyring with a hard-deived path (empty phrase, so uses dev)
// Add Alice to our keyring with a hard-deived path (empty phrase, so uses dev)
const alice = keyring.addFromUri('//Alice');
// Create a extrinsic, transferring 12345 units to Bob
@@ -15,16 +15,16 @@ async function main () {
// Constuct the keying after the API (crypto has an async init)
const keyring = new Keyring({ type: 'sr25519' });
// Add alice to our keyring with a hard-derived path (empty phrase, so uses dev)
// Add Alice to our keyring with a hard-derived path (empty phrase, so uses dev)
const alice = keyring.addFromUri('//Alice');
// Get nonce for account
const nonce = await api.query.system.accountNonce(alice.address);
// get current block
// Get current block
const signedBlock = await api.rpc.chain.getBlock();
// get current block height and hash
// Get current block height and hash
const currentHeight = signedBlock.block.header.number;
const blockHash = signedBlock.block.header.hash;
@@ -8,21 +8,21 @@ async function main () {
// Create our API with a default connection to the local node
const api = await ApiPromise.create();
// subscribe to system events via storage
// Subscribe to system events via storage
api.query.system.events((events) => {
console.log(`\nReceived ${events.length} events:`);
// loop through the Vec<EventRecord>
// Loop through the Vec<EventRecord>
events.forEach((record) => {
// extract the phase, event and the event types
// Extract the phase, event and the event types
const { event, phase } = record;
const types = event.typeDef;
// show what we are busy with
// Show what we are busy with
console.log(`\t${event.section}:${event.method}:: (phase=${phase.toString()})`);
console.log(`\t\t${event.meta.documentation.toString()}`);
// loop through each of the parameters, displaying the type and data
// Loop through each of the parameters, displaying the type and data
event.data.forEach((data, index) => {
console.log(`\t\t\t${types[index].type}: ${data.toString()}`);
});
@@ -4,13 +4,13 @@
// Import the API & Provider and some utility functions
const { ApiPromise } = require('@polkadot/api');
// import the test keyring (already has dev keys for Alice, Bob, Charlie, Eve & Ferdie)
// Import the test keyring (already has dev keys for Alice, Bob, Charlie, Eve & Ferdie)
const testKeyring = require('@polkadot/keyring/testing');
// utility function for random values
// Utility function for random values
const { randomAsU8a } = require('@polkadot/util-crypto');
// some constants we are using in this sample
// Some constants we are using in this sample
const ALICE = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
const AMOUNT = 10000;
@@ -18,18 +18,18 @@ async function main () {
// Create the API and wait until ready
const api = await ApiPromise.create();
// create an instance of our testing keyring
// Create an instance of our testing keyring
// If you're using ES6 module imports instead of require, just change this line to:
// const keyring = testKeyring();
const keyring = testKeyring.default();
// get the nonce for the admin key
// Get the nonce for the admin key
const nonce = await api.query.system.accountNonce(ALICE);
// find the actual keypair in the keyring
// Find the actual keypair in the keyring
const alicePair = keyring.getPair(ALICE);
// create a new random recipient
// Create a new random recipient
const recipient = keyring.addFromSeed(randomAsU8a(32)).address;
console.log('Sending', AMOUNT, 'from', alicePair.address, 'to', recipient, 'with nonce', nonce.toString());
@@ -16,22 +16,22 @@ async function main () {
// Create the API and wait until ready (optional provider passed through)
const api = await ApiPromise.create({ provider });
// retrieve the upgrade key from the chain state
// Retrieve the upgrade key from the chain state
const adminId = await api.query.sudo.key();
// find the actual keypair in the keyring (if this is an changed value, the key
// Find the actual keypair in the keyring (if this is a changed value, the key
// needs to be added to the keyring before - this assumes we have defaults, i.e.
// Alice as the key - and this already exists on the test keyring)
const keyring = testKeyring.default();
const adminPair = keyring.getPair(adminId.toString());
// retrieve the runtime to upgrade to
// Retrieve the runtime to upgrade to
const code = fs.readFileSync('./test.wasm').toString('hex');
const proposal = api.tx.consensus.setCode(`0x${code}`);
console.log(`Upgrading from ${adminId}, ${code.length / 2} bytes`);
// preform the actual chain upgrade via the sudo module
// Perform the actual chain upgrade via the sudo module
api.tx.sudo
.sudo(proposal)
.signAndSend(adminPair, ({ events = [], status }) => {
@@ -22,7 +22,7 @@ async function main () {
pairwise()
)
.subscribe((balance) => {
if (balance[0] === 'first') {
if (balance[0].eq('first')) {
// Now we know that if the previous value emitted as balance[0] is `first`,
// then balance[1] is the initial value of Alice account.
console.log(`Alice ${Alice} has a balance of ${balance[1]}`);
+1 -1
View File
@@ -7,7 +7,7 @@ const { switchMap } = require('rxjs/operators');
async function main () {
// Create a new instance of the api
// Subscribe to chain updates and log the current block number on update.
// Subscribe to chain updates and log the current block number on update.
const subscription = new ApiRx().isReady
.pipe(
switchMap((api) =>
+8 -8
View File
@@ -4,13 +4,13 @@
// Import the API and some utility functions
const { ApiRx } = require('@polkadot/api');
// import the test keyring (already has dev keys for Alice, Bob, Charlie, Eve & Ferdie)
// Import the test keyring (already has dev keys for Alice, Bob, Charlie, Eve & Ferdie)
const testKeyring = require('@polkadot/keyring/testing');
// utility function for random values
// Utility function for random values
const { randomAsU8a } = require('@polkadot/util-crypto');
// some constants we are using in this sample
// Some constants we are using in this sample
const ALICE = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
const AMOUNT = 10000;
@@ -18,21 +18,21 @@ async function main () {
// Create our API with a connection to the node
const api = await ApiRx.create().toPromise();
// create an instance of our testign keyring
// Create an instance of our testign keyring
// If you're using ES6 module imports instead of require, just change this line to:
// const keyring = testKeyring();
const keyring = testKeyring.default();
// find the actual keypair in the keyring
// Find the actual keypair in the keyring
const alicePair = keyring.getPair(ALICE);
// create a new random recipient
// Create a new random recipient
const recipient = keyring.addFromSeed(randomAsU8a(32)).address;
console.log('Sending', AMOUNT, 'from', alicePair.address, 'to', recipient);
// get the nonce for the admin key
// Create a extrinsic, transferring 12345 units to Bob.
// Get the nonce for the admin key
// Create a extrinsic, transferring 12345 units to Bob.
api.tx.balances
// Do the transfer
.transfer(recipient, AMOUNT)
+7 -7
View File
@@ -3,7 +3,7 @@
/* eslint-disable @typescript-eslint/no-var-requires */
// Import the API & Provider and some utility functions
const { ApiRx, WsProvider } = require('@polkadot/api');
// import the test keyring (already has dev keys for Alice, Bob, Charlie, Eve & Ferdie)
// Import the test keyring (already has dev keys for Alice, Bob, Charlie, Eve & Ferdie)
const testKeyring = require('@polkadot/keyring/testing');
const fs = require('fs');
@@ -14,27 +14,27 @@ async function main () {
// Create the API and wait until ready (optional provider passed through)
const api = await ApiRx.create({ provider }).toPromise();
// retrieve the upgrade key from the chain state
// Retrieve the upgrade key from the chain state
const adminId = await api.query.sudo.key().toPromise();
// find the actual keypair in the keyring (if this is an changed value, the key
// Find the actual keypair in the keyring (if this is an changed value, the key
// needs to be added to the keyring before - this assumes we have defaults, i.e.
// Alice as the key - and this already exists on the test keyring)
const keyring = testKeyring.default();
const adminPair = keyring.getPair(adminId.toString());
// retrieve the runtime to upgrade to
// Retrieve the runtime to upgrade to
const code = fs.readFileSync('./test.wasm').toString('hex');
const proposal = api.tx.consensus.setCode(`0x${code}`);
console.log(`Upgrading chain runtime from ${adminId}`);
api.tx.sudo
// preform the actual chain upgrade via the sudo module
// Perform the actual chain upgrade via the sudo module
.sudo(proposal)
// sign and send the proposal
// Sign and send the proposal
.signAndSend(adminPair)
// subscribe to overall result
// Subscribe to overall result
.subscribe(({ events = [], status }) => {
// Log transfer events
console.log('Proposal status:', status.type);
+1 -1
View File
@@ -4,7 +4,7 @@ These sections should provide you with all the information needed to install the
## 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...
This is not line-by-line documentation of all the existing 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
+4 -4
View File
@@ -5,16 +5,16 @@ Constant queries will introduce you to the concepts behind the types and the int
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
// Initialize the API as per previous sections
...
// the length of an epoch (session) in Babe
// The length of an epoch (session) in Babe
console.log(api.consts.babe.epochDuration.toNumber());
// the amount required to create a new account
// The amount required to create a new account
console.log(api.consts.balances.creationFee.toNumber());
// the amount required per byte on an extrinsic
// The amount required per byte on an extrinsic
console.log(api.consts.balances.transactionByteFee.toNumber());
```
+7 -7
View File
@@ -7,19 +7,19 @@ In previous sections, we initialized the API and retrieved runtime constants. Th
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
// Initialize the API as in previous sections
...
// the actual address that we will use
// The actual address that we will use
const ADDR = '5DTestUPts3kjeXSTMyerHihn1uwMfLj8vU8sqF7qYrFabHE';
// retrieve the last timestamp
// Retrieve the last timestamp
const now = await api.query.timestamp.now();
// retrieve the account nonce via the system module
// Retrieve the account nonce via the system module
const nonce = await api.query.system.accountNonce(ADDR);
// retrieve the account balance via the balances module
// Retrieve the account balance via the balances module
const balance = await api.query.balances.freeBalance(ADDR);
console.log(`${now}: balance of ${balance} and a nonce of ${nonce}`);
@@ -29,7 +29,7 @@ There have been some additions in the code above comparing with retrieving runti
```js
...
// retrieve last block timestamp, account nonce & balance
// Retrieve last block timestamp, account nonce & balance
const [now, nonce, balance] = await Promise.all([
api.query.timestamp.now(),
api.query.system.accountNonce(ADDR),
@@ -41,7 +41,7 @@ const [now, nonce, balance] = await Promise.all([
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.
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
+5 -5
View File
@@ -4,12 +4,12 @@ In a number of applications, it is useful to monitor a number of like-queries at
## 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 -
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)
// 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;
@@ -22,10 +22,10 @@ A couple of items to note in the example above: we don't call `freeBalance` dire
```js
...
// retrieve a snapshot of the validators
// Retrieve a snapshot of the validators
const validators = await api.query.session.validators();
// subscribe to the balances for these accounts
// Subscribe to the balances for these accounts
const unsub = await api.query.balances.freeBalance.multi(validators, (balances) => {
console.log(`The balances are: ${balances}`);
});
@@ -40,7 +40,7 @@ The previous `.multi` examples assumes that we do queries for the same types, i.
```js
...
// subscribe to the timestamp, our index and balance
// Subscribe to the timestamp, our index and balance
const unsub = await api.queryMulti([
api.query.timestamp.now,
[api.query.system.accountNonce, ADDR],
+9 -9
View File
@@ -9,16 +9,16 @@ Quite often is is useful (taking pruning into account, more on this later) to re
```js
...
// retrieve the current block header
// Retrieve the current block header
const lastHdr = await api.rpc.chain.getHeader();
// retrieve the balance at both the current and the parent hashes
// 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
// Display the difference
console.log(`The delta was ${balanceNow.sub(balancePrev)}`);
```
@@ -27,13 +27,13 @@ In the above example, we introduce the `.at(<hash>[, ...params])` query. For all
```js
...
// retrieve the timestamp for the previous block
// Retrieve the timestamp 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)
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 aggressive state pruning)
## State entries
@@ -42,13 +42,13 @@ In addition to using `api.query` to make actual on-chain queries, it can also be
```js
...
// retrieve the hash & size of the entry as stored on-chain
// Retrieve the hash & size of the entry as stored on-chain
const [entryHash, entrySize] = await Promise.all([
api.query.balances.freeBalance.hash(ADDR),
api.query.balances.freeBalance.size(ADDR)
]);
// output the info
// Output the info
console.log(`The current size is ${entrySize} bytes with a hash of ${entryHash}`);
```
@@ -59,10 +59,10 @@ As per the previous examples, the params here apply explicitly to the actual nee
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
// Extract the info
const { meta, method, section } = api.query.balances.freeBalance;
// display some info on a specific entry
// Display some info on a specific entry
console.log(`${section}.${method}: ${meta.documentation.join(' ')}`);
console.log(`query key: ${api.query.balances.freeBalance.key(ADDR)}`);
```
+2 -2
View File
@@ -9,7 +9,7 @@ As in the case with `api.rpc` subscriptions, query subscriptions follow exactly
```js
...
// retrieve the current timestamp via subscription
// Retrieve the current timestamp via subscription
const unsub = await api.query.timestamp.now((moment) => {
console.log(`The last block has a timestamp of ${moment}`);
});
@@ -24,7 +24,7 @@ If we had a query with parameters, i.e. where we wish to perform a query for a s
```js
...
// subscribe to balance changes for our account
// Subscribe to balance changes for our account
const unsub = await api.query.balances.freeBalance(ADDR, (balance) => {
console.log(`Your account balance is ${balance}`);
});
+6 -6
View File
@@ -7,13 +7,13 @@ Since you are already familiar with the `api.query` interface, the `api.rpc` int
```js
...
// retrieve the chain name
// Retrieve the chain name
const chain = await api.rpc.system.chain();
// retrieve the latest header
// Retrieve the latest header
const lastHeader = await api.rpc.chain.getHeader();
// log the information
// Log the information
console.log(`${chain}: last block #${lastHeader.number} has hash ${lastHeader.hash}`);
```
@@ -26,13 +26,13 @@ The RPCs lend themselves to using subscriptions, for instance in the above case
```js
...
// subscribe to the new headers
// 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.
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 -
@@ -40,7 +40,7 @@ In general, whenever we create a subscription, we would like to cleanup after ou
...
let count = 0;
// subscribe to the new headers
// Subscribe to the new headers
const unsubHeads = await api.rpc.chain.subscribeNewHeads((lastHeader) => {
console.log(`${chain}: last block #${lastHeader.number} has hash ${lastHeader.hash}`);
+3 -3
View File
@@ -9,12 +9,12 @@ To start off, let's make a balance transfer from Alice to Bob.
```js
...
// sign and send a transfer from Alice to Bob
// Sign and send a transfer from Alice to Bob
const txHash = await api.tx.balances
.transfer(BOB, 12345)
.signAndSend(alice);
// show the hash
// Show the hash
console.log(`Submitted with hash ${txHash}`);
```
@@ -26,7 +26,7 @@ The result for this call (we will deal with subscriptions in a short while), is
## 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) -
Despite the single-line format of `signAndSend`, there is a lot happening 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)
+5 -5
View File
@@ -9,10 +9,10 @@ To send a transaction and then waiting until it has been included in a block, we
```js
...
// create alice (carry-over from the keyring section)
// Create alice (carry-over from the keyring section)
const alice = keyring.addFromUri('//Alice');
// make a transfer from Alice to BOB, waiting for inclusion
// Make a transfer from Alice to BOB, waiting for inclusion
const unsub = await api.tx.balances
.transfer(BOB, 12345)
.signAndSend(alice, (result) => {
@@ -39,7 +39,7 @@ To display or act on these events, we can do the following -
```js
...
// make a transfer from Alice to BOB, waiting for inclusion
// Make a transfer from Alice to BOB, waiting for inclusion
const unsub = await api.tx.balances
.transfer(BOB, 12345)
.signAndSend(alice, ({ events = [], status }) => {
@@ -48,7 +48,7 @@ const unsub = await api.tx.balances
if (status.isFinalized) {
console.log(`Transaction included at blockHash ${status.asFinalized}`);
// loop through Vec<EventRecord> to display all events
// Loop through Vec<EventRecord> to display all events
events.forEach(({ phase, event: { data, method, section } }) => {
console.log(`\t' ${phase}: ${section}.${method}:: ${data}`);
});
@@ -58,7 +58,7 @@ const unsub = await api.tx.balances
});
```
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.
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
+4 -4
View File
@@ -8,13 +8,13 @@ When running a development chain (Polkadot/Substrate with a `--dev` flag), or in
```js
...
// get the current sudo key in the system
// 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`)
// Lookup from keyring (assuming we have added all, on --dev this would be `//Alice`)
const sudoPair = keyring.getPair(sudoKey);
// send the actual sudo transaction
// Send the actual sudo transaction
const unsub = await api.tx.sudo
.sudo(
api.tx.balances.setBalance(ADDR, 12345, 678)
@@ -41,4 +41,4 @@ In the above example, all we need to provide is a the fields for the `ValidatorP
## 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).
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).
+1 -1
View File
@@ -27,7 +27,7 @@ In addition to the `api.[consts | query | tx]` detailed above, the API, upon con
- `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`
- `api.libraryInfo` - The version of the API, i.e. `@polkadot/api v0.90.1`
## Let's do something!
+9 -9
View File
@@ -3,15 +3,15 @@
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
import { ApiPromise, WsProvider } from '@polkadot/api';
...
// construct
// Construct
const wsProvider = new WsProvider('wss://poc-3.polkadot.io');
const api = await ApiPromise.create({ provider: wsProvider });
// do something
// Do something
console.log(api.genesisHash.toHex());
```
@@ -21,12 +21,12 @@ We will have some explanation on the ES2015 syntax used next, but just a small n
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()`.
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
// Import
const { ApiPromise, WsProvider } = require('@polkadot/api');
...
```
@@ -58,13 +58,13 @@ ApiPromise
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
// Create the instance
const api = new ApiPromise({ provider: wsProvider });
// wait until we are ready and connected
// Wait until we are ready and connected
await api.isReady;
// do something
// Do something
console.log(api.genesisHash.toHex());
```
@@ -76,4 +76,4 @@ In these cases, create via `new`, attach listeners and then wait for the `isRead
## 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).
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).
+16 -16
View File
@@ -13,13 +13,13 @@ If you do opt to install it seperately, ensure that the version of `@polkadot/ut
Once installed, you can create an instance by just creating an instance of the `Keyring` class -
```js
// import the keyring as required
// Import the keyring as required
import { Keyring } from '@polkadot/api';
// initialize the API as we would normally do
// Initialize the API as we would normally do
...
// create a keyring instance
// Create a keyring instance
const keyring = new Keyring({ type: 'sr25519' });
```
@@ -27,18 +27,18 @@ In the above example, the import is self-explanatory. Upon creation we pass thro
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.
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
// Crypto promise, package used by keyring internally
import { cryptoWaitReady } from '@polkadot/util-crypto';
// wait for the promise to resolve, async WASM or `cryptoWaitReady().then(() => { ... })`
// Wait for the promise to resolve, async WASM or `cryptoWaitReady().then(() => { ... })`
await cryptoWaitReady();
// create a keyring instance
// Create a keyring instance
const keyring = new Keyring({ type: 'sr25519' });
```
@@ -49,16 +49,16 @@ The recommended catch-all approach to adding accounts is via `.addFromUri(<suri>
```js
...
// some mnemonic phrase
// Some mnemonic phrase
const PHRASE = 'entire material egg meadow latin bargain dutch coral blood melt acoustic thought';
// add an account, straight menemonic
// Add an account, straight menemonic
const newPair = keyring.addFromUri(PHRASE);
// (advanced) add an account with a derivation path (hard & soft)
// (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
// (Advanced, development-only) add with an implied dev seed and hard derivation
const alice = keyring.addFromUri('//Alice', { name: 'Alice default' });
```
@@ -71,27 +71,27 @@ In the previous examples we added a pair to the keyring (and we actually immedia
```js
...
// add our Alice dev account
// Add our Alice dev account
const alice = keyring.addFromUri('//Alice', { name: 'Alice default' });
// log some info
// 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
// Some helper functions used here
import { stringToU8a, u8aToHex } from '@polkadot/util';
...
// convert message, sign and then verify
// 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
// Log info
console.log(`The signature ${u8aToHex(signature)}, is ${isValid ? '' : 'in'}valid`);
```
+21 -4
View File
@@ -19,9 +19,26 @@ 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
## Comparing types
To reiterate the above API, the `.eq` method is the preferred means of comparing base types, rather than the JavaScript equality operator (`===`).
For example:
```js
const { metadata } = await api.rpc.state.getMetadata();
const modules = metadata.asV3.modules;
// This will not work, because `name` is an instance of `Text`, not a string
// const system = modules.find(m => m.name === 'system');
// This will work, because `Text.eq()` can compare against a string
const system = modules.find(m => m.name.eq('system'));
```
## 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 -
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
@@ -30,7 +47,7 @@ In cases where a `Compact` is returned, i.e. `Compact<Balance>`, the value is wr
## 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.
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.
@@ -48,7 +65,7 @@ An `Option<Type>` attempts to mimic the Rust approach of having `None` and `Some
- `.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.
- `.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
@@ -56,7 +73,7 @@ An `Option<Type>` attempts to mimic the Rust approach of having `None` and `Some
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)`
// Assuming a tuple defined as `(32, AccountId)`
const [count, accountId] = tuple;
console.log(`${accountId} has ${count.toNumber()} values`);
+4 -4
View File
@@ -6,7 +6,7 @@ Therefore to cater for all types, a mapping in done on the [@polkadot/types libr
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.
There is a the [recommendation](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
@@ -24,7 +24,7 @@ const api = await ApiPromise.create({
});
```
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.
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
@@ -102,7 +102,7 @@ const api = await ApiPromise.create({
});
```
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) -
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
...
@@ -139,4 +139,4 @@ const api = await ApiPromise.create({
## 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).
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).
+4 -4
View File
@@ -1,6 +1,6 @@
# 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`.
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
@@ -19,7 +19,7 @@ api.rpc.chain.subscribeNewHeads((lastHead: Header): void => {
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).
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.
@@ -46,11 +46,11 @@ As of this writing, there are still some gray areas to type detection, specifica
- `.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 -
In addition to expanding the type coverage, 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
## And that's 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.
+9 -4
View File
@@ -90,7 +90,8 @@ function addConstants (metadata: MetadataV7): string {
return orderedConstants.reduce((md, func): string => {
const methodName = stringCamelCase(func.name.toString());
const doc = func.documentation.reduce((md, doc): string => `${md} ${doc}`, '');
const doc = func.documentation
.reduce((md, doc): string => `${md.length ? `${md} ` : ''}${doc.trim()}`, '');
const type = func.type;
const renderSignature = `${md}\n▸ **${methodName}**: ` + '`' + type + '`';
const renderSummary = `${doc ? `\n- **summary**: ${doc}\n` : '\n'}`;
@@ -122,7 +123,9 @@ function addEvents (metadata: MetadataV7): string {
return orderedMethods.reduce((md, func): string => {
const methodName = func.name.toString();
const args = func.args.map((type): string => '`' + type + '`').join(', ');
const doc = func.documentation.reduce((md, doc): string => `${md} ${doc}`, '');
const doc = func.documentation
.reduce((md, doc): string => `${md.length ? `${md} ` : ''}${doc.trim()}`, '');
const renderSignature = `${md}\n▸ **${methodName}**(${args})`;
const renderSummary = `${doc ? `\n- **summary**: ${doc}\n` : '\n'}`;
@@ -153,7 +156,8 @@ function addExtrinsics (metadata: MetadataV7): string {
return orderedMethods.reduce((md, func): string => {
const methodName = stringCamelCase(func.name.toString());
const args = Call.filterOrigin(func).map(({ name, type }): string => `${name}: ` + '`' + type + '`').join(', ');
const doc = func.documentation.reduce((md, doc): string => `${md} ${doc}`, '');
const doc = func.documentation
.reduce((md, doc): string => `${md.length ? `${md} ` : ''}${doc.trim()}`, '');
const renderSignature = `${md}\n▸ **${methodName}**(${args})`;
const renderSummary = `${doc ? `\n- **summary**: ${doc}\n` : '\n'}`;
@@ -187,7 +191,8 @@ function addStorage (metadata: MetadataV7): string {
: func.type.isDoubleMap
? ('`' + func.type.asDoubleMap.key1.toString() + ', ' + func.type.asDoubleMap.key2.toString() + '`')
: '';
const doc = func.documentation.reduce((md, doc): string => `${md} ${doc}`, '');
const doc = func.documentation
.reduce((md, doc): string => `${md.length ? `${md} ` : ''}${doc.trim()}`, '');
let result = (
func.type.isDoubleMap
? func.type.asDoubleMap.value