From 0fa6b99c9e2bbe6b529486a1eda9f2d5aaa80bf2 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Fri, 13 Mar 2020 07:52:53 +0100 Subject: [PATCH] Query docs with entries & keys, submittable with paymentInfo (#2041) --- CHANGELOG.md | 8 +++++-- docs/start/api.query.other.md | 42 ++++++++++++++++++++++++++++++++++- docs/start/api.tx.subs.md | 17 ++++++++++++++ 3 files changed, 64 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0056a980aa..61afde3c5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ -# 1.6.2 Feb 27, 2020 +# 1.7.0-beta.x -- Revert checks for arg, not working on `.at()` queries (proper fix in next version) +- Re-add fixed (with tests) checks for query args + +# 1.6.2 Mar 12, 2020 + +- Revert checks for query args, not working on `.at()` queries (proper fix in next version) # 1.6.1 Mar 12, 2020 diff --git a/docs/start/api.query.other.md b/docs/start/api.query.other.md index dd0f126747..95c857c10a 100644 --- a/docs/start/api.query.other.md +++ b/docs/start/api.query.other.md @@ -22,7 +22,7 @@ const [{ data: balanceNow }, { data: balancePrev }] = await Promise.all([ console.log(`The delta was ${balanceNow.free.sub(balancePrev.free)}`); ``` -In the above example, we introduce the `.at([, ...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 - +In the above example, we introduce the `.at([, ...params]): Type` 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 ... @@ -35,6 +35,46 @@ The `.at` queries are all single-shot, i.e. there are no subscription option to 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. (Generally most public RPC nodes only run with default settings, which includes aggressive state pruning) +## State for a range of blocks + +In addition to the `.at` queries, you can also query state starting at a specific historic block and up to either a specified or the latest blocks. This is done via the `.range([from, to?], <...opt params>): [Hash, Type][]` query. As an example - + +```js +... +// Retrieve the current block header +const lastHdr = await api.rpc.chain.getHeader(); +const startHdr = await api.rpc.chain.getBlockHash(lastHdr.number.unwrap().subn(500)); + +// retrieve the range of changes +const changes = await api.query.system.account.range([startHdr]); + +changes.forEach(([hash, value]) => { + console.log(hash.toHex(), value.toHuman()); +}); +``` + +## Map keys & entries + +When working maps and double-maps, it is possible to retrieve a list of all the keys and entries for the map. For this we can use the `.entries(): [StorageKey, Type][]` queries. For example we may want to know the current list of validator exposures at a current era in the staking module - + +```js +... +// Retrieve the active era +const activeEra = await api.query.staking.activeEra(); + +// retrieve all exposures int the active era +const exposures = await api.query.staking.erasStakers.entries(activeEra.index); + +exposures.forEach(([key, exposure]) => { + console.log('key arguments:', key.args.map((k) => k.toHuman())); + console.log(' exposure:', exposure.toHuman()); +}); +``` + +Here we are querying a double-map, so we supply 1 argument. No arguments on double-maps will be very costly, retrieving all the eras and associated entries. Additionally when `twox64_concat` & `blake2_concat` is used, the key `.args` will contain decoded values of the params, in this case it will contain the actual `AccountId` of the staker. (Since that was not supplied) + +In the same way as above we can simply do `.keys(activeEra.index): StorageKey[]` to retrieve all the keys here, including the individual keys args decoding, as available on maps with decodable hashing functions. + ## 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 - diff --git a/docs/start/api.tx.subs.md b/docs/start/api.tx.subs.md index 4d954ea865..9bbc78dd04 100644 --- a/docs/start/api.tx.subs.md +++ b/docs/start/api.tx.subs.md @@ -62,6 +62,23 @@ 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. +## Payment information + +The Polkadot/Substrate RPC endpoints exposes weight/payment information that takes an encoded extrinsic and calculates the on-chain weight fees for it. A wrapper for this is available on the tx itself, taking exactly the same parameters as you would pass to a normal `.signAndSend` operation, specifically `.paymentInfo(sender, )`. To expand on our previous example - + +```js +// construct a transaction +const transfer = api.tx.balances.transfer(BOB, 12345); + +// retrieve the payment info +const { partialFee, weight } = await transfer.paymentInfo(alice); + +console.log(`transaction will have a weight of ${weight}, with ${partialFee.toHuman()} weight fees`); + +// send the tx +transfer.signAndSend(alice, ({ events = [], status }) => { ... }); +``` + ## 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).