Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
05a62ad89e | ||
|
|
3cd81fc691 | ||
|
|
ef805165d8 | ||
|
|
591253ff31 | ||
|
|
548cfbefb8 | ||
|
|
1827c34b69 | ||
|
|
e4400f0a26 | ||
|
|
8de8e3511c | ||
|
|
018c63b641 | ||
|
|
249c120e42 | ||
|
|
51ed24bf5e | ||
|
|
7b9fa97788 | ||
|
|
195469892e | ||
|
|
1ce536008d | ||
|
|
e8624244ea | ||
|
|
773b8654eb | ||
|
|
069fddee64 | ||
|
|
fb9c900d17 | ||
|
|
57adf72347 | ||
|
|
a304e0d35d | ||
|
|
61b49acb51 | ||
|
|
62e7fe8623 | ||
|
|
3cce0902de | ||
|
|
e61f1634ab | ||
|
|
572c0905b8 | ||
|
|
10d0215e69 | ||
|
|
27c63b709f | ||
|
|
5968647a05 | ||
|
|
022b938f00 | ||
|
|
f9318fe013 | ||
|
|
0aefc24bc2 | ||
|
|
f50922dd38 | ||
|
|
53ab0127cd | ||
|
|
a487bda3bd | ||
|
|
47cc0647d5 | ||
|
|
1bf40ceeab | ||
|
|
e16a4652e6 | ||
|
|
b5d5084b55 | ||
|
|
3b92be5284 | ||
|
|
2699dcfc9c | ||
|
|
194077033a | ||
|
|
0e5906473b | ||
|
|
f070b637f7 | ||
|
|
7c8ec1f7f5 | ||
|
|
a0a741d886 | ||
|
|
9f1499f435 | ||
|
|
4c925d9d4b | ||
|
|
fca5fb7110 | ||
|
|
bbc474432e | ||
|
|
2630f378a4 | ||
|
|
e0994f3c5a | ||
|
|
a0b1205258 | ||
|
|
fbbe7625b1 | ||
|
|
b573531ebf | ||
|
|
67de525077 | ||
|
|
f55732af51 | ||
|
|
5b6a6d4431 | ||
|
|
1857d8d521 | ||
|
|
afcc7fb701 | ||
|
|
be617df21f | ||
|
|
abc11890f7 | ||
|
|
5728e45261 | ||
|
|
4e6b533539 | ||
|
|
224803f4c4 |
+1
-1
@@ -1 +1 @@
|
||||
5
|
||||
6
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "10"
|
||||
cache:
|
||||
yarn: true
|
||||
directories:
|
||||
- node_modules
|
||||
before_install:
|
||||
- curl -o- -L https://yarnpkg.com/install.sh | bash
|
||||
- export PATH=$HOME/.yarn/bin::$(yarn global bin):$(yarn global dir):$PATH
|
||||
- yarn global add @plugnet/dev
|
||||
script:
|
||||
- plugnet-dev-travis-build
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
@@ -1,3 +1,24 @@
|
||||
# 0.80.1
|
||||
|
||||
- Support for mortal transactions
|
||||
- Better DoubleMap storage support
|
||||
- api-derive cleanups, including additional info for balances.all and taking.info returning redeemable and locked balances
|
||||
- Added SignaturePayloadRaw for better offline signing support
|
||||
- Updated metadata for current substrate master
|
||||
- Misc. cleanups and fixes
|
||||
|
||||
# 0.79.1
|
||||
|
||||
- DoubleMap query support
|
||||
- Support for mortal transactions via Era
|
||||
- Support latest EventRecord metadata (substrate master)
|
||||
- Introduce RuntimeVersion type overrides in api/nodeCompat.ts
|
||||
- Add `api.queryMulti` and `api.query.<module>.<method>.multi`
|
||||
- Convert `api.derive.*` to use multi queries
|
||||
- `types/codec/Set` now extends the base JS `Set` (breaking: `set.values` -> `set.strings`)
|
||||
- Breaking: rename `meta.arguments` to `meta.args` in function metadata (`arguments` is a JS reserved word)
|
||||
- Add `toRawType` on all type classes (breakdown into primitive types)
|
||||
|
||||
# 0.78.1
|
||||
|
||||
- Fix linked-maps (not working since 0.77.1)
|
||||
|
||||
@@ -50,6 +50,9 @@ ___
|
||||
▸ **CodeStored**(`Hash`)
|
||||
- **summary**: Code with the specified hash has been stored.
|
||||
|
||||
▸ **Contract**(`AccountId`, `Bytes`)
|
||||
- **summary**: An event from contract of account.
|
||||
|
||||
▸ **Dispatched**(`AccountId`, `bool`)
|
||||
- **summary**: A call was dispatched from the given account. The bool signals whether it was successful execution or not.
|
||||
|
||||
@@ -163,10 +166,10 @@ ___
|
||||
### staking
|
||||
|
||||
▸ **OfflineSlash**(`AccountId`, `Balance`)
|
||||
- **summary**: One validator (and their nominators) has been slashed by the given amount.
|
||||
- **summary**: One validator (and its nominators) has been slashed by the given amount.
|
||||
|
||||
▸ **OfflineWarning**(`AccountId`, `u32`)
|
||||
- **summary**: One validator (and their nominators) has been given a offline-warning (they're still within their grace). The accrued number of slashes is recorded, too.
|
||||
- **summary**: One validator (and its nominators) has been given an offline-warning (it is still within its grace). The accrued number of slashes is recorded, too.
|
||||
|
||||
▸ **Reward**(`Balance`)
|
||||
- **summary**: All validators have been rewarded by the given balance.
|
||||
|
||||
+33
-10
@@ -15,9 +15,9 @@ _The following sections contain Extrinsics methods are part of the default Subst
|
||||
|
||||
- **[democracy](#democracy)**
|
||||
|
||||
- **[finalityTracker](#finalityTracker)**
|
||||
- **[](#)**
|
||||
|
||||
- **[grandpa](#grandpa)**
|
||||
- **[grandpaFinality](#grandpaFinality)**
|
||||
|
||||
- **[session](#session)**
|
||||
|
||||
@@ -50,7 +50,7 @@ ___
|
||||
- **summary**: Kill some items from storage.
|
||||
|
||||
▸ **noteOffline**(offline: `InherentOfflineReport`)
|
||||
- **summary**: Note the previous block's validator missed their opportunity to propose a block.
|
||||
- **summary**: Note that the previous block's validator missed its opportunity to propose a block.
|
||||
|
||||
▸ **remark**(_remark: `Bytes`)
|
||||
- **summary**: Make some on-chain remark.
|
||||
@@ -75,11 +75,17 @@ ___
|
||||
▸ **call**(dest: `Address`, value: `Compact<BalanceOf>`, gas_limit: `Compact<Gas>`, data: `Bytes`)
|
||||
- **summary**: Makes a call to an account, optionally transferring some balance. * If the account is a smart-contract account, the associated code will be executed and any value will be transferred. * If the account is a regular account, any value will be transferred. * If no account exists and the call value is not less than `existential_deposit`, a regular account will be created and any value will be transferred.
|
||||
|
||||
▸ **claimSurcharge**(dest: `AccountId`, aux_sender: `Option<AccountId>`)
|
||||
- **summary**: Allows block producers to claim a small reward for evicting a contract. If a block producer fails to do so, a regular users will be allowed to claim the reward. If contract is not evicted as a result of this call, no actions are taken and the sender is not eligible for the reward.
|
||||
|
||||
▸ **create**(endowment: `Compact<BalanceOf>`, gas_limit: `Compact<Gas>`, code_hash: `CodeHash`, data: `Bytes`)
|
||||
- **summary**: Creates a new contract from the `codehash` generated by `put_code`, optionally transferring some balance. Creation is executed as follows: - the destination address is computed based on the sender and hash of the code. - the smart-contract account is created at the computed address. - the `ctor_code` is executed in the context of the newly created account. Buffer returned after the execution is saved as the `code` of the account. That code will be invoked upon any call received by this account. - The contract is initialized.
|
||||
- **summary**: Creates a new contract from the `codehash` generated by `put_code`, optionally transferring some balance. Creation is executed as follows: - The destination address is computed based on the sender and hash of the code. - The smart-contract account is created at the computed address. - The `ctor_code` is executed in the context of the newly-created account. Buffer returned after the execution is saved as the `code` of the account. That code will be invoked upon any call received by this account. - The contract is initialized.
|
||||
|
||||
▸ **putCode**(gas_limit: `Compact<Gas>`, code: `Bytes`)
|
||||
- **summary**: Stores the given binary Wasm code into the chains storage and returns its `codehash`. You can instantiate contracts only with stored code.
|
||||
- **summary**: Stores the given binary Wasm code into the chain's storage and returns its `codehash`. You can instantiate contracts only with stored code.
|
||||
|
||||
▸ **restoreTo**(dest: `AccountId`, code_hash: `CodeHash`, rent_allowance: `BalanceOf`, delta: `Vec<ContractStorageKey>`)
|
||||
- **summary**: Allows a contract to restore a tombstone by giving its storage. The contract that wants to restore (i.e. origin of the call, or `msg.sender` in Solidity terms) will compute a tombstone with its storage and the given code_hash. If the computed tombstone match the destination one, the destination contract is restored with the rent_allowance` specified, while the origin sends all its funds to the destination and is removed.
|
||||
|
||||
▸ **updateSchedule**(schedule: `Schedule`)
|
||||
- **summary**: Updates the schedule for metering contracts. The schedule must have a greater version than the stored schedule.
|
||||
@@ -92,6 +98,9 @@ ___
|
||||
▸ **presentWinner**(candidate: `Address`, total: `Compact<BalanceOf>`, index: `Compact<VoteIndex>`)
|
||||
- **summary**: Claim that `signed` is one of the top Self::carry_count() + current_vote().1 candidates. Only works if the `block_number >= current_vote().0` and `< current_vote().0 + presentation_duration()`` `signed` should have at least
|
||||
|
||||
▸ **proxySetApprovals**(votes: `Vec<bool>`, index: `Compact<VoteIndex>`)
|
||||
- **summary**: Set candidate approvals from a proxy. Approval slots stay valid as long as candidates in those slots are registered.
|
||||
|
||||
▸ **reapInactiveVoter**(reporter_index: `Compact<u32>`, who: `Address`, who_index: `Compact<u32>`, assumed_vote_index: `Compact<VoteIndex>`)
|
||||
- **summary**: Remove a voter. For it not to be a bond-consuming no-op, all approved candidate indices must now be either unregistered or registered to a candidate that registered the slot after the voter gave their last approval set. May be called by anyone. Returns the voter deposit to `signed`.
|
||||
|
||||
@@ -157,9 +166,21 @@ ___
|
||||
▸ **propose**(proposal: `Proposal`, value: `Compact<BalanceOf>`)
|
||||
- **summary**: Propose a sensitive action to be taken.
|
||||
|
||||
▸ **proxyVote**(ref_index: `Compact<ReferendumIndex>`, vote: `Vote`)
|
||||
- **summary**: Vote in a referendum on behalf of a stash. If `vote.is_aye()`, the vote is to enact the proposal; otherwise it is a vote to keep the status quo.
|
||||
|
||||
▸ **removeProxy**(proxy: `AccountId`)
|
||||
- **summary**: Clear the proxy. Called by the stash.
|
||||
|
||||
▸ **resignProxy**()
|
||||
- **summary**: Clear the proxy. Called by the proxy.
|
||||
|
||||
▸ **second**(proposal: `Compact<PropIndex>`)
|
||||
- **summary**: Propose a sensitive action to be taken.
|
||||
|
||||
▸ **setProxy**(proxy: `AccountId`)
|
||||
- **summary**: Specify a proxy. Called by the stash.
|
||||
|
||||
▸ **startReferendum**(proposal: `Proposal`, threshold: `VoteThreshold`, delay: `BlockNumber`)
|
||||
- **summary**: Start a referendum.
|
||||
|
||||
@@ -172,7 +193,7 @@ ___
|
||||
___
|
||||
|
||||
|
||||
### finalityTracker
|
||||
###
|
||||
|
||||
▸ **finalHint**(hint: `Compact<BlockNumber>`)
|
||||
- **summary**: Hint that the author of this block thinks the best finalized block is the given number.
|
||||
@@ -180,7 +201,7 @@ ___
|
||||
___
|
||||
|
||||
|
||||
### grandpa
|
||||
### grandpaFinality
|
||||
|
||||
▸ **reportMisbehavior**(_report: `Bytes`)
|
||||
- **summary**: Report some misbehavior.
|
||||
@@ -194,7 +215,7 @@ ___
|
||||
- **summary**: Forces a new session.
|
||||
|
||||
▸ **setKey**(key: `SessionKey`)
|
||||
- **summary**: Sets the session key of `_validator` to `_key`. This doesn't take effect until the next session.
|
||||
- **summary**: Sets the session key of a validator (function caller) to `key`. This doesn't take effect until the next session.
|
||||
|
||||
▸ **setLength**(new: `Compact<BlockNumber>`)
|
||||
- **summary**: Set a new session length. Won't kick in until the next session change (at current length).
|
||||
@@ -205,7 +226,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. The dispatch origin for this call must be _Signed_.
|
||||
- **summary**: Take the origin account as a stash and lock up `value` of its balance. `controller` will be the account that controls it. The dispatch origin for this call must be _Signed_ by the stash account.
|
||||
|
||||
▸ **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. The dispatch origin for this call must be _Signed_ by the stash, not the controller.
|
||||
@@ -255,8 +276,10 @@ ___
|
||||
### sudo
|
||||
|
||||
▸ **setKey**(new: `Address`)
|
||||
- **summary**: Authenticates the current sudo key and sets the given AccountId (`new`) as the new sudo key. The dispatch origin for this call must be _Signed_.
|
||||
|
||||
▸ **sudo**(proposal: `Proposal`)
|
||||
- **summary**: Authenticates the sudo key and dispatches a function call with `Root` origin. The dispatch origin for this call must be _Signed_.
|
||||
|
||||
___
|
||||
|
||||
@@ -264,7 +287,7 @@ ___
|
||||
### timestamp
|
||||
|
||||
▸ **set**(now: `Compact<Moment>`)
|
||||
- **summary**: Set the current time. This call should be invoked exactly once per block. It will panic at the finalization phase, if this call hasn't been invoked by that time. The timestamp should be greater than the previous one by the amount specified by `minimum_period`. The dispatch origin for this call must be `Inherent`.
|
||||
- **summary**: Set the current time. This call should be invoked exactly once per block. It will panic at the finalization phase, if this call hasn't been invoked by that time. The timestamp should be greater than the previous one by the amount specified by `minimum_period`. The dispatch origin for this call must be `Inherent`.
|
||||
|
||||
___
|
||||
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ _Retrieval of chain data_
|
||||
- **summary**: Get the block hash for a specific block
|
||||
|
||||
▸ **getFinalizedHead**(): `Hash`
|
||||
- **summary**: Get hash of the last finalised block in the canon chain
|
||||
- **summary**: Get hash of the last finalized block in the canon chain
|
||||
|
||||
▸ **getHeader**(hash?: `Hash`): `Header`
|
||||
- **summary**: Retrieves the header for a specific block
|
||||
|
||||
+46
-21
@@ -82,10 +82,7 @@ ___
|
||||
### contract
|
||||
|
||||
▸ **accountCounter**(): `u64`
|
||||
- **summary**: The subtrie counter
|
||||
|
||||
▸ **accountInfoOf**(`AccountId`): `Option<AccountInfo>`
|
||||
- **summary**: The code associated with a given account.
|
||||
- **summary**: The subtrie counter.
|
||||
|
||||
▸ **blockGasLimit**(): `Gas`
|
||||
- **summary**: The maximum amount of gas that could be expended per block.
|
||||
@@ -93,15 +90,15 @@ ___
|
||||
▸ **callBaseFee**(): `Gas`
|
||||
- **summary**: The base fee charged for calling into a contract.
|
||||
|
||||
▸ **codeHashOf**(`AccountId`): `Option<CodeHash>`
|
||||
- **summary**: The code associated with a given account.
|
||||
|
||||
▸ **codeStorage**(`CodeHash`): `Option<PrefabWasmModule>`
|
||||
- **summary**: A mapping between an original code hash and instrumented wasm code, ready for the execution.
|
||||
- **summary**: A mapping between an original code hash and instrumented wasm code, ready for execution.
|
||||
|
||||
▸ **contractFee**(): `BalanceOf`
|
||||
- **summary**: The fee required to create a contract instance.
|
||||
|
||||
▸ **contractInfoOf**(`AccountId`): `Option<ContractInfo>`
|
||||
- **summary**: The code associated with a given account.
|
||||
|
||||
▸ **createBaseFee**(): `Gas`
|
||||
- **summary**: The base fee charged for creating a contract.
|
||||
|
||||
@@ -123,6 +120,24 @@ ___
|
||||
▸ **pristineCode**(`CodeHash`): `Option<Bytes>`
|
||||
- **summary**: A mapping from an original code hash to the original code, untouched by instrumentation.
|
||||
|
||||
▸ **rentByteFee**(): `BalanceOf`
|
||||
- **summary**: Price of a byte of storage per one block interval. Should be greater than 0.
|
||||
|
||||
▸ **rentDepositOffset**(): `BalanceOf`
|
||||
- **summary**: The amount of funds a contract should deposit in order to offset the cost of one byte. Let's suppose the deposit is 1,000 BU (balance units)/byte and the rent is 1 BU/byte/day, then a contract with 1,000,000 BU that uses 1,000 bytes of storage would pay no rent. But if the balance reduced to 500,000 BU and the storage stayed the same at 1,000, then it would pay 500 BU/day.
|
||||
|
||||
▸ **signedClaimHandicap**(): `BlockNumber`
|
||||
- **summary**: Number of block delay an extrinsic claim surcharge has. When claim surchage is called by an extrinsic the rent is checked for current_block - delay
|
||||
|
||||
▸ **storageSizeOffset**(): `u32`
|
||||
- **summary**: Size of a contract at the time of creation. This is a simple way to ensure that empty contracts eventually gets deleted.
|
||||
|
||||
▸ **surchargeReward**(): `BalanceOf`
|
||||
- **summary**: Reward that is received by the party whose touch has led to removal of a contract.
|
||||
|
||||
▸ **tombstoneDeposit**(): `BalanceOf`
|
||||
- **summary**: The minimum amount required to generate a tombstone.
|
||||
|
||||
▸ **transactionBaseFee**(): `BalanceOf`
|
||||
- **summary**: The fee to be paid for making a transaction; the base.
|
||||
|
||||
@@ -169,12 +184,12 @@ ___
|
||||
▸ **nextFinalize**(): `Option<(BlockNumber,u32,Vec<AccountId>)>`
|
||||
- **summary**: The accounts holding the seats that will become free on the next tally.
|
||||
|
||||
▸ **presentSlashPerVoter**(): `BalanceOf`
|
||||
- **summary**: The punishment, per voter, if you provide an invalid presentation.
|
||||
|
||||
▸ **presentationDuration**(): `BlockNumber`
|
||||
- **summary**: How long to give each top candidate to present themselves after the vote ends.
|
||||
|
||||
▸ **presentSlashPerVoter**(): `BalanceOf`
|
||||
- **summary**: The punishment, per voter, if you provide an invalid presentation.
|
||||
|
||||
▸ **registerInfoOf**(`AccountId`): `Option<(VoteIndex,u32)>`
|
||||
- **summary**: The vote index and list slot that the candidate `who` was registered or `None` if they are not currently registered.
|
||||
|
||||
@@ -227,10 +242,10 @@ ___
|
||||
|
||||
▸ **proposalOf**(`Hash`): `Option<Proposal>`
|
||||
|
||||
▸ **proposalVoters**(`Hash`): `Vec<AccountId>`
|
||||
|
||||
▸ **proposals**(): `Vec<(BlockNumber,Hash)>`
|
||||
|
||||
▸ **proposalVoters**(`Hash`): `Vec<AccountId>`
|
||||
|
||||
▸ **vetoedProposal**(`Hash`): `Option<(BlockNumber,Vec<AccountId>)>`
|
||||
|
||||
▸ **votingPeriod**(): `BlockNumber`
|
||||
@@ -261,6 +276,9 @@ ___
|
||||
▸ **nextTally**(): `ReferendumIndex`
|
||||
- **summary**: The next referendum index that should be tallied.
|
||||
|
||||
▸ **proxy**(`AccountId`): `Option<AccountId>`
|
||||
- **summary**: Who is able to vote for whom. Value is the fund-holding account, key is the vote-transaction-sending account.
|
||||
|
||||
▸ **publicDelay**(): `BlockNumber`
|
||||
- **summary**: The delay before enactment for all public referenda.
|
||||
|
||||
@@ -271,7 +289,7 @@ ___
|
||||
- **summary**: The public proposals. Unsorted.
|
||||
|
||||
▸ **referendumCount**(): `ReferendumIndex`
|
||||
- **summary**: The next free referendum index, aka the number of referendums started so far.
|
||||
- **summary**: The next free referendum index, aka the number of referenda started so far.
|
||||
|
||||
▸ **referendumInfoOf**(`ReferendumIndex`): `Option<ReferendumInfo>`
|
||||
- **summary**: Information concerning any given referendum.
|
||||
@@ -317,7 +335,7 @@ ___
|
||||
- **summary**: Timestamp when current session started.
|
||||
|
||||
▸ **forcingNewSession**(): `Option<bool>`
|
||||
- **summary**: New session is being forced is this entry exists; in which case, the boolean value is whether the new session should be considered a normal rotation (rewardable) or exceptional (slashable).
|
||||
- **summary**: New session is being forced if this entry exists; in which case, the boolean value is true if the new session should be considered a normal rotation (rewardable) and false if the new session should be considered exceptional (slashable).
|
||||
|
||||
▸ **lastLengthChange**(): `Option<BlockNumber>`
|
||||
- **summary**: Block at which the session length last changed.
|
||||
@@ -343,7 +361,7 @@ ___
|
||||
- **summary**: Map from all locked "stash" accounts to the controller account.
|
||||
|
||||
▸ **bondingDuration**(): `BlockNumber`
|
||||
- **summary**: The length of the bonding duration in blocks.
|
||||
- **summary**: The length of the bonding duration in eras.
|
||||
|
||||
▸ **currentElected**(): `Vec<AccountId>`
|
||||
- **summary**: The currently elected validator set keyed by stash account ID.
|
||||
@@ -388,7 +406,7 @@ ___
|
||||
- **summary**: Where the reward payment should be made. Keyed by stash.
|
||||
|
||||
▸ **recentlyOffline**(): `Vec<(AccountId,BlockNumber,u32)>`
|
||||
- **summary**: Most recent `RECENT_OFFLINE_COUNT` instances. (who it was, when it was reported, how many instances they were offline for).
|
||||
- **summary**: Most recent `RECENT_OFFLINE_COUNT` instances. (Who it was, when it was reported, how many instances they were offline for).
|
||||
|
||||
▸ **sessionReward**(): `Perbill`
|
||||
- **summary**: Maximum reward, per validator, that is provided per acceptable session.
|
||||
@@ -403,7 +421,7 @@ ___
|
||||
- **summary**: The amount of balance actively at stake for each validator slot, currently. This is used to derive rewards and punishments.
|
||||
|
||||
▸ **stakers**(`AccountId`): `Exposure`
|
||||
- **summary**: Nominators for a particular account that is in action right now. You can't iterate through validators here, but you can find them in the `sessions` module. This is keyed by the stash account.
|
||||
- **summary**: Nominators for a particular account that is in action right now. You can't iterate through validators here, but you can find them in the Session module. This is keyed by the stash account.
|
||||
|
||||
▸ **validatorCount**(): `u32`
|
||||
- **summary**: The ideal number of staking participants.
|
||||
@@ -417,6 +435,7 @@ ___
|
||||
### sudo
|
||||
|
||||
▸ **key**(): `AccountId`
|
||||
- **summary**: The `AccountId` of the sudo key.
|
||||
|
||||
___
|
||||
|
||||
@@ -435,14 +454,20 @@ ___
|
||||
▸ **digest**(): `Digest`
|
||||
- **summary**: Digest of the current block, also part of the block header.
|
||||
|
||||
▸ **eventCount**(): `EventIndex`
|
||||
- **summary**: The number of events in the `Events<T>` list.
|
||||
|
||||
▸ **events**(): `Vec<EventRecord>`
|
||||
- **summary**: Events deposited for the current block.
|
||||
|
||||
▸ **eventTopics**(): `DoubleMap<Vec<(BlockNumber,EventIndex)>>`
|
||||
- **summary**: Mapping between a topic (represented by T::Hash) and a vector of indexes of events in the `<Events<T>>` list. The first key serves no purpose. This field is declared as double_map just for convenience of using `remove_prefix`. All topic vectors have deterministic storage locations depending on the topic. This allows light-clients to leverage the changes trie storage tracking mechanism and in case of changes fetch the list of events of interest. The value has the type `(T::BlockNumber, EventIndex)` because if we used only just the `EventIndex` then in case if the topic has the same contents on the next block no notification will be triggered thus the event might be lost.
|
||||
|
||||
▸ **extrinsicCount**(): `Option<u32>`
|
||||
- **summary**: Total extrinsics count for the current block.
|
||||
|
||||
▸ **extrinsicData**(`u32`): `Bytes`
|
||||
- **summary**: Extrinsics data for the current block (maps extrinsic's index to its data).
|
||||
- **summary**: Extrinsics data for the current block (maps an extrinsic's index to its data).
|
||||
|
||||
▸ **extrinsicsRoot**(): `Hash`
|
||||
- **summary**: Extrinsics root of the current block, also part of the block header.
|
||||
@@ -453,8 +478,8 @@ ___
|
||||
▸ **parentHash**(): `Hash`
|
||||
- **summary**: Hash of the previous block.
|
||||
|
||||
▸ **randomSeed**(): `Hash`
|
||||
- **summary**: Random seed of the current block.
|
||||
▸ **randomMaterial**(): `(i8,Vec<Hash>)`
|
||||
- **summary**: Series of block headers from the last 81 blocks that acts as random seed material. This is arranged as a ring buffer with the `i8` prefix being the index into the `Vec` of the oldest hash.
|
||||
|
||||
___
|
||||
|
||||
|
||||
+1
-1
@@ -9,5 +9,5 @@
|
||||
"packages": [
|
||||
"packages/*"
|
||||
],
|
||||
"version": "0.78.102"
|
||||
"version": "0.80.100"
|
||||
}
|
||||
|
||||
+2
-1
@@ -20,7 +20,8 @@
|
||||
"lint": "tslint --project . && tsc --noEmit --pretty",
|
||||
"clean": "plugnet-dev-clean-build",
|
||||
"postinstall": "plugnet-dev-yarn-only",
|
||||
"test": "jest --coverage"
|
||||
"test": "jest --coverage",
|
||||
"test:watch": "jest --coverage --watch"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.4.4",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@plugnet/api-derive",
|
||||
"version": "0.78.102",
|
||||
"version": "0.80.100",
|
||||
"description": "Common functions used across Plugnet, derived from RPC calls and storage queries.",
|
||||
"main": "index.js",
|
||||
"keywords": [
|
||||
@@ -14,9 +14,6 @@
|
||||
],
|
||||
"contributors": [],
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=8.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://registry.npmjs.org"
|
||||
@@ -30,9 +27,10 @@
|
||||
},
|
||||
"homepage": "https://github.com/plugblockchain/api.js/tree/master/packages/api-derive#readme",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.4.4",
|
||||
"@plugnet/api": "^0.78.102",
|
||||
"@plugnet/types": "^0.78.102",
|
||||
"@babel/runtime": "^7.4.5",
|
||||
"@plugnet/api": "^0.80.100",
|
||||
"@plugnet/keyring": "^0.92.100",
|
||||
"@plugnet/types": "^0.80.100",
|
||||
"@types/memoizee": "^0.4.2",
|
||||
"memoizee": "^0.4.14"
|
||||
}
|
||||
|
||||
@@ -15,6 +15,19 @@ import { drr } from '../util/drr';
|
||||
|
||||
export type AccountIdAndIndex = [AccountId?, AccountIndex?];
|
||||
|
||||
/**
|
||||
* @name idAndIndex
|
||||
* @param {(Address | AccountId | AccountIndex | string | null)} address - An accounts address in various formats.
|
||||
* @description An array containing the [[AccountId]] and [[AccountIndex]] as optional values.
|
||||
* @example
|
||||
* <BR>
|
||||
*
|
||||
* ```javascript
|
||||
* api.derive.accounts.idAndIndex('F7Hs', ([id, ix]) => {
|
||||
* console.log(`AccountId #${id} with corresponding AccountIndex ${ix}`);
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export function idAndIndex (api: ApiInterface$Rx) {
|
||||
return (address?: Address | AccountId | AccountIndex | string | null): Observable<AccountIdAndIndex> => {
|
||||
try {
|
||||
|
||||
@@ -10,6 +10,20 @@ import { AccountId, AccountIndex } from '@plugnet/types';
|
||||
import { indexes, AccountIndexes } from './indexes';
|
||||
import { drr } from '../util/drr';
|
||||
|
||||
/**
|
||||
* @name idToIndex
|
||||
* @param {( AccountId | string )} accountId - An accounts Id in different formats.
|
||||
* @returns Returns the corresponding AccountIndex.
|
||||
* @example
|
||||
* <BR>
|
||||
*
|
||||
* ```javascript
|
||||
* const ALICE = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
|
||||
* api.derive.accounts.idToIndex(ALICE, (accountIndex) => {
|
||||
* console.log(`The AccountIndex of ${ALICE} is ${accountIndex}`);
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export function idToIndex (api: ApiInterface$Rx) {
|
||||
return (accountId: AccountId | string): Observable<AccountIndex | undefined> =>
|
||||
indexes(api)()
|
||||
|
||||
@@ -5,11 +5,24 @@
|
||||
import { Observable } from 'rxjs';
|
||||
import { map, startWith } from 'rxjs/operators';
|
||||
import { ApiInterface$Rx } from '@plugnet/api/types';
|
||||
import { ENUMSET_SIZE } from '@plugnet/types/type/AccountIndex';
|
||||
import { ENUMSET_SIZE } from '@plugnet/types/primitive/AccountIndex';
|
||||
import { AccountId, AccountIndex, Vector } from '@plugnet/types';
|
||||
|
||||
import { drr } from '../util/drr';
|
||||
|
||||
/**
|
||||
* @name indexToId
|
||||
* @param {( AccountIndex | string )} accountIndex - An accounts index in different formats.
|
||||
* @returns Returns the corresponding AccountId.
|
||||
* @example
|
||||
* <BR>
|
||||
*
|
||||
* ```javascript
|
||||
* api.derive.accounts.indexToId('F7Hs', (accountId) => {
|
||||
* console.log(`The AccountId of F7Hs is ${accountId}`);
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export function indexToId (api: ApiInterface$Rx) {
|
||||
return (_accountIndex: AccountIndex | string): Observable<AccountId> => {
|
||||
const querySection = api.query.indices || api.query.balances;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import { Observable } from 'rxjs';
|
||||
import { map, switchMap } from 'rxjs/operators';
|
||||
import { ApiInterface$Rx } from '@plugnet/api/types';
|
||||
import { ENUMSET_SIZE } from '@plugnet/types/type/AccountIndex';
|
||||
import { ENUMSET_SIZE } from '@plugnet/types/primitive/AccountIndex';
|
||||
import { AccountId, AccountIndex } from '@plugnet/types';
|
||||
|
||||
import { drr } from '../util/drr';
|
||||
@@ -15,9 +15,19 @@ export type AccountIndexes = { [index: string]: AccountIndex };
|
||||
const enumsetSize = ENUMSET_SIZE.toNumber();
|
||||
|
||||
/**
|
||||
* Returns all the indexes on the system - this is an unwieldly query since it loops through
|
||||
* @name indexes
|
||||
* @returns Returns all the indexes on the system.
|
||||
* @description This is an unwieldly query since it loops through
|
||||
* all of the enumsets and returns all of the values found. This could be up to 32k depending
|
||||
* on the number of active accounts in the system
|
||||
* @example
|
||||
* <BR>
|
||||
*
|
||||
* ```javascript
|
||||
* api.derive.accounts.indexes((indexes) => {
|
||||
* console.log('All existing AccountIndexes', indexes);
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export function indexes (api: ApiInterface$Rx) {
|
||||
return (): Observable<AccountIndexes> => {
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
// Copyright 2017-2019 @polkadot/api-derive 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 { combineLatest, of, Observable } from 'rxjs';
|
||||
import { map, switchMap } from 'rxjs/operators';
|
||||
import { ApiInterface$Rx } from '@plugnet/api/types';
|
||||
import { AccountId, AccountIndex, Address, Balance, BalanceLock, BlockNumber, Index, StructAny, Option, VestingSchedule } from '@plugnet/types';
|
||||
import { bnMax } from '@plugnet/util';
|
||||
|
||||
import { idAndIndex } from '../accounts/idAndIndex';
|
||||
import { DerivedBalances } from '../types';
|
||||
import { drr } from '../util/drr';
|
||||
|
||||
type Result = [AccountId | undefined, BlockNumber | undefined, [Balance?, Balance?, Array<BalanceLock>?, Option<VestingSchedule>?, Index?]];
|
||||
|
||||
const EMPTY_ACCOUNT = new AccountId();
|
||||
const ZERO = new Balance(0);
|
||||
|
||||
function calcBalances ([accountId = EMPTY_ACCOUNT, bestNumber = ZERO, [freeBalance = ZERO, reservedBalance = ZERO, locks = [], vesting = new Option<VestingSchedule>(VestingSchedule, null), accountNonce = ZERO]]: Result): DerivedBalances {
|
||||
let lockedBalance = ZERO;
|
||||
|
||||
if (Array.isArray(locks)) {
|
||||
// only get the locks that are valid until passed the current block
|
||||
const totals = locks.filter((value) => bestNumber && value.until.gt(bestNumber));
|
||||
// get the maximum of the locks according to https://github.com/paritytech/substrate/blob/master/srml/balances/src/lib.rs#L699
|
||||
lockedBalance = totals[0]
|
||||
? bnMax(...totals.map(({ amount }) => amount)) as Balance
|
||||
: ZERO;
|
||||
}
|
||||
|
||||
// offset = balance locked at genesis, perBlock is the unlock amount
|
||||
const { offset, perBlock } = vesting.unwrapOr(new VestingSchedule());
|
||||
const vestedNow = perBlock.mul(bestNumber);
|
||||
const vestedBalance = vestedNow.gt(offset)
|
||||
? freeBalance
|
||||
: freeBalance.sub(offset).add(vestedNow);
|
||||
|
||||
// NOTE Workaround for this account on Alex (one of a couple reported) -
|
||||
// 5F7BJL6Z4m8RLtK7nXEqqpEqhBbd535Z3CZeYF6ccvaQAY6N
|
||||
// The locked is > the vested and ended up with the locked > free,
|
||||
// i.e. related to https://github.com/paritytech/polkadot/issues/225
|
||||
// (most probably due to movements from stash -> controller -> free)
|
||||
const availableBalance = bnMax(ZERO, vestedBalance.sub(lockedBalance));
|
||||
|
||||
return new StructAny({
|
||||
accountId,
|
||||
accountNonce,
|
||||
availableBalance,
|
||||
freeBalance,
|
||||
lockedBalance,
|
||||
reservedBalance,
|
||||
vestedBalance,
|
||||
votingBalance: freeBalance.add(reservedBalance)
|
||||
}) as DerivedBalances;
|
||||
}
|
||||
|
||||
/**
|
||||
* @name all
|
||||
* @param {( ccountIndex | AccountId | Address | string )} address - An accounts Id in different formats.
|
||||
* @returns An object containing the combined results of the storage queries for
|
||||
* all relevant fees as declared in the substrate chain spec.
|
||||
* @example
|
||||
* <BR>
|
||||
*
|
||||
* ```javascript
|
||||
* const ALICE = 'F7Hs';
|
||||
*
|
||||
* api.derive.balances.all(ALICE, ([accountId, lockedBalance]) => {
|
||||
* console.log(`The account ${accountId} has a locked balance ${lockedBalance} units.`);
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export function all (api: ApiInterface$Rx) {
|
||||
return (address: AccountIndex | AccountId | Address | string): Observable<DerivedBalances> => {
|
||||
return idAndIndex(api)(address).pipe(
|
||||
switchMap(([accountId]) =>
|
||||
(accountId
|
||||
? combineLatest([
|
||||
of(accountId),
|
||||
api.derive.chain.bestNumber(),
|
||||
api.queryMulti([
|
||||
[api.query.balances.freeBalance, accountId],
|
||||
[api.query.balances.reservedBalance, accountId],
|
||||
[api.query.balances.locks, accountId],
|
||||
[api.query.balances.vesting, accountId],
|
||||
[api.query.system.accountNonce, accountId]
|
||||
])
|
||||
])
|
||||
: of([undefined, undefined, [undefined, undefined, undefined, undefined, undefined]])
|
||||
) as any as Observable<Result>
|
||||
),
|
||||
map(calcBalances),
|
||||
drr()
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -2,31 +2,46 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { Observable, combineLatest } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { ApiInterface$Rx } from '@plugnet/api/types';
|
||||
import { DerivedFees } from '../types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import { DerivedFees } from '../types';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { StructAny } from '@plugnet/types';
|
||||
|
||||
import { drr } from '../util/drr';
|
||||
|
||||
/**
|
||||
* @name fees
|
||||
* @returns An object containing the combined results of the storage queries for
|
||||
* all relevant fees as declared in the substrate chain spec.
|
||||
* @example
|
||||
* <BR>
|
||||
*
|
||||
* ```javascript
|
||||
* api.derive.balances.fees(([creationFee, transferFee]) => {
|
||||
* console.log(`The fee for creating a new account on this chain is ${transferFee} units. The fee required for making a transfer is ${transferFee} units.`);
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export function fees (api: ApiInterface$Rx) {
|
||||
return (): Observable<DerivedFees> => {
|
||||
// FIXME Remove fees checks, only use balances (fees is a removed module now)
|
||||
return (combineLatest([
|
||||
api.query.balances.creationFee(),
|
||||
api.query.balances.existentialDeposit(),
|
||||
api.query.balances.transactionBaseFee(),
|
||||
api.query.balances.transactionByteFee(),
|
||||
api.query.balances.transferFee()
|
||||
return (api.queryMulti([
|
||||
api.query.balances.creationFee,
|
||||
api.query.balances.existentialDeposit,
|
||||
api.query.balances.transactionBaseFee,
|
||||
api.query.balances.transactionByteFee,
|
||||
api.query.balances.transferFee
|
||||
]) as any as Observable<[BN, BN, BN, BN, BN]>).pipe(
|
||||
map(([creationFee, existentialDeposit, transactionBaseFee, transactionByteFee, transferFee]) => ({
|
||||
creationFee,
|
||||
existentialDeposit,
|
||||
transactionBaseFee,
|
||||
transactionByteFee,
|
||||
transferFee
|
||||
})),
|
||||
map(([creationFee, existentialDeposit, transactionBaseFee, transactionByteFee, transferFee]) =>
|
||||
new StructAny({
|
||||
creationFee,
|
||||
existentialDeposit,
|
||||
transactionBaseFee,
|
||||
transactionByteFee,
|
||||
transferFee
|
||||
}) as DerivedFees),
|
||||
drr()
|
||||
);
|
||||
};
|
||||
|
||||
@@ -2,9 +2,15 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { all } from './all';
|
||||
|
||||
export * from './fees';
|
||||
export * from './validatingBalance';
|
||||
export * from './validatingBalances';
|
||||
export * from './votingBalance';
|
||||
export * from './votingBalances';
|
||||
export * from './votingBalancesNominatorsFor';
|
||||
|
||||
const votingBalance = all;
|
||||
|
||||
export {
|
||||
all,
|
||||
votingBalance
|
||||
};
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
// Copyright 2017-2019 @polkadot/api-derive 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 BN from 'bn.js';
|
||||
import { combineLatest, Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { ApiInterface$Rx } from '@plugnet/api/types';
|
||||
import { AccountId, AccountIndex, Address, Balance } from '@plugnet/types';
|
||||
|
||||
import { DerivedBalances } from '../types';
|
||||
import { drr } from '../util/drr';
|
||||
import { votingBalance } from './votingBalance';
|
||||
import { votingBalancesNominatorsFor } from './votingBalancesNominatorsFor';
|
||||
|
||||
export function validatingBalance (api: ApiInterface$Rx) {
|
||||
return (address: AccountId | AccountIndex | Address | string): Observable<DerivedBalances> => {
|
||||
return combineLatest([
|
||||
votingBalance(api)(address),
|
||||
votingBalancesNominatorsFor(api)(address)
|
||||
]).pipe(
|
||||
map(([balance, nominators]) => {
|
||||
const nominatedBalance = nominators.reduce(
|
||||
(total: BN, nominatorBalance: DerivedBalances) =>
|
||||
total.add(nominatorBalance.votingBalance),
|
||||
new BN(0)
|
||||
);
|
||||
|
||||
return {
|
||||
...balance,
|
||||
nominators,
|
||||
nominatedBalance: new Balance(nominatedBalance),
|
||||
stakingBalance: new Balance(
|
||||
nominatedBalance.add(balance.votingBalance)
|
||||
)
|
||||
};
|
||||
}),
|
||||
drr()
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
// Copyright 2017-2019 @polkadot/api-derive 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 { combineLatest, Observable, of } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { ApiInterface$Rx } from '@plugnet/api/types';
|
||||
import { AccountId, Address } from '@plugnet/types';
|
||||
|
||||
import { DerivedBalancesMap } from '../types';
|
||||
import { drr } from '../util/drr';
|
||||
import { validatingBalance } from './validatingBalance';
|
||||
|
||||
export function validatingBalances (api: ApiInterface$Rx) {
|
||||
return (accountIds: Array<AccountId | Address | string>): Observable<DerivedBalancesMap> => {
|
||||
return !accountIds || !accountIds.length
|
||||
? of({}).pipe(drr())
|
||||
: combineLatest(
|
||||
accountIds.map(validatingBalance(api))
|
||||
).pipe(
|
||||
map((result) => {
|
||||
return result.reduce((balances, balance) => {
|
||||
balances[balance.accountId.toString()] = balance;
|
||||
|
||||
return balances;
|
||||
}, {} as DerivedBalancesMap);
|
||||
}),
|
||||
drr()
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
// Copyright 2017-2019 @polkadot/api-derive 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 { combineLatest, of, Observable } from 'rxjs';
|
||||
import { map, switchMap } from 'rxjs/operators';
|
||||
import { ApiInterface$Rx } from '@plugnet/api/types';
|
||||
import { AccountId, AccountIndex, Address, Balance } from '@plugnet/types';
|
||||
|
||||
import { idAndIndex } from '../accounts/idAndIndex';
|
||||
import { DerivedBalances } from '../types';
|
||||
import { drr } from '../util/drr';
|
||||
|
||||
const EMPTY_ACCOUNT = new AccountId(new Uint8Array(32));
|
||||
|
||||
export function votingBalance (api: ApiInterface$Rx) {
|
||||
return (address: AccountIndex | AccountId | Address | string): Observable<DerivedBalances> => {
|
||||
return idAndIndex(api)(address).pipe(
|
||||
switchMap(([accountId]) =>
|
||||
(accountId
|
||||
? combineLatest([
|
||||
of(accountId),
|
||||
api.query.balances.freeBalance(accountId),
|
||||
api.query.balances.reservedBalance(accountId)
|
||||
])
|
||||
: of([undefined, undefined, undefined])
|
||||
) as Observable<[AccountId?, Balance?, Balance?]>
|
||||
),
|
||||
map(([accountId = EMPTY_ACCOUNT, freeBalance = new Balance(0), reservedBalance = new Balance(0)]) => {
|
||||
return {
|
||||
accountId,
|
||||
freeBalance,
|
||||
nominatedBalance: new Balance(0),
|
||||
reservedBalance,
|
||||
stakingBalance: new Balance(0),
|
||||
votingBalance: new Balance(
|
||||
freeBalance.add(reservedBalance)
|
||||
)
|
||||
};
|
||||
}),
|
||||
drr()
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -3,17 +3,25 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { combineLatest, Observable, of } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { ApiInterface$Rx } from '@plugnet/api/types';
|
||||
import { AccountId, AccountIndex, Address } from '@plugnet/types';
|
||||
import { AccountId, AccountIndex, Address, VectorAny } from '@plugnet/types';
|
||||
|
||||
import { DerivedBalances } from '../types';
|
||||
import { drr } from '../util/drr';
|
||||
import { votingBalance } from './votingBalance';
|
||||
import { all } from './all';
|
||||
|
||||
export function votingBalances (api: ApiInterface$Rx) {
|
||||
return (addresses?: Array<AccountId | AccountIndex | Address | string>): Observable<Array<DerivedBalances>> => {
|
||||
return !addresses || !addresses.length
|
||||
? of([]).pipe(drr())
|
||||
: combineLatest(addresses.map(votingBalance(api))).pipe(drr());
|
||||
return (addresses?: Array<AccountId | AccountIndex | Address | string>): Observable<VectorAny<DerivedBalances>> => {
|
||||
return (
|
||||
!addresses || !addresses.length
|
||||
? of([] as Array<DerivedBalances>)
|
||||
: combineLatest(addresses.map(all(api)))
|
||||
).pipe(
|
||||
map((balances) =>
|
||||
new VectorAny(...balances)
|
||||
),
|
||||
drr()
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { switchMap } from 'rxjs/operators';
|
||||
import { ApiInterface$Rx } from '@plugnet/api/types';
|
||||
import { AccountId, AccountIndex, Address, Vector } from '@plugnet/types';
|
||||
import { AccountId, AccountIndex, Address, Vector, VectorAny } from '@plugnet/types';
|
||||
|
||||
import { idAndIndex } from '../accounts/idAndIndex';
|
||||
import { DerivedBalances } from '../types';
|
||||
@@ -13,7 +13,7 @@ import { drr } from '../util/drr';
|
||||
import { votingBalances } from './votingBalances';
|
||||
|
||||
export function votingBalancesNominatorsFor (api: ApiInterface$Rx) {
|
||||
return (address: AccountId | AccountIndex | Address | string): Observable<Array<DerivedBalances>> => {
|
||||
return (address: AccountId | AccountIndex | Address | string): Observable<VectorAny<DerivedBalances>> => {
|
||||
return idAndIndex(api)(address).pipe(
|
||||
switchMap(([accountId]) =>
|
||||
accountId
|
||||
|
||||
@@ -10,7 +10,8 @@ import { BlockNumber, Header } from '@plugnet/types';
|
||||
import { drr } from '../util/drr';
|
||||
|
||||
/**
|
||||
* @description Get the latest block number.
|
||||
* @name bestNumber
|
||||
* @returns The latest block number.
|
||||
* @example
|
||||
* <BR>
|
||||
*
|
||||
|
||||
@@ -10,13 +10,15 @@ import { BlockNumber, Header } from '@plugnet/types';
|
||||
import { drr } from '../util/drr';
|
||||
|
||||
/**
|
||||
* @description Get the latest finalised block number.
|
||||
* example
|
||||
* @name bestNumberFinalized
|
||||
* @returns A BlockNumber
|
||||
* @description Get the latest finalized block number.
|
||||
* @example
|
||||
* <BR>
|
||||
*
|
||||
* ```javascript
|
||||
* api.derive.chain.bestNumberFinalized((blockNumber) => {
|
||||
* console.log(`the current finalised block is #${blockNumber}`);
|
||||
* console.log(`the current finalized block is #${blockNumber}`);
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
|
||||
@@ -12,13 +12,15 @@ import { bestNumber } from './bestNumber';
|
||||
import { bestNumberFinalized } from './bestNumberFinalized';
|
||||
|
||||
/**
|
||||
* @description Calculates the lag between finalised head and best head
|
||||
* @name bestNumberLag
|
||||
* @returns A number of blocks
|
||||
* @description Calculates the lag between finalized head and best head
|
||||
* @example
|
||||
* <BR>
|
||||
*
|
||||
* ```javascript
|
||||
* api.derive.chain.bestNumberLag((lag) => {
|
||||
* console.log(`finalised is ${lag} blocks behind head`);
|
||||
* console.log(`finalized is ${lag} blocks behind head`);
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
|
||||
@@ -5,14 +5,17 @@
|
||||
import { Observable, combineLatest, of } from 'rxjs';
|
||||
import { catchError, map } from 'rxjs/operators';
|
||||
import { ApiInterface$Rx } from '@plugnet/api/types';
|
||||
import { Header, HeaderExtended } from '@plugnet/types';
|
||||
import { Header } from '@plugnet/types';
|
||||
|
||||
import { HeaderExtended } from '../type';
|
||||
import { drr } from '../util/drr';
|
||||
import { HeaderAndValidators } from './subscribeNewHead';
|
||||
|
||||
/**
|
||||
* @description Get the a specific block header and extend it with the author
|
||||
* @param hash: Uint8Array | string
|
||||
* @name bestNumberFinalized
|
||||
* @param {( Uint8Array | string )} hash - A block hash as U8 array or string.
|
||||
* @returns An array containing the block header and the block author
|
||||
* @description Get a specific block header and extend it with the author
|
||||
* @example
|
||||
* <BR>
|
||||
*
|
||||
|
||||
@@ -5,14 +5,17 @@
|
||||
import { Observable, combineLatest, of } from 'rxjs';
|
||||
import { filter, map, switchMap } from 'rxjs/operators';
|
||||
import { ApiInterface$Rx } from '@plugnet/api/types';
|
||||
import { AccountId, Header, HeaderExtended } from '@plugnet/types';
|
||||
import { AccountId, Header } from '@plugnet/types';
|
||||
|
||||
import { HeaderExtended } from '../type';
|
||||
import { drr } from '../util/drr';
|
||||
|
||||
export type HeaderAndValidators = [Header, Array<AccountId>];
|
||||
|
||||
/**
|
||||
* @description Subscribe to block headers and extend it with the author
|
||||
* @name subscribeNewHead
|
||||
* @returns An array containing the block header and the block author
|
||||
* @description An observable of the current block header and it's author
|
||||
* @example
|
||||
* <BR>
|
||||
*
|
||||
|
||||
@@ -2,46 +2,35 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { ApiInterface$Rx, QueryableStorageFunctionBase } from '@plugnet/api/types';
|
||||
import { Codec } from '@plugnet/types/types';
|
||||
import { ApiInterface$Rx } from '@plugnet/api/types';
|
||||
import { DerivedContractFees } from '../types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import { Observable, combineLatest, of } from 'rxjs';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { StructAny } from '@plugnet/types';
|
||||
|
||||
import { drr } from '../util/drr';
|
||||
|
||||
// TODO The contracts API is still in flux, so some of these may disappear or re-appear or get replaced. Instead of
|
||||
// failing here, do best effort - if something does not exist, return 0 for the specific value. Should be removed
|
||||
// when actually stable and no more changes expected
|
||||
function nonexistentZero (fn: QueryableStorageFunctionBase<Observable<Codec>, Observable<Codec>>): Observable<BN> {
|
||||
return fn
|
||||
? fn() as any as Observable<BN>
|
||||
: of(new BN(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the balances for all intentions and their nominators
|
||||
* Retrieves all the contract fees
|
||||
*/
|
||||
export function fees (api: ApiInterface$Rx) {
|
||||
// TODO Once we have muti support for disjointed queries, add here (so we make a single
|
||||
// query for all these to the node).
|
||||
return (): Observable<DerivedContractFees> => {
|
||||
return (combineLatest([
|
||||
nonexistentZero(api.query.contract.callBaseFee),
|
||||
nonexistentZero(api.query.contract.contractFee),
|
||||
nonexistentZero(api.query.contract.createBaseFee),
|
||||
nonexistentZero(api.query.contract.creationFee),
|
||||
nonexistentZero(api.query.contract.rentByteFee),
|
||||
nonexistentZero(api.query.contract.rentDepositOffset),
|
||||
nonexistentZero(api.query.contract.transactionBaseFee),
|
||||
nonexistentZero(api.query.contract.transactionByteFee),
|
||||
nonexistentZero(api.query.contract.transferFee),
|
||||
nonexistentZero(api.query.contract.tombstoneDeposit)
|
||||
return (api.queryMulti([
|
||||
api.query.contract.callBaseFee,
|
||||
api.query.contract.contractFee,
|
||||
api.query.contract.createBaseFee,
|
||||
api.query.contract.creationFee,
|
||||
api.query.contract.rentByteFee,
|
||||
api.query.contract.rentDepositOffset,
|
||||
api.query.contract.transactionBaseFee,
|
||||
api.query.contract.transactionByteFee,
|
||||
api.query.contract.transferFee,
|
||||
api.query.contract.tombstoneDeposit
|
||||
]) as any as Observable<Array<BN>>).pipe(
|
||||
map(([callBaseFee, contractFee, createBaseFee, creationFee, rentByteFee, rentDepositOffset, transactionBaseFee, transactionByteFee, transferFee, tombstoneDeposit]) => {
|
||||
return {
|
||||
map(([callBaseFee, contractFee, createBaseFee, creationFee, rentByteFee, rentDepositOffset, transactionBaseFee, transactionByteFee, transferFee, tombstoneDeposit]) =>
|
||||
new StructAny({
|
||||
callBaseFee,
|
||||
contractFee,
|
||||
createBaseFee,
|
||||
@@ -52,8 +41,8 @@ export function fees (api: ApiInterface$Rx) {
|
||||
transactionByteFee,
|
||||
transferFee,
|
||||
tombstoneDeposit
|
||||
};
|
||||
}),
|
||||
}) as DerivedContractFees
|
||||
),
|
||||
drr()
|
||||
);
|
||||
};
|
||||
|
||||
@@ -6,59 +6,32 @@ import BN from 'bn.js';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { ApiInterface$Rx } from '@plugnet/api/types';
|
||||
import { Option, ReferendumInfo, ReferendumIndex } from '@plugnet/types';
|
||||
import { Option, ReferendumInfo } from '@plugnet/types';
|
||||
import { isNull } from '@plugnet/util';
|
||||
|
||||
import { ReferendumInfoExtended } from '../type';
|
||||
import { drr } from '../util/drr';
|
||||
|
||||
/**
|
||||
* @name ReferendumInfoExtended
|
||||
* @description
|
||||
* A [[ReferendumInfo]] with an additional `index` field
|
||||
*/
|
||||
export class ReferendumInfoExtended extends ReferendumInfo {
|
||||
private _index: ReferendumIndex;
|
||||
export function constructInfo (index: BN | number, optionInfo?: Option<ReferendumInfo>): Option<ReferendumInfoExtended> {
|
||||
const info = optionInfo
|
||||
? optionInfo.unwrapOr(null)
|
||||
: null;
|
||||
|
||||
constructor (value: ReferendumInfo | ReferendumInfoExtended, index?: BN | number) {
|
||||
super(value);
|
||||
|
||||
this._index = value instanceof ReferendumInfoExtended
|
||||
? value.index
|
||||
: new ReferendumIndex(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Convenience getter, returns the referendumIndex
|
||||
*/
|
||||
get index (): ReferendumIndex {
|
||||
return this._index;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Creates the JSON representation
|
||||
*/
|
||||
toJSON (): any {
|
||||
return {
|
||||
...super.toJSON(),
|
||||
index: this.index.toJSON()
|
||||
};
|
||||
}
|
||||
return new Option<ReferendumInfoExtended>(
|
||||
ReferendumInfoExtended,
|
||||
isNull(info)
|
||||
? null
|
||||
: new ReferendumInfoExtended(info, index)
|
||||
);
|
||||
}
|
||||
|
||||
export function referendumInfo (api: ApiInterface$Rx) {
|
||||
return (index: BN | number): Observable<Option<ReferendumInfoExtended>> => {
|
||||
return (api.query.democracy.referendumInfoOf(index) as Observable<Option<ReferendumInfo>>)
|
||||
.pipe(
|
||||
map((optionInfo) => {
|
||||
const info = optionInfo.unwrapOr(null);
|
||||
|
||||
return new Option<ReferendumInfoExtended>(
|
||||
ReferendumInfoExtended,
|
||||
isNull(info)
|
||||
? null
|
||||
: new ReferendumInfoExtended(info, index)
|
||||
);
|
||||
}),
|
||||
map((optionInfo) =>
|
||||
constructInfo(index, optionInfo)
|
||||
),
|
||||
drr()
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,22 +3,29 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import BN from 'bn.js';
|
||||
import { combineLatest, Observable, of } from 'rxjs';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { ApiInterface$Rx } from '@plugnet/api/types';
|
||||
import { Option } from '@plugnet/types';
|
||||
import { Option, ReferendumInfo, VectorAny } from '@plugnet/types';
|
||||
|
||||
import { ReferendumInfoExtended } from '../type';
|
||||
import { drr } from '../util/drr';
|
||||
import { referendumInfo, ReferendumInfoExtended } from './referendumInfo';
|
||||
import { constructInfo } from './referendumInfo';
|
||||
|
||||
export function referendumInfos (api: ApiInterface$Rx) {
|
||||
const referendumInfoOf = referendumInfo(api);
|
||||
|
||||
return (ids: Array<BN | number> = []): Observable<Array<Option<ReferendumInfoExtended>>> => {
|
||||
return !ids || !ids.length
|
||||
? of([]).pipe(drr())
|
||||
: combineLatest(
|
||||
ids.map((id) => referendumInfoOf(id))
|
||||
).pipe(
|
||||
return (ids: Array<BN | number> = []): Observable<VectorAny<Option<ReferendumInfoExtended>>> => {
|
||||
return (
|
||||
!ids || !ids.length
|
||||
? of([] as Array<Option<ReferendumInfo>>)
|
||||
: api.query.democracy.referendumInfoOf.multi(ids) as Observable<VectorAny<Option<ReferendumInfo>>>
|
||||
).pipe(
|
||||
map((infos) =>
|
||||
new VectorAny(
|
||||
...ids.map((id, index) =>
|
||||
constructInfo(id, infos[index])
|
||||
)
|
||||
)
|
||||
),
|
||||
drr()
|
||||
);
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@ import BN from 'bn.js';
|
||||
import { combineLatest, Observable, of } from 'rxjs';
|
||||
import { map, switchMap } from 'rxjs/operators';
|
||||
import { ApiInterface$Rx } from '@plugnet/api/types';
|
||||
import { AccountId, Balance, Vector, Vote } from '@plugnet/types';
|
||||
import { AccountId, Balance, StructAny, Vector, VectorAny, Vote } from '@plugnet/types';
|
||||
|
||||
import { DerivedReferendumVote } from '../types';
|
||||
import { drr } from '../util/drr';
|
||||
@@ -14,7 +14,7 @@ import { votes } from './votes';
|
||||
import { votingBalances } from '../balances/votingBalances';
|
||||
|
||||
export function referendumVotesFor (api: ApiInterface$Rx) {
|
||||
return (referendumId: BN | number): Observable<Array<DerivedReferendumVote>> =>
|
||||
return (referendumId: BN | number): Observable<VectorAny<DerivedReferendumVote>> =>
|
||||
(api.query.democracy.votersFor(referendumId) as Observable<Vector<AccountId>>).pipe(
|
||||
switchMap((votersFor) =>
|
||||
combineLatest([
|
||||
@@ -24,11 +24,15 @@ export function referendumVotesFor (api: ApiInterface$Rx) {
|
||||
])
|
||||
),
|
||||
map(([votersFor, votes, balances]) =>
|
||||
votersFor.map((accountId, index): DerivedReferendumVote => ({
|
||||
accountId,
|
||||
balance: balances[index].votingBalance || new Balance(0),
|
||||
vote: votes[index] || new Vote(0)
|
||||
}))
|
||||
new VectorAny(
|
||||
...votersFor.map((accountId, index): DerivedReferendumVote =>
|
||||
new StructAny({
|
||||
accountId,
|
||||
balance: balances[index].votingBalance || new Balance(0),
|
||||
vote: votes[index] || new Vote(0)
|
||||
}) as DerivedReferendumVote
|
||||
)
|
||||
)
|
||||
),
|
||||
drr()
|
||||
);
|
||||
|
||||
@@ -2,21 +2,21 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { combineLatest, Observable, of } from 'rxjs';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { switchMap } from 'rxjs/operators';
|
||||
import { ApiInterface$Rx } from '@plugnet/api/types';
|
||||
import { Option, ReferendumIndex } from '@plugnet/types';
|
||||
|
||||
import { ReferendumInfoExtended } from '../type';
|
||||
import { drr } from '../util/drr';
|
||||
import { ReferendumInfoExtended } from './referendumInfo';
|
||||
import { referendumInfos } from './referendumInfos';
|
||||
|
||||
export function referendums (api: ApiInterface$Rx) {
|
||||
return (): Observable<Array<Option<ReferendumInfoExtended>>> =>
|
||||
(combineLatest([
|
||||
api.query.democracy.nextTally(),
|
||||
api.query.democracy.referendumCount()
|
||||
]) as Observable<[ReferendumIndex?, ReferendumIndex?]>).pipe(
|
||||
(api.queryMulti([
|
||||
api.query.democracy.nextTally,
|
||||
api.query.democracy.referendumCount
|
||||
]) as any as Observable<[ReferendumIndex?, ReferendumIndex?]>).pipe(
|
||||
switchMap(([nextTally, referendumCount]) =>
|
||||
referendumCount && nextTally && referendumCount.gt(nextTally) && referendumCount.gtn(0)
|
||||
? referendumInfos(api)(
|
||||
|
||||
@@ -3,23 +3,20 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import BN from 'bn.js';
|
||||
import { combineLatest, Observable, of } from 'rxjs';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { ApiInterface$Rx } from '@plugnet/api/types';
|
||||
import { AccountId, Vote } from '@plugnet/types';
|
||||
import { AccountId, VectorAny, Vote } from '@plugnet/types';
|
||||
|
||||
import { drr } from '../util/drr';
|
||||
|
||||
export function votes (api: ApiInterface$Rx) {
|
||||
return (referendumId: BN, accountIds: Array<AccountId> = []): Observable<Array<Vote>> => {
|
||||
return !accountIds || !accountIds.length
|
||||
? of([]).pipe(drr())
|
||||
: combineLatest(
|
||||
// FIXME Convert to multi
|
||||
accountIds.map(
|
||||
(accountId) => api.query.democracy.voteOf([referendumId, accountId]) as Observable<Vote>
|
||||
)
|
||||
).pipe(
|
||||
drr()
|
||||
);
|
||||
return (referendumId: BN, accountIds: Array<AccountId> = []): Observable<VectorAny<Vote>> => {
|
||||
return (
|
||||
!accountIds || !accountIds.length
|
||||
? of(new VectorAny<Vote>())
|
||||
: api.query.democracy.voteOf.multi(
|
||||
accountIds.map(accountId => [referendumId, accountId])
|
||||
) as Observable<VectorAny<Vote>>
|
||||
).pipe(drr());
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { Observable, from } from 'rxjs';
|
||||
|
||||
import ApiRx from '@plugnet/api/rx/Api';
|
||||
import { ApiInterface$Rx } from '@plugnet/api/types';
|
||||
import MockProvider from '@plugnet/rpc-provider/mock';
|
||||
@@ -39,9 +40,8 @@ describe('derive', () => {
|
||||
testFunction(api)('accounts', 'indexes', []);
|
||||
testFunction(api)('accounts', 'indexToId', []);
|
||||
|
||||
testFunction(api)('balances', 'all', []);
|
||||
testFunction(api)('balances', 'fees', []);
|
||||
testFunction(api)('balances', 'validatingBalance', []);
|
||||
testFunction(api)('balances', 'validatingBalances', []);
|
||||
testFunction(api)('balances', 'votingBalance', []);
|
||||
testFunction(api)('balances', 'votingBalances', []);
|
||||
testFunction(api)('balances', 'votingBalancesNominatorsFor', []);
|
||||
|
||||
@@ -13,6 +13,8 @@ import * as democracy from './democracy';
|
||||
import * as session from './session';
|
||||
import * as staking from './staking';
|
||||
|
||||
export * from './type';
|
||||
|
||||
// Put all derived functions in an object, for easier Object.keys()-ing.
|
||||
const functions = { accounts, balances, chain, democracy, session, staking };
|
||||
|
||||
@@ -47,7 +49,7 @@ export interface Derive {
|
||||
function injectFunctions (api: ApiInterface$Rx, derive: Derive, functions: DeriveCustom): Derive {
|
||||
Object.keys(functions).forEach((sectionName: string) => {
|
||||
const section = functions[sectionName as keyof Derive];
|
||||
const result = derive[sectionName as keyof Derive] || {};
|
||||
const result = derive[sectionName as keyof Derive] = derive[sectionName as keyof Derive] || {};
|
||||
|
||||
Object.keys(section).forEach((methodName) => {
|
||||
// @ts-ignore No idea how to make this work...
|
||||
|
||||
@@ -3,23 +3,17 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import BN from 'bn.js';
|
||||
import { combineLatest, Observable } from 'rxjs';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { ApiInterface$Rx } from '@plugnet/api/types';
|
||||
import { BlockNumber } from '@plugnet/types';
|
||||
|
||||
import { drr } from '../util/drr';
|
||||
import { info } from './info';
|
||||
|
||||
export function eraLength (api: ApiInterface$Rx) {
|
||||
return (): Observable<BN> =>
|
||||
(combineLatest([
|
||||
api.query.session.sessionLength(),
|
||||
api.query.staking.sessionsPerEra()
|
||||
]) as Observable<[BlockNumber?, BlockNumber?]>).pipe(
|
||||
map(
|
||||
([sessionLength, sessionsPerEra]) =>
|
||||
(sessionLength || new BN(1)).mul(sessionsPerEra || new BN(1))
|
||||
),
|
||||
info(api)().pipe(
|
||||
map(({ eraLength }) => eraLength),
|
||||
drr()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,33 +3,17 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import BN from 'bn.js';
|
||||
import { combineLatest, Observable } from 'rxjs';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { ApiInterface$Rx } from '@plugnet/api/types';
|
||||
import { BlockNumber } from '@plugnet/types';
|
||||
|
||||
import { sessionProgress } from './sessionProgress';
|
||||
import { drr } from '../util/drr';
|
||||
import { info } from './info';
|
||||
|
||||
export function eraProgress (api: ApiInterface$Rx) {
|
||||
return (): Observable<BN> =>
|
||||
(combineLatest([
|
||||
sessionProgress(api)(),
|
||||
api.query.session.currentIndex(),
|
||||
api.query.session.sessionLength(),
|
||||
api.query.staking.lastEraLengthChange(),
|
||||
api.query.staking.sessionsPerEra()
|
||||
]) as Observable<[BN, BlockNumber?, BlockNumber?, BlockNumber?, BlockNumber?]>).pipe(
|
||||
map(
|
||||
([sessionProgress, currentIndex, sessionLength, lastEraLengthChange, sessionsPerEra]) =>
|
||||
(currentIndex || new BN(0))
|
||||
.sub(lastEraLengthChange || new BN(0))
|
||||
.mod(sessionsPerEra || new BN(1))
|
||||
.mul(sessionLength || new BN(1))
|
||||
.add(sessionProgress || new BN(0)
|
||||
)
|
||||
|
||||
),
|
||||
info(api)().pipe(
|
||||
map(({ eraProgress }) => eraProgress),
|
||||
drr()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,4 +4,5 @@
|
||||
|
||||
export * from './eraLength';
|
||||
export * from './eraProgress';
|
||||
export * from './info';
|
||||
export * from './sessionProgress';
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
// Copyright 2017-2019 @polkadot/api-derive 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 { DerivedSessionInfo } from '../types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import { Observable, combineLatest } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { ApiInterface$Rx } from '@plugnet/api/types';
|
||||
import { BlockNumber, Option, StructAny } from '@plugnet/types';
|
||||
|
||||
import { drr } from '../util/drr';
|
||||
import { bestNumber } from '../chain';
|
||||
|
||||
type Result = [BN, [BlockNumber, Option<BlockNumber>, BlockNumber, BlockNumber, BlockNumber]];
|
||||
|
||||
const ZERO = new BN(0);
|
||||
|
||||
// internal helper to just split the logic - take all inputs, do the calculations and combine
|
||||
function createDerived ([bestNumber, [currentIndex, _lastLengthChange, sessionLength, lastEraLengthChange, sessionsPerEra]]: Result): DerivedSessionInfo {
|
||||
const eraLength = sessionLength.mul(sessionsPerEra);
|
||||
const lastLengthChange = _lastLengthChange
|
||||
? _lastLengthChange.unwrapOr(ZERO)
|
||||
: ZERO;
|
||||
const sessionProgress = bestNumber
|
||||
.sub(lastLengthChange)
|
||||
.add(sessionLength)
|
||||
.mod(sessionLength);
|
||||
const eraProgress = (currentIndex)
|
||||
.sub(lastEraLengthChange)
|
||||
.mod(sessionsPerEra)
|
||||
.mul(sessionLength)
|
||||
.add(sessionProgress);
|
||||
|
||||
return new StructAny({
|
||||
currentIndex,
|
||||
eraLength,
|
||||
eraProgress,
|
||||
lastEraLengthChange,
|
||||
lastLengthChange,
|
||||
sessionLength,
|
||||
sessionsPerEra,
|
||||
sessionProgress
|
||||
}) as DerivedSessionInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Retrieves all the session and era info and calculates specific valus on it sunh as the length of the session and eras
|
||||
*/
|
||||
export function info (api: ApiInterface$Rx) {
|
||||
return (): Observable<DerivedSessionInfo> =>
|
||||
// This is a much more optimal way to calculate since we only make a single call to the RPC backend
|
||||
// instead of making a subscription for each of the params (this means all others in session use)
|
||||
(combineLatest([
|
||||
bestNumber(api)(),
|
||||
api.queryMulti([
|
||||
api.query.session.currentIndex,
|
||||
api.query.session.lastLengthChange,
|
||||
api.query.session.sessionLength,
|
||||
api.query.staking.lastEraLengthChange,
|
||||
api.query.staking.sessionsPerEra
|
||||
])
|
||||
]) as any as Observable<Result>).pipe(
|
||||
map(createDerived),
|
||||
drr()
|
||||
);
|
||||
}
|
||||
@@ -3,28 +3,17 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import BN from 'bn.js';
|
||||
import { combineLatest, Observable } from 'rxjs';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { ApiInterface$Rx } from '@plugnet/api/types';
|
||||
import { BlockNumber, Option } from '@plugnet/types';
|
||||
|
||||
import { bestNumber } from '../chain';
|
||||
import { drr } from '../util/drr';
|
||||
import { info } from './info';
|
||||
|
||||
export function sessionProgress (api: ApiInterface$Rx) {
|
||||
return (): Observable<BN> =>
|
||||
combineLatest([
|
||||
bestNumber(api)(),
|
||||
api.query.session.sessionLength(),
|
||||
api.query.session.lastLengthChange()
|
||||
]).pipe(
|
||||
map(
|
||||
([bestNumber, sessionLength, lastLengthChange]) =>
|
||||
(bestNumber || new BN(0))
|
||||
.sub((lastLengthChange as Option<BlockNumber>).unwrapOr(new BN(0)))
|
||||
.add(sessionLength as BlockNumber || new BN(1))
|
||||
.mod(sessionLength as BlockNumber || new BN(1))
|
||||
),
|
||||
info(api)().pipe(
|
||||
map(({ sessionProgress }) => sessionProgress),
|
||||
drr()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,15 +9,6 @@ import { AccountId, Option } from '@plugnet/types';
|
||||
|
||||
import { drr } from '../util/drr';
|
||||
|
||||
function allBonds (api: ApiInterface$Rx, stashIds: Array<AccountId>) {
|
||||
return combineLatest(
|
||||
// FIXME Convert to multi
|
||||
stashIds.map((id) =>
|
||||
(api.query.staking.bonded(id) as Observable<Option<AccountId>>)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description From the list of stash accounts, retrieve the list of controllers
|
||||
*/
|
||||
@@ -28,7 +19,7 @@ export function controllers (api: ApiInterface$Rx) {
|
||||
switchMap(([stashIds]) =>
|
||||
combineLatest([
|
||||
of(stashIds),
|
||||
allBonds(api, stashIds)
|
||||
api.query.staking.bonded.multi(stashIds) as any as Observable<Array<Option<AccountId>>>
|
||||
])
|
||||
),
|
||||
drr()
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
export * from './controllers';
|
||||
export * from './intentionsBalances';
|
||||
export * from './info';
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
// Copyright 2017-2019 @polkadot/api-derive 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 { ApiInterface$Rx } from '@plugnet/api/types';
|
||||
import { AccountId, BlockNumber, Exposure, Option, StakingLedger,StructAny, ValidatorPrefs, UnlockChunk } from '@plugnet/types';
|
||||
import { DerivedStaking, DerivedUnlocking } from '../types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import { combineLatest, Observable, of } from 'rxjs';
|
||||
import { map, switchMap } from 'rxjs/operators';
|
||||
|
||||
import { bestNumber } from '../chain/bestNumber';
|
||||
import { drr } from '../util/drr';
|
||||
import { eraLength } from '../session/eraLength';
|
||||
import { isUndefined } from '@plugnet/util';
|
||||
|
||||
function calculateUnlocking (stakingLedger: StakingLedger | undefined, eraLength: BN, bestNumber: BlockNumber): DerivedUnlocking | undefined {
|
||||
if (isUndefined(stakingLedger)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// select the Unlockchunks that can't be redeemed yet.
|
||||
const unlockingChunks = stakingLedger.unlocking.filter((chunk) => remainingBlocks(chunk.era, eraLength, bestNumber).gtn(0));
|
||||
|
||||
if (!unlockingChunks.length) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// group the Unlockchunks that have the same era and sum their values
|
||||
const groupedResult = groupByEra(unlockingChunks);
|
||||
const results = Object.entries(groupedResult).map(([eraString, value]) => ({
|
||||
value,
|
||||
remainingBlocks: remainingBlocks(new BlockNumber(eraString), eraLength, bestNumber)
|
||||
}));
|
||||
|
||||
return results.length ? results : undefined;
|
||||
}
|
||||
|
||||
function groupByEra (list: UnlockChunk[]) {
|
||||
return list.reduce((map, { era, value }) => {
|
||||
const key = era.toString();
|
||||
|
||||
if (!map[key]) {
|
||||
map[key] = value;
|
||||
} else {
|
||||
map[key] = map[key].add(value);
|
||||
}
|
||||
|
||||
return map;
|
||||
}, {} as { [index: string]: BN });
|
||||
}
|
||||
|
||||
function redeemableSum (stakingLedger: StakingLedger | undefined, eraLength: BN, bestNumber: BlockNumber) {
|
||||
if (isUndefined(stakingLedger)) {
|
||||
return new BN(0);
|
||||
}
|
||||
|
||||
return stakingLedger.unlocking
|
||||
.filter((chunk) => remainingBlocks(chunk.era, eraLength, bestNumber).eqn(0))
|
||||
.reduce((curr, prev) => {
|
||||
return curr.add(prev.value);
|
||||
}, new BN(0));
|
||||
}
|
||||
|
||||
function remainingBlocks (era: BN, eraLength: BN, bestNumber: BlockNumber) {
|
||||
const remaining = eraLength.mul(era).sub(bestNumber);
|
||||
|
||||
return remaining.lten(0) ? new BN(0) : remaining;
|
||||
}
|
||||
|
||||
function withStashController (api: ApiInterface$Rx, accountId: AccountId, controllerId: AccountId): Observable<DerivedStaking> {
|
||||
const stashId = accountId;
|
||||
|
||||
return (
|
||||
combineLatest([
|
||||
eraLength(api)(),
|
||||
bestNumber(api)(),
|
||||
api.queryMulti([
|
||||
[api.query.session.nextKeyFor, controllerId],
|
||||
[api.query.staking.ledger, controllerId],
|
||||
[api.query.staking.nominators, stashId],
|
||||
[api.query.staking.stakers, stashId],
|
||||
[api.query.staking.validators, stashId]
|
||||
])
|
||||
]) as any as Observable<[BN, BlockNumber, [Option<AccountId>, Option<StakingLedger>, [Array<AccountId>], Exposure, [ValidatorPrefs]]]>
|
||||
).pipe(
|
||||
map(([eraLength, bestNumber,[nextKeyFor, _stakingLedger, [nominators], stakers, [validatorPrefs]]]) => {
|
||||
const stakingLedger = _stakingLedger.isSome ? _stakingLedger.unwrap() : undefined;
|
||||
|
||||
return new StructAny({
|
||||
accountId,
|
||||
controllerId,
|
||||
nextSessionId: nextKeyFor.isSome
|
||||
? nextKeyFor.unwrap()
|
||||
: undefined,
|
||||
nominators,
|
||||
redeemable: redeemableSum(stakingLedger, eraLength, bestNumber),
|
||||
stakers,
|
||||
stakingLedger,
|
||||
stashId,
|
||||
unlocking: calculateUnlocking(stakingLedger, eraLength, bestNumber),
|
||||
validatorPrefs
|
||||
}) as DerivedStaking;
|
||||
}),
|
||||
drr()
|
||||
);
|
||||
}
|
||||
|
||||
function withControllerLedger (api: ApiInterface$Rx, accountId: AccountId, stakingLedger: StakingLedger): Observable<DerivedStaking> {
|
||||
const controllerId = accountId;
|
||||
const stashId = stakingLedger.stash;
|
||||
|
||||
return (
|
||||
api.queryMulti([
|
||||
[api.query.session.nextKeyFor, controllerId],
|
||||
[api.query.staking.nominators, stashId],
|
||||
[api.query.staking.stakers, stashId],
|
||||
[api.query.staking.validators, stashId]
|
||||
]) as any as Observable<[Option<AccountId>, [Array<AccountId>], Exposure, [ValidatorPrefs]]>
|
||||
).pipe(
|
||||
map(([nextKeyFor, [nominators], stakers, [validatorPrefs]]) =>
|
||||
new StructAny({
|
||||
accountId,
|
||||
controllerId,
|
||||
nextSessionId: nextKeyFor.isSome
|
||||
? nextKeyFor.unwrap()
|
||||
: undefined,
|
||||
nominators,
|
||||
stakers,
|
||||
stakingLedger,
|
||||
stashId,
|
||||
validatorPrefs
|
||||
}) as DerivedStaking),
|
||||
drr()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description From either a stash or controller id, retrieve the controllerId, stashId, nextSessionId, stakingLedger and preferences
|
||||
*/
|
||||
export function info (api: ApiInterface$Rx) {
|
||||
return (_accountId: Uint8Array | string): Observable<DerivedStaking> => {
|
||||
const accountId = new AccountId(_accountId);
|
||||
|
||||
return (
|
||||
api.queryMulti([
|
||||
[api.query.staking.bonded, accountId], // try to map to controller
|
||||
[api.query.staking.ledger, accountId] // try to map to stash
|
||||
]) as any as Observable<[Option<AccountId>, Option<StakingLedger>]>
|
||||
).pipe(
|
||||
switchMap(([controllerId, stakingLedger]) =>
|
||||
controllerId.isSome
|
||||
// we have a controller, so input was a stash, great
|
||||
? withStashController(api, accountId, controllerId.unwrap())
|
||||
: (
|
||||
stakingLedger.isSome
|
||||
? withControllerLedger(api, accountId, stakingLedger.unwrap())
|
||||
// dangit, this is something else, ok, we are done
|
||||
: of(new StructAny({ accountId }) as DerivedStaking)
|
||||
)
|
||||
),
|
||||
drr()
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
// Copyright 2017-2019 @polkadot/api-derive 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 { Observable } from 'rxjs';
|
||||
import { switchMap } from 'rxjs/operators';
|
||||
import { ApiInterface$Rx } from '@plugnet/api/types';
|
||||
import { AccountId, Vector } from '@plugnet/types';
|
||||
|
||||
import { DerivedBalancesMap } from '../types';
|
||||
import { validatingBalances } from '../balances';
|
||||
import { drr } from '../util/drr';
|
||||
|
||||
/**
|
||||
* Get the balances for all intentions and their nominators
|
||||
*/
|
||||
export function intentionsBalances (api: ApiInterface$Rx) {
|
||||
return (): Observable<DerivedBalancesMap> =>
|
||||
// tslint:disable-next-line
|
||||
(api.query.staking.intentions() as Observable<Vector<AccountId>>)
|
||||
.pipe(
|
||||
switchMap(validatingBalances(api)),
|
||||
drr()
|
||||
) as Observable<DerivedBalancesMap>;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
// Copyright 2017-2019 @polkadot/api-derive 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, Header, u64 } from '@plugnet/types';
|
||||
|
||||
/**
|
||||
* @name HeaderExtended
|
||||
* @description
|
||||
* A [[Block]] header with an additional `author` field that indicates the block author
|
||||
*/
|
||||
export default class HeaderExtended extends Header {
|
||||
private _author?: AccountId;
|
||||
|
||||
constructor (header: Header | null = null, sessionValidators: Array<AccountId> = []) {
|
||||
super(header);
|
||||
|
||||
if (!header || !header.digest || !sessionValidators.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
let item = header.digest.logs.find((log) => log.isConsensus);
|
||||
let slot: u64 | undefined;
|
||||
|
||||
// extract author from the consensus (substrate 1.0, digest)
|
||||
if (item) {
|
||||
const consensus = item.asConsensus;
|
||||
|
||||
if (consensus.isAura) {
|
||||
slot = consensus.asAura[0];
|
||||
}
|
||||
} else {
|
||||
item = header.digest.logs.find((log) => log.isSeal);
|
||||
|
||||
// extract author from the seal (pre substrate 1.0, backwards compat)
|
||||
if (item) {
|
||||
slot = item.asSeal.slot;
|
||||
}
|
||||
}
|
||||
|
||||
// found a slot? Great, extract the validator
|
||||
if (slot) {
|
||||
this._author = sessionValidators[slot.toNumber() % sessionValidators.length];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Convenience method, returns the author for the block
|
||||
*/
|
||||
get author (): AccountId | undefined {
|
||||
return this._author;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Creates the JSON representation
|
||||
*/
|
||||
toJSON (): any {
|
||||
return {
|
||||
...super.toJSON(),
|
||||
author: this.author
|
||||
? this.author.toJSON()
|
||||
: undefined
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// Copyright 2017-2019 @polkadot/api-derive 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 BN from 'bn.js';
|
||||
import { ReferendumInfo, ReferendumIndex } from '@plugnet/types';
|
||||
|
||||
/**
|
||||
* @name ReferendumInfoExtended
|
||||
* @description
|
||||
* A [[ReferendumInfo]] with an additional `index` field
|
||||
*/
|
||||
export default class ReferendumInfoExtended extends ReferendumInfo {
|
||||
private _index: ReferendumIndex;
|
||||
|
||||
constructor (value: ReferendumInfo | ReferendumInfoExtended, index?: BN | number) {
|
||||
super(value);
|
||||
|
||||
this._index = value instanceof ReferendumInfoExtended
|
||||
? value.index
|
||||
: new ReferendumIndex(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Convenience getter, returns the referendumIndex
|
||||
*/
|
||||
get index (): ReferendumIndex {
|
||||
return this._index;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Creates the JSON representation
|
||||
*/
|
||||
toJSON (): any {
|
||||
return {
|
||||
...super.toJSON(),
|
||||
index: this.index.toJSON()
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright 2017-2019 @polkadot/api-derive 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 HeaderExtended } from './HeaderExtended';
|
||||
export { default as ReferendumInfoExtended } from './ReferendumInfoExtended';
|
||||
@@ -3,45 +3,72 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import BN from 'bn.js';
|
||||
import { AccountId, Balance, Vote } from '@plugnet/types';
|
||||
import { AccountId, Balance, BlockNumber, Exposure, Index, StakingLedger, StructAny, ValidatorPrefs, Vote } from '@plugnet/types';
|
||||
|
||||
export type DerivedBalances = {
|
||||
accountId: AccountId,
|
||||
freeBalance: Balance,
|
||||
nominatedBalance: Balance,
|
||||
reservedBalance: Balance,
|
||||
votingBalance: Balance,
|
||||
stakingBalance: Balance,
|
||||
nominators?: Array<DerivedBalances>
|
||||
};
|
||||
|
||||
export type DerivedFees = {
|
||||
creationFee: BN,
|
||||
existentialDeposit: BN,
|
||||
transactionBaseFee: BN,
|
||||
transactionByteFee: BN,
|
||||
transferFee: BN
|
||||
};
|
||||
export interface DerivedBalances extends StructAny {
|
||||
accountId: AccountId;
|
||||
accountNonce: Index;
|
||||
freeBalance: BN;
|
||||
lockedBalance: BN;
|
||||
availableBalance: BN;
|
||||
reservedBalance: BN;
|
||||
votingBalance: BN;
|
||||
vestedBalance: BN;
|
||||
}
|
||||
|
||||
export type DerivedBalancesMap = {
|
||||
[index: string]: DerivedBalances
|
||||
};
|
||||
|
||||
export type DerivedReferendumVote = {
|
||||
accountId: AccountId,
|
||||
balance: Balance,
|
||||
vote: Vote
|
||||
};
|
||||
export interface DerivedContractFees extends StructAny {
|
||||
callBaseFee: BN;
|
||||
contractFee: BN;
|
||||
createBaseFee: BN;
|
||||
creationFee: BN;
|
||||
rentByteFee: BN;
|
||||
rentDepositOffset: BN;
|
||||
transactionBaseFee: BN;
|
||||
transactionByteFee: BN;
|
||||
transferFee: BN;
|
||||
tombstoneDeposit: BN;
|
||||
}
|
||||
|
||||
export type DerivedContractFees = {
|
||||
callBaseFee: BN,
|
||||
contractFee: BN,
|
||||
createBaseFee: BN,
|
||||
creationFee: BN,
|
||||
rentByteFee: BN,
|
||||
rentDepositOffset: BN,
|
||||
transactionBaseFee: BN,
|
||||
transactionByteFee: BN,
|
||||
transferFee: BN,
|
||||
tombstoneDeposit: BN
|
||||
};
|
||||
export interface DerivedFees extends StructAny {
|
||||
creationFee: BN;
|
||||
existentialDeposit: BN;
|
||||
transactionBaseFee: BN;
|
||||
transactionByteFee: BN;
|
||||
transferFee: BN;
|
||||
}
|
||||
|
||||
export interface DerivedReferendumVote extends StructAny {
|
||||
accountId: AccountId;
|
||||
balance: Balance;
|
||||
vote: Vote;
|
||||
}
|
||||
|
||||
export interface DerivedSessionInfo extends StructAny {
|
||||
currentIndex: BlockNumber;
|
||||
eraLength: BN;
|
||||
eraProgress: BN;
|
||||
lastEraLengthChange: BlockNumber;
|
||||
lastLengthChange: BN;
|
||||
sessionLength: BlockNumber;
|
||||
sessionsPerEra: BlockNumber;
|
||||
sessionProgress: BN;
|
||||
}
|
||||
|
||||
export interface DerivedStaking extends StructAny {
|
||||
accountId: AccountId;
|
||||
controllerId?: AccountId;
|
||||
nextSessionId?: AccountId;
|
||||
nominators?: Array<AccountId>;
|
||||
redeemable?: BN;
|
||||
stakers?: Exposure;
|
||||
stakingLedger?: StakingLedger;
|
||||
stashId?: AccountId;
|
||||
unlocking?: DerivedUnlocking;
|
||||
validatorPrefs?: ValidatorPrefs;
|
||||
}
|
||||
|
||||
export type DerivedUnlocking = Array<{remainingBlocks: BN, value: BN}>;
|
||||
|
||||
@@ -2,8 +2,12 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import ApiPromise from '@plugnet/api/promise/Api';
|
||||
import { BlockNumber } from '@plugnet/types';
|
||||
import { DerivedStaking } from '../../src/types';
|
||||
import { SubmittableResult } from '../../../api/src';
|
||||
|
||||
import ApiPromise from '@plugnet/api/promise/Api';
|
||||
import testKeyring from '@plugnet/keyring/testing';
|
||||
import { WsProvider } from '@plugnet/rpc-provider';
|
||||
|
||||
// const WS = 'ws://127.0.0.1:9944/';
|
||||
@@ -22,7 +26,7 @@ describe.skip('derive e2e', () => {
|
||||
});
|
||||
|
||||
it('returns correct results', async () => {
|
||||
// https://github.com/plugblockchain/api.js/issues/777
|
||||
// https://github.com/polkadot-js/api/issues/777
|
||||
const block1 = await api.derive.chain.bestNumber();
|
||||
await new Promise((resolve) => setTimeout(resolve, 15000));
|
||||
const block2 = await api.derive.chain.bestNumber();
|
||||
@@ -32,11 +36,122 @@ describe.skip('derive e2e', () => {
|
||||
|
||||
it('subscribes to newHead, retrieving the actual validator', (done) => {
|
||||
return api.derive.chain.subscribeNewHead(({ author }) => {
|
||||
console.error('author', author.toString());
|
||||
console.log('author', author.toString());
|
||||
|
||||
if (author) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('retrieves the fees (api.queryMulti)', (done) => {
|
||||
return api.derive.balances.fees((fees) => {
|
||||
console.error('fees', JSON.stringify(fees));
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('retrieves the balances', (done) => {
|
||||
return api.derive.balances.all('5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y', (balance) => {
|
||||
console.error(JSON.stringify(balance));
|
||||
|
||||
if (balance.freeBalance.gtn(1)) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('retrieves balances for a problematic account', (done) => {
|
||||
return api.derive.balances.all('5F7BJL6Z4m8RLtK7nXEqqpEqhBbd535Z3CZeYF6ccvaQAY6N', (balance) => {
|
||||
console.error(JSON.stringify(balance));
|
||||
|
||||
if (balance.availableBalance.eqn(0)) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('retrieves all session info', (done) => {
|
||||
let count = 0;
|
||||
|
||||
return api.derive.session.info((info) => {
|
||||
console.error(JSON.stringify(info));
|
||||
|
||||
// 5 blocks only, then go our merry way
|
||||
if (++count === 5) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('retrieves all staking info (for controller)', (done) => {
|
||||
const accountId = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
|
||||
|
||||
return api.derive.staking.info(accountId, (info) => {
|
||||
console.error(JSON.stringify(info));
|
||||
|
||||
expect(info.accountId.eq(accountId)).toBe(true);
|
||||
expect(info.controllerId.eq(accountId)).toBe(true);
|
||||
expect(info.stashId.eq('5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY')).toBe(true);
|
||||
expect(info.stashId.eq(info.stakingLedger.stash)).toBe(true);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('retrieves all staking info (for stash)', (done) => {
|
||||
const accountId = '5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY';
|
||||
|
||||
return api.derive.staking.info(accountId, (info: DerivedStaking) => {
|
||||
console.error(JSON.stringify(info));
|
||||
|
||||
if (!info.stashId || !info.controllerId || !info.stakingLedger) {
|
||||
return done.fail(new Error('At leat one of info.stashId, info.controllerId or info.stakingLedger is undefined.'));
|
||||
}
|
||||
|
||||
expect(info.accountId.eq(accountId)).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);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('verifies derive.staking.unlocking',() => {
|
||||
const BOND_VALUE = 10;
|
||||
const UNBOND_VALUE = 1;
|
||||
const ALICE_STASH = '5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY';
|
||||
const ALICE = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
|
||||
const keyring = testKeyring();
|
||||
const aliceStashPair = keyring.getPair(ALICE_STASH);
|
||||
const alicePair = keyring.getPair(ALICE);
|
||||
|
||||
it('bondsExtra funds for Alice Stash', (done) => {
|
||||
return api.tx.staking.bondExtra(BOND_VALUE)
|
||||
.signAndSend(aliceStashPair, (result: SubmittableResult) => {
|
||||
if (result.status.isFinalized) {
|
||||
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('unbonds dots for Alice (from Alice Stash)', (done) => {
|
||||
return api.tx.staking.unbond(UNBOND_VALUE)
|
||||
.signAndSend(alicePair, (result: SubmittableResult) => {
|
||||
if (result.status.isFinalized) {
|
||||
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('verifies that derive.staking.unlocking isn\'t empty/undefined', () => {
|
||||
return api.derive.session.info(ALICE_STASH, (info: DerivedStaking) => {
|
||||
expect(info.unlocking).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,14 +3,19 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import BN from 'bn.js';
|
||||
|
||||
import ApiRx from '@plugnet/api/rx/Api';
|
||||
import { ApiInterface$Rx } from '@plugnet/api/types';
|
||||
import { BlockNumber } from '@plugnet/types';
|
||||
import { AccountId, AccountIndex, Balance, BlockNumber } from '@plugnet/types';
|
||||
import { WsProvider } from '@plugnet/rpc-provider';
|
||||
|
||||
const WS_LOCAL = 'ws://127.0.0.1:9944/';
|
||||
// const WS_POC3 = 'wss://poc3-rpc.polkadot.io/';
|
||||
|
||||
// Dev account Alice
|
||||
const ID = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
|
||||
const IX = 'F7Hs';
|
||||
|
||||
describe.skip('derive e2e', () => {
|
||||
let api: ApiInterface$Rx;
|
||||
|
||||
@@ -23,49 +28,191 @@ describe.skip('derive e2e', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
it('derive.chain.bestNumber', async (done) => {
|
||||
api.derive.chain.bestNumber().subscribe((blockNumber) => {
|
||||
expect(blockNumber instanceof BlockNumber).toBe(true);
|
||||
expect((blockNumber as BlockNumber).gten(0)).toBe(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('derive.session.sessionProgress', async (done) => {
|
||||
api.derive.session.sessionProgress().subscribe((progress) => {
|
||||
expect(progress instanceof BN).toBe(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('returns the intentions with balances', async (done) => {
|
||||
api.derive.staking.intentionsBalances().subscribe((balances) => {
|
||||
expect(Object.keys(balances as object)).not.toHaveLength(0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
// these only work on localhost, not the poc-3 URL
|
||||
// These derive.accounts tests only work on localhost, not the poc-3 URL
|
||||
// (and it is assuming it sent at least 1 tx)
|
||||
describe('accounts', () => {
|
||||
const ID = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
|
||||
const IX = 'F7Gh';
|
||||
describe('derive.accounts', () => {
|
||||
describe('idAndIndex', () => {
|
||||
it('looks up AccountId & AccountIndex from AccountId', async (done) => {
|
||||
// @ts-ignore silence warning until we have static types here
|
||||
api.derive.accounts.idAndIndex(ID).subscribe(([accountId, accountIndex]) => {
|
||||
expect(accountId.toString()).toEqual(ID);
|
||||
// The first emitted value for ix is undefined when passing the ID
|
||||
if (accountIndex) {
|
||||
expect(accountIndex.toString()).toEqual(IX);
|
||||
} else {
|
||||
expect(accountIndex).toEqual(undefined);
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('looks up id & index from id', async (done) => {
|
||||
// @ts-ignore silence warning until we have static types here
|
||||
api.derive.accounts.idAndIndex(ID).subscribe(([id, ix]) => {
|
||||
expect(id.toString()).toEqual(ID);
|
||||
expect(ix.toString()).toEqual(IX);
|
||||
done();
|
||||
it('looks up AccountId & AccountIndex from AccountIndex', async (done) => {
|
||||
// @ts-ignore silence warning until we have static types here
|
||||
api.derive.accounts.idAndIndex(IX).subscribe(([accountId, accountIndex]) => {
|
||||
// The first emitted value for id is undefined when passing the IX
|
||||
if (accountId) {
|
||||
expect(accountId.toString()).toEqual(ID);
|
||||
} else {
|
||||
expect(accountId).toEqual(undefined);
|
||||
}
|
||||
expect(accountIndex.toString()).toEqual(IX);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('looks up id & index from index', async (done) => {
|
||||
// @ts-ignore silence warning until we have static types here
|
||||
api.derive.accounts.idAndIndex(IX).subscribe(([id, ix]) => {
|
||||
expect(id.toString()).toEqual(ID);
|
||||
expect(ix.toString()).toEqual(IX);
|
||||
done();
|
||||
describe('indexToId', () => {
|
||||
it('looks up AccountId from AccountIndex', async (done) => {
|
||||
// @ts-ignore silence warning until we have static types here
|
||||
api.derive.accounts.indexToId(IX).subscribe((accountId) => {
|
||||
// The first emitted value for accountId is undefined when passing the IX
|
||||
if (accountId) {
|
||||
expect(accountId instanceof AccountId).toBe(true);
|
||||
expect(accountId.toString()).toEqual(ID);
|
||||
} else {
|
||||
expect(accountId).toEqual(undefined);
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('idToIndex', () => {
|
||||
it('looks up AccountIndex from AccountId', async (done) => {
|
||||
// @ts-ignore silence warning until we have static types here
|
||||
api.derive.accounts.idToIndex(ID).subscribe((accountIndex) => {
|
||||
// The first emitted value for AccountIndex is undefined when passing the ID
|
||||
if (accountIndex) {
|
||||
expect(accountIndex instanceof AccountIndex).toBe(true);
|
||||
expect(accountIndex.toString()).toEqual(IX);
|
||||
} else {
|
||||
expect(accountIndex).toEqual(undefined);
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('indexes', () => {
|
||||
it('looks up all AccountIndexes', async (done) => {
|
||||
// @ts-ignore silence warning until we have static types here
|
||||
api.derive.accounts.indexes().subscribe((accountIndexes) => {
|
||||
// A local dev chain should have the AccountIndex of Alice
|
||||
expect(accountIndexes).toHaveProperty(
|
||||
ID,
|
||||
new AccountIndex(IX)
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// these only work on localhost, not the poc-3 URL
|
||||
// (and it is assuming it sent at least 1 tx)
|
||||
describe('derive.balances', () => {
|
||||
describe('all', () => {
|
||||
it('It returns an object with all relevant balance information of an account', async (done) => {
|
||||
api.derive.balances.all(ID).subscribe((balances) => {
|
||||
expect(balances).toEqual(expect.objectContaining({
|
||||
accountId: expect.any(AccountId),
|
||||
availableBalance: expect.any(Balance),
|
||||
freeBalance: expect.any(Balance),
|
||||
lockedBalance: expect.any(Balance),
|
||||
reservedBalance: expect.any(Balance),
|
||||
vestedBalance: expect.any(Balance),
|
||||
votingBalance: expect.any(Balance)
|
||||
}));
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('fees', () => {
|
||||
it('fees: It returns an object with all relevant fees of type BN', async (done) => {
|
||||
api.derive.balances.fees().subscribe((fees) => {
|
||||
expect(fees).toEqual(expect.objectContaining({
|
||||
creationFee: expect.any(BN),
|
||||
existentialDeposit: expect.any(BN),
|
||||
transactionBaseFee: expect.any(BN),
|
||||
transactionByteFee: expect.any(BN),
|
||||
transferFee: expect.any(BN)
|
||||
}));
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('derive.chain', () => {
|
||||
describe('bestNumber', () => {
|
||||
it('Get the latest block number', async (done) => {
|
||||
api.derive.chain.bestNumber().subscribe((blockNumber) => {
|
||||
expect(blockNumber instanceof BlockNumber).toBe(true);
|
||||
expect((blockNumber as BlockNumber).gten(0)).toBe(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('bestNumberFinalized', () => {
|
||||
it('Get the latest finalised block number', async (done) => {
|
||||
api.derive.chain.bestNumberFinalized().subscribe((blockNumber) => {
|
||||
expect(blockNumber instanceof BlockNumber).toBe(true);
|
||||
expect((blockNumber as BlockNumber).gten(0)).toBe(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('bestNumberLag', () => {
|
||||
it('lag between finalised head and best head', async (done) => {
|
||||
api.derive.chain.bestNumberLag().subscribe((numberLag) => {
|
||||
expect(numberLag instanceof BlockNumber).toBe(true);
|
||||
expect((numberLag as BlockNumber).gten(0)).toBe(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getHeader', () => {
|
||||
it('gets a specific block header and extended with it\`s author', async (done) => {
|
||||
api.derive.chain.getHeader().subscribe((headerExtended) => {
|
||||
// WIP
|
||||
expect(headerExtended).toEqual(expect.arrayContaining([]));
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('subscribeNewHead', () => {
|
||||
it('gets an observable of the current block header and it\'s author', async (done) => {
|
||||
api.derive.chain.subscribeNewHead().subscribe((headerExtended) => {
|
||||
// WIP
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('derive.session', () => {
|
||||
describe('sessionProgress', () => {
|
||||
it('derive.session.sessionProgress', async (done) => {
|
||||
api.derive.session.sessionProgress().subscribe((progress) => {
|
||||
expect(progress instanceof BN).toBe(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('derive.staking', () => {
|
||||
describe('intentionsBalances', () => {
|
||||
it('returns the intentions with balances', async (done) => {
|
||||
api.derive.staking.intentionsBalances().subscribe((balances) => {
|
||||
expect(Object.keys(balances as object)).not.toHaveLength(0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+12
-12
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@plugnet/api",
|
||||
"version": "0.78.102",
|
||||
"version": "0.80.100",
|
||||
"description": "Promise and RxJS wrappers around the Plugnet JS RPC",
|
||||
"main": "index.js",
|
||||
"keywords": [
|
||||
@@ -13,9 +13,6 @@
|
||||
],
|
||||
"contributors": [],
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=8.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://registry.npmjs.org"
|
||||
@@ -29,13 +26,16 @@
|
||||
},
|
||||
"homepage": "https://github.com/plugblockchain/api.js/tree/master/packages/api#readme",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.4.4",
|
||||
"@plugnet/api-derive": "^0.78.102",
|
||||
"@plugnet/extrinsics": "^0.78.102",
|
||||
"@plugnet/rpc-provider": "^0.78.102",
|
||||
"@plugnet/rpc-rx": "^0.78.102",
|
||||
"@plugnet/storage": "^0.78.102",
|
||||
"@plugnet/types": "^0.78.102",
|
||||
"@plugnet/util-crypto": "^0.90.100"
|
||||
"@babel/runtime": "^7.4.5",
|
||||
"@plugnet/api-derive": "^0.80.100",
|
||||
"@plugnet/extrinsics": "^0.80.100",
|
||||
"@plugnet/rpc-provider": "^0.80.100",
|
||||
"@plugnet/rpc-rx": "^0.80.100",
|
||||
"@plugnet/storage": "^0.80.100",
|
||||
"@plugnet/types": "^0.80.100",
|
||||
"@plugnet/util-crypto": "^0.92.100"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@plugnet/keyring": "^0.92.100"
|
||||
}
|
||||
}
|
||||
|
||||
+106
-34
@@ -11,7 +11,7 @@ import {
|
||||
DecoratedRpc, DecoratedRpc$Method, DecoratedRpc$Section,
|
||||
Derive, DeriveSection, HashResult, U64Result,
|
||||
OnCallDefinition, OnCallFunction,
|
||||
QueryableModuleStorage, QueryableStorage, QueryableStorageFunction,
|
||||
QueryableModuleStorage, QueryableStorage, QueryableStorageFunction, QueryableStorageMulti, QueryableStorageMultiArg, QueryableStorageMultiArgs,
|
||||
SubmittableExtrinsicFunction, SubmittableExtrinsics, SubmittableModuleExtrinsics, Signer
|
||||
} from './types';
|
||||
|
||||
@@ -23,13 +23,14 @@ import extrinsicsFromMeta from '@plugnet/extrinsics/fromMetadata';
|
||||
import RpcBase from '@plugnet/rpc-core';
|
||||
import RpcRx from '@plugnet/rpc-rx';
|
||||
import storageFromMeta from '@plugnet/storage/fromMetadata';
|
||||
import { Event, getTypeRegistry, Hash, Metadata, Method, RuntimeVersion, Null } from '@plugnet/types';
|
||||
import { Linkage, LinkageResult } from '@plugnet/types/codec/Linkage';
|
||||
import { Event, getTypeRegistry, Hash, Metadata, Method, RuntimeVersion, Null, VectorAny } from '@plugnet/types';
|
||||
import Linkage, { LinkageResult } from '@plugnet/types/codec/Linkage';
|
||||
import { MethodFunction, ModulesWithMethods } from '@plugnet/types/primitive/Method';
|
||||
import { StorageFunction } from '@plugnet/types/primitive/StorageKey';
|
||||
import { assert, compactStripLength, isFunction, isObject, isUndefined, logger, u8aToHex } from '@plugnet/util';
|
||||
import { cryptoWaitReady } from '@plugnet/util-crypto';
|
||||
|
||||
import injectNodeCompat from './nodeCompat';
|
||||
import createSubmittable, { SubmittableExtrinsic } from './SubmittableExtrinsic';
|
||||
|
||||
type MetaDecoration = {
|
||||
@@ -58,11 +59,7 @@ try {
|
||||
* Put the `this.onCall` function of ApiRx here, because it is needed by
|
||||
* `api._rx`.
|
||||
*/
|
||||
function rxOnCall (
|
||||
method: OnCallFunction<RxResult, RxResult>,
|
||||
params: Array<CodecArg> = [],
|
||||
_callback?: CodecCallback
|
||||
): RxResult {
|
||||
function rxOnCall (method: OnCallFunction<RxResult, RxResult>, params: Array<CodecArg> = [], _callback?: CodecCallback): RxResult {
|
||||
return method(...params);
|
||||
}
|
||||
|
||||
@@ -74,6 +71,7 @@ export default abstract class ApiBase<CodecResult, SubscriptionResult> implement
|
||||
private _isReady: boolean = false;
|
||||
protected readonly _options: ApiOptions;
|
||||
private _query?: QueryableStorage<CodecResult, SubscriptionResult>;
|
||||
private _queryMulti: QueryableStorageMulti<CodecResult, SubscriptionResult>;
|
||||
private _rpc: DecoratedRpc<CodecResult, SubscriptionResult>;
|
||||
protected _rpcBase: RpcBase; // FIXME These two could be merged
|
||||
protected _rpcRx: RpcRx; // FIXME These two could be merged
|
||||
@@ -118,6 +116,8 @@ export default abstract class ApiBase<CodecResult, SubscriptionResult> implement
|
||||
this._rpcRx = new RpcRx(thisProvider);
|
||||
this._rpc = this.decorateRpc(this._rpcRx, this.onCall) as any; // FIXME 3.4.1
|
||||
this._rx.rpc = this.decorateRpc(this._rpcRx, rxOnCall);
|
||||
this._queryMulti = this.decorateMulti(this.onCall) as any; // as above :(
|
||||
this._rx.queryMulti = this.decorateMulti(rxOnCall);
|
||||
this._rx.signer = options.signer;
|
||||
|
||||
// we only re-register the types (global) if this is not a cloned instance
|
||||
@@ -221,6 +221,30 @@ export default abstract class ApiBase<CodecResult, SubscriptionResult> implement
|
||||
return this._query as QueryableStorage<CodecResult, SubscriptionResult>;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Allows for the querying of multiple storage entries and the combination thereof into a single result. This is a very optimal way to make multiple queries since it only makes a single connection to the node and retrieves the data over one subscription.
|
||||
*
|
||||
* @example
|
||||
* <BR>
|
||||
*
|
||||
* ```javascript
|
||||
* api.queryMulti(
|
||||
* [
|
||||
* // you can include the storage without any parameters
|
||||
* api.query.balances.existentialDeposit,
|
||||
* // or you can pass parameters to the storage query
|
||||
* [api.query.balances.freeBalance, '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY']
|
||||
* ],
|
||||
* ([existential, balance]) => {
|
||||
* console.log(`You have ${balance.sub(existential)} more than the existential deposit`);
|
||||
* }
|
||||
* );
|
||||
* ```
|
||||
*/
|
||||
get queryMulti (): QueryableStorageMulti<CodecResult, SubscriptionResult> {
|
||||
return this._queryMulti;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Contains all the raw rpc sections and their subsequent methods in the API as defined by the jsonrpc interface definitions. Unlike the dynamic `api.query` and `api.tx` sections, these methods are fixed (although extensible with node upgrades) and not determined by the runtime.
|
||||
*
|
||||
@@ -367,8 +391,12 @@ export default abstract class ApiBase<CodecResult, SubscriptionResult> implement
|
||||
// ignore
|
||||
});
|
||||
}, KEEPALIVE_INTERVAL);
|
||||
} catch (error) {
|
||||
l.error('FATAL: Unable to initialize the API: ', error.message);
|
||||
} catch (_error) {
|
||||
const error = new Error(`FATAL: Unable to initialize the API: ${_error.message}`);
|
||||
|
||||
l.error(error);
|
||||
|
||||
this.emit('error', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -398,7 +426,9 @@ export default abstract class ApiBase<CodecResult, SubscriptionResult> implement
|
||||
this._genesisHash = this._options.source.genesisHash;
|
||||
}
|
||||
|
||||
const extrinsics = extrinsicsFromMeta(this.runtimeMetadata.asV0);
|
||||
injectNodeCompat(this._runtimeVersion as RuntimeVersion);
|
||||
|
||||
const extrinsics = extrinsicsFromMeta(this.runtimeMetadata);
|
||||
const storage = storageFromMeta(this.runtimeMetadata);
|
||||
|
||||
this._extrinsics = this.decorateExtrinsics(extrinsics, this.onCall);
|
||||
@@ -413,7 +443,7 @@ export default abstract class ApiBase<CodecResult, SubscriptionResult> implement
|
||||
|
||||
// only inject if we are not a clone (global init)
|
||||
if (!this._options.source) {
|
||||
Event.injectMetadata(this.runtimeMetadata.asV0);
|
||||
Event.injectMetadata(this.runtimeMetadata);
|
||||
Method.injectMethods(extrinsics);
|
||||
}
|
||||
|
||||
@@ -466,7 +496,30 @@ export default abstract class ApiBase<CodecResult, SubscriptionResult> implement
|
||||
}, {} as DecoratedRpc<C, S>);
|
||||
}
|
||||
|
||||
private decorateMulti<C, S> (onCall: OnCallDefinition<C, S>): QueryableStorageMulti<C, S> {
|
||||
return ((calls: QueryableStorageMultiArgs<C, S>, callback?: CodecCallback): S => {
|
||||
const mapped = calls.map((arg: QueryableStorageMultiArg<C, S>): [QueryableStorageFunction<CodecResult, SubscriptionResult>, ...Array<CodecArg>] =>
|
||||
// the input is a QueryableStorageFunction, convert to StorageFunction
|
||||
Array.isArray(arg)
|
||||
? [arg[0].creator, ...arg.slice(1)]
|
||||
: [arg.creator] as any
|
||||
);
|
||||
|
||||
return onCall(
|
||||
() =>
|
||||
this._rpcRx.state
|
||||
.subscribeStorage(mapped)
|
||||
.pipe(map((results) => new VectorAny(...results))),
|
||||
[],
|
||||
callback
|
||||
) as S;
|
||||
}) as QueryableStorageMulti<C, S>;
|
||||
}
|
||||
|
||||
private decorateExtrinsics<C, S> (extrinsics: ModulesWithMethods, onCall: OnCallDefinition<C, S>): SubmittableExtrinsics<C, S> {
|
||||
const creator = (value: Uint8Array | string): SubmittableExtrinsic<C, S> =>
|
||||
createSubmittable(this.type, this._rx as ApiInterface$Rx, onCall, value);
|
||||
|
||||
return Object.keys(extrinsics).reduce((result, sectionName) => {
|
||||
const section = extrinsics[sectionName];
|
||||
|
||||
@@ -477,14 +530,14 @@ export default abstract class ApiBase<CodecResult, SubscriptionResult> implement
|
||||
}, {} as SubmittableModuleExtrinsics<C, S>);
|
||||
|
||||
return result;
|
||||
}, {} as SubmittableExtrinsics<C, S>);
|
||||
}, creator as SubmittableExtrinsics<C, S>);
|
||||
}
|
||||
|
||||
private decorateExtrinsicEntry<C, S> (method: MethodFunction, onCall: OnCallDefinition<C, S>): SubmittableExtrinsicFunction<C, S> {
|
||||
const decorated: any = (...params: Array<CodecArg>): SubmittableExtrinsic<C, S> =>
|
||||
const decorated = (...params: Array<CodecArg>): SubmittableExtrinsic<C, S> =>
|
||||
createSubmittable(this.type, this._rx as ApiInterface$Rx, onCall, method(...params));
|
||||
|
||||
return this.decorateFunctionMeta(method, decorated) as SubmittableExtrinsicFunction<C, S>;
|
||||
return this.decorateFunctionMeta(method, decorated as any) as SubmittableExtrinsicFunction<C, S>;
|
||||
}
|
||||
|
||||
private decorateStorage<C, S> (storage: Storage, onCall: OnCallDefinition<C, S>): QueryableStorage<C, S> {
|
||||
@@ -501,7 +554,7 @@ export default abstract class ApiBase<CodecResult, SubscriptionResult> implement
|
||||
}, {} as QueryableStorage<C, S>);
|
||||
}
|
||||
|
||||
private decorateStorageEntry<C, S> (method: StorageFunction, onCall: OnCallDefinition<C, S>): QueryableStorageFunction<C, S> {
|
||||
private decorateStorageEntry<C, S> (creator: StorageFunction, onCall: OnCallDefinition<C, S>): QueryableStorageFunction<C, S> {
|
||||
// These signatures are allowed and exposed here -
|
||||
// (arg?: CodecArg): CodecResult;
|
||||
// (arg: CodecArg, callback: CodecCallback): SubscriptionResult;
|
||||
@@ -515,13 +568,16 @@ export default abstract class ApiBase<CodecResult, SubscriptionResult> implement
|
||||
params = args.slice(0, args.length - 1);
|
||||
}
|
||||
|
||||
if (method.headKey && params.length === 0) {
|
||||
return this.decorateStorageEntryLinked(method, onCall, callback);
|
||||
if (creator.headKey && params.length === 0) {
|
||||
return this.decorateStorageEntryLinked(creator, onCall, callback);
|
||||
}
|
||||
|
||||
return onCall(
|
||||
(arg: CodecArg) => this._rpcRx.state
|
||||
.subscribeStorage([[method, arg]])
|
||||
(...args: CodecArg[]) => this._rpcRx.state
|
||||
.subscribeStorage([
|
||||
creator.meta.type.isDoubleMap
|
||||
? [creator, args]
|
||||
: [creator, ...args]])
|
||||
.pipe(
|
||||
// state_storage returns an array of values, since we have just subscribed to
|
||||
// a single entry, we pull that from the array and return it as-is
|
||||
@@ -534,37 +590,53 @@ export default abstract class ApiBase<CodecResult, SubscriptionResult> implement
|
||||
);
|
||||
}) as QueryableStorageFunction<C, S>;
|
||||
|
||||
decorated.at = (hash: Hash | Uint8Array | string, arg?: CodecArg): C =>
|
||||
decorated.creator = creator;
|
||||
|
||||
decorated.at = (hash: Hash | Uint8Array | string, arg1?: CodecArg, arg2?: CodecArg): C =>
|
||||
onCall(
|
||||
(arg: CodecArg) => this._rpcRx.state.getStorage([method, arg], hash),
|
||||
[arg]
|
||||
(arg1?: CodecArg, arg2?: CodecArg) => this._rpcRx.state.getStorage(
|
||||
creator.meta.type.isDoubleMap
|
||||
? [creator, [arg1, arg2]]
|
||||
: [creator, arg1],
|
||||
hash),
|
||||
[arg1, arg2]
|
||||
) as C;
|
||||
|
||||
// FIXME The unknown cast is needed since the onCall result, `C | S` cannot
|
||||
// be converted from C to the actual result required
|
||||
decorated.hash = (arg?: CodecArg): HashResult<C, S> =>
|
||||
decorated.hash = (arg1?: CodecArg, arg2?: CodecArg): HashResult<C, S> =>
|
||||
onCall(
|
||||
(arg: CodecArg) => this._rpcRx.state.getStorageHash([method, arg]),
|
||||
[arg]
|
||||
(arg1?: CodecArg, arg2?: CodecArg) => this._rpcRx.state.getStorageHash(
|
||||
creator.meta.type.isDoubleMap
|
||||
? [creator, [arg1, arg2]]
|
||||
: [creator, arg1]),
|
||||
[arg1, arg2]
|
||||
) as unknown as HashResult<C, S>;
|
||||
|
||||
decorated.key = (arg?: CodecArg): string =>
|
||||
u8aToHex(compactStripLength(method(arg))[1]);
|
||||
decorated.key = (arg1?: CodecArg, arg2?: CodecArg): string =>
|
||||
u8aToHex(compactStripLength(creator(creator.meta.type.isDoubleMap ? [arg1, arg2] : arg1))[1]);
|
||||
|
||||
decorated.multi = (args: Array<CodecArg>, callback?: CodecCallback): S =>
|
||||
// When using double map storage function, user need to path double map key as an array
|
||||
decorated.multi = (args: Array<CodecArg[] | CodecArg>, callback?: CodecCallback): S =>
|
||||
onCall(
|
||||
() => this._rpcRx.state.subscribeStorage(args.map((arg) => [method, arg])),
|
||||
() =>
|
||||
this._rpcRx.state
|
||||
.subscribeStorage(args.map((arg: CodecArg[] | CodecArg) => [creator, arg]))
|
||||
.pipe(map((results) => new VectorAny(...results))),
|
||||
[],
|
||||
callback
|
||||
) as unknown as S;
|
||||
|
||||
decorated.size = (arg?: CodecArg): U64Result<C, S> =>
|
||||
decorated.size = (arg1?: CodecArg, arg2?: CodecArg): U64Result<C, S> =>
|
||||
onCall(
|
||||
(arg: CodecArg) => this._rpcRx.state.getStorageSize([method, arg]),
|
||||
[arg]
|
||||
(arg1?: CodecArg, arg2?: CodecArg) => this._rpcRx.state.getStorageSize(
|
||||
creator.meta.type.isDoubleMap
|
||||
? [creator, [arg1, arg2]]
|
||||
: [creator, arg1]),
|
||||
[arg1, arg2]
|
||||
) as unknown as U64Result<C, S>;
|
||||
|
||||
return this.decorateFunctionMeta(method, decorated) as QueryableStorageFunction<C, S>;
|
||||
return this.decorateFunctionMeta(creator, decorated) as QueryableStorageFunction<C, S>;
|
||||
}
|
||||
|
||||
private decorateStorageEntryLinked<C, S> (method: StorageFunction, onCall: OnCallDefinition<C, S>, callback: CodecCallback | undefined): C | S {
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { KeyringPair } from '@plugnet/keyring/types';
|
||||
import { AccountId, Address, ExtrinsicStatus, EventRecord, getTypeRegistry, Hash, Index, Method, SignedBlock, Struct, Vector } from '@plugnet/types';
|
||||
import { Codec, CodecCallback, IExtrinsic, SignatureOptions } from '@plugnet/types/types';
|
||||
import { Codec, CodecCallback, IExtrinsic, IKeyringPair, SignatureOptions } from '@plugnet/types/types';
|
||||
import { ApiInterface$Rx, ApiType, OnCallDefinition, Signer } from './types';
|
||||
|
||||
import { Observable, of, combineLatest } from 'rxjs';
|
||||
@@ -62,14 +61,14 @@ export interface SubmittableExtrinsic<CodecResult, SubscriptionResult> extends I
|
||||
|
||||
send (statusCb: (result: SubmittableResult) => any): SumbitableResultSubscription<CodecResult, SubscriptionResult>;
|
||||
|
||||
sign (account: KeyringPair, _options: Partial<SignatureOptions>): this;
|
||||
sign (account: IKeyringPair, _options: Partial<SignatureOptions>): this;
|
||||
|
||||
signAndSend (account: KeyringPair | string | AccountId | Address, options?: Partial<Partial<SignatureOptions>>): SumbitableResultResult<CodecResult, SubscriptionResult>;
|
||||
signAndSend (account: IKeyringPair | string | AccountId | Address, options?: Partial<Partial<SignatureOptions>>): SumbitableResultResult<CodecResult, SubscriptionResult>;
|
||||
|
||||
signAndSend (account: KeyringPair | string | AccountId | Address, statusCb: StatusCb): SumbitableResultSubscription<CodecResult, SubscriptionResult>;
|
||||
signAndSend (account: IKeyringPair | string | AccountId | Address, statusCb: StatusCb): SumbitableResultSubscription<CodecResult, SubscriptionResult>;
|
||||
}
|
||||
|
||||
export default function createSubmittableExtrinsic<CodecResult, SubscriptionResult> (type: ApiType, api: ApiInterface$Rx, onCall: OnCallDefinition<CodecResult, SubscriptionResult>, extrinsic: Method, trackingCb?: (result: SubmittableResult) => any): SubmittableExtrinsic<CodecResult, SubscriptionResult> {
|
||||
export default function createSubmittableExtrinsic<CodecResult, SubscriptionResult> (type: ApiType, api: ApiInterface$Rx, onCall: OnCallDefinition<CodecResult, SubscriptionResult>, extrinsic: Method | Uint8Array | string, trackingCb?: (result: SubmittableResult) => any): SubmittableExtrinsic<CodecResult, SubscriptionResult> {
|
||||
const _extrinsic = new (getTypeRegistry().getOrThrow('Extrinsic'))(extrinsic) as SubmittableExtrinsic<CodecResult, SubscriptionResult>;
|
||||
const _noStatusCb = type === 'rxjs';
|
||||
|
||||
@@ -157,7 +156,7 @@ export default function createSubmittableExtrinsic<CodecResult, SubscriptionResu
|
||||
}
|
||||
},
|
||||
sign: {
|
||||
value: function (account: KeyringPair, _options: Partial<SignatureOptions>): SubmittableExtrinsic<CodecResult, SubscriptionResult> {
|
||||
value: function (account: IKeyringPair, _options: Partial<SignatureOptions>): SubmittableExtrinsic<CodecResult, SubscriptionResult> {
|
||||
// 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<SignatureOptions> = isBn(_options) || isNumber(_options)
|
||||
@@ -170,7 +169,7 @@ export default function createSubmittableExtrinsic<CodecResult, SubscriptionResu
|
||||
}
|
||||
},
|
||||
signAndSend: {
|
||||
value: function (account: KeyringPair | string | AccountId | Address, _options?: Partial<Partial<SignatureOptions>> | StatusCb, statusCb?: StatusCb): SumbitableResultResult<CodecResult, SubscriptionResult> | SumbitableResultSubscription<CodecResult, SubscriptionResult> {
|
||||
value: function (account: IKeyringPair | string | AccountId | Address, _options?: Partial<Partial<SignatureOptions>> | StatusCb, statusCb?: StatusCb): SumbitableResultResult<CodecResult, SubscriptionResult> | SumbitableResultSubscription<CodecResult, SubscriptionResult> {
|
||||
let options: Partial<Partial<SignatureOptions>> = {};
|
||||
|
||||
if (isFunction(_options)) {
|
||||
@@ -180,8 +179,8 @@ export default function createSubmittableExtrinsic<CodecResult, SubscriptionResu
|
||||
}
|
||||
|
||||
const isSubscription = _noStatusCb || !!statusCb;
|
||||
const isKeyringPair = isFunction((account as KeyringPair).address) && isFunction((account as KeyringPair).sign);
|
||||
const address = isKeyringPair ? (account as KeyringPair).address() : account.toString();
|
||||
const isKeyringPair = isFunction((account as IKeyringPair).address) && isFunction((account as IKeyringPair).sign);
|
||||
const address = isKeyringPair ? (account as IKeyringPair).address() : account.toString();
|
||||
let updateId: number | undefined;
|
||||
|
||||
return onCall(
|
||||
@@ -193,14 +192,14 @@ export default function createSubmittableExtrinsic<CodecResult, SubscriptionResu
|
||||
first(),
|
||||
mergeMap(async (nonce) => {
|
||||
if (isKeyringPair) {
|
||||
this.sign(account as KeyringPair, { ...options, nonce });
|
||||
this.sign(account as IKeyringPair, { ...options, nonce });
|
||||
} else {
|
||||
assert(api.signer, 'no signer exists');
|
||||
|
||||
updateId = await (api.signer as Signer).sign(_extrinsic, address, expandOptions({
|
||||
...options,
|
||||
nonce
|
||||
}));
|
||||
updateId = await (api.signer as Signer).sign(_extrinsic, address, {
|
||||
...expandOptions({ ...options, nonce }),
|
||||
genesisHash: api.genesisHash
|
||||
});
|
||||
}
|
||||
}),
|
||||
switchMap(() => {
|
||||
|
||||
@@ -5,9 +5,12 @@
|
||||
// Simple non-runnable checks to test type definitions in the editor itself
|
||||
|
||||
import { ApiPromise } from '@plugnet/api';
|
||||
import { HeaderExtended } from '@plugnet/api-derive';
|
||||
import testKeyring from '@plugnet/keyring/testingPairs';
|
||||
import { IExtrinsic, IMethod } from '@plugnet/types/types';
|
||||
import { Header, HeaderExtended } from '@plugnet/types';
|
||||
import { Header } from '@plugnet/types';
|
||||
|
||||
import { SubmittableResult } from './';
|
||||
|
||||
export default async function test () {
|
||||
const api = await ApiPromise.create();
|
||||
@@ -38,7 +41,7 @@ export default async function test () {
|
||||
|
||||
const unsub = await api.tx.balances
|
||||
.transfer(keyring.bob.address(), 12345)
|
||||
.signAndSend(keyring.alice, ({ status }) => {
|
||||
.signAndSend(keyring.alice, ({ status }: SubmittableResult) => {
|
||||
console.log('transfer status:', status.type);
|
||||
|
||||
unsub();
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
// 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 { Constructor } from '@plugnet/types/types';
|
||||
|
||||
import { EventRecord_0_76, RuntimeVersion, getTypeRegistry } from '@plugnet/types';
|
||||
|
||||
type Compat = {
|
||||
// an array of the spec-name and spec_version that denotes the first version that
|
||||
// does not support this feature. i.e. for EventRecord between 0-76, the new version
|
||||
// went live at 77 - hence the version here denotes 77
|
||||
nodeSpecs: Array<[string, number]>,
|
||||
types: {
|
||||
[index: string]: Constructor
|
||||
}
|
||||
};
|
||||
|
||||
const ANY_VERSION = 0xffffff;
|
||||
|
||||
const types: Array<Compat> = [
|
||||
{
|
||||
nodeSpecs: [
|
||||
['node', 77],
|
||||
['polkadot', 1000],
|
||||
['node-template', ANY_VERSION],
|
||||
['edgeware', ANY_VERSION]
|
||||
],
|
||||
types: {
|
||||
'EventRecord': EventRecord_0_76
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
export default function injectNodeCompat ({ specName, specVersion }: RuntimeVersion): void {
|
||||
types
|
||||
.filter(({ nodeSpecs }) =>
|
||||
nodeSpecs.some(([name, version]) =>
|
||||
specName.eq(name) &&
|
||||
specVersion.ltn(version)
|
||||
)
|
||||
)
|
||||
.forEach(({ types }) => {
|
||||
getTypeRegistry().register(types);
|
||||
});
|
||||
}
|
||||
@@ -3,11 +3,10 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { Hash, Metadata } from '@plugnet/types';
|
||||
|
||||
import Mock from '@plugnet/rpc-provider/mock/index';
|
||||
|
||||
import ApiPromise from './Api';
|
||||
import { ApiOptions } from '../types';
|
||||
import { ApiPromise } from './..';
|
||||
import { ApiOptions } from './../types';
|
||||
|
||||
describe.skip('Metadata queries', () => {
|
||||
let mock: Mock;
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { SubscriptionResult } from './types';
|
||||
|
||||
import Combinator from './Combinator';
|
||||
|
||||
describe('Combinator', () => {
|
||||
|
||||
@@ -48,17 +48,19 @@ export type U64Result<CodecResult, SubscriptionResult> =
|
||||
: Promise<U64>;
|
||||
|
||||
export interface QueryableStorageFunctionBase<CodecResult, SubscriptionResult> extends StorageFunction {
|
||||
(arg?: CodecArg): CodecResult;
|
||||
at: (hash: Hash | Uint8Array | string, arg?: CodecArg) => CodecResult;
|
||||
hash: (arg?: CodecArg) => HashResult<CodecResult, SubscriptionResult>;
|
||||
key: (arg?: CodecArg) => string;
|
||||
multi: (args: Array<CodecArg>, callback?: CodecCallback) => SubscriptionResult;
|
||||
size: (arg?: CodecArg) => U64Result<CodecResult, SubscriptionResult>;
|
||||
(arg1?: CodecArg, arg2?: CodecArg): CodecResult;
|
||||
at: (hash: Hash | Uint8Array | string, arg1?: CodecArg, arg2?: CodecArg) => CodecResult;
|
||||
creator: StorageFunction;
|
||||
hash: (arg1?: CodecArg, arg2?: CodecArg) => HashResult<CodecResult, SubscriptionResult>;
|
||||
key: (arg1?: CodecArg, arg2?: CodecArg) => string;
|
||||
multi: (args: Array<CodecArg[] | CodecArg>, callback?: CodecCallback) => SubscriptionResult;
|
||||
size: (arg1?: CodecArg, arg2?: CodecArg) => U64Result<CodecResult, SubscriptionResult>;
|
||||
}
|
||||
|
||||
interface QueryableStorageFunctionPromise<CodecResult, SubscriptionResult> extends QueryableStorageFunctionBase<CodecResult, SubscriptionResult> {
|
||||
(callback: CodecCallback): SubscriptionResult;
|
||||
(arg: CodecArg, callback: CodecCallback): SubscriptionResult;
|
||||
(arg1: CodecArg, arg2: CodecArg, callback: CodecCallback): SubscriptionResult;
|
||||
}
|
||||
|
||||
export type QueryableStorageFunction<CodecResult, SubscriptionResult> =
|
||||
@@ -70,6 +72,25 @@ export interface QueryableModuleStorage<CodecResult, SubscriptionResult> {
|
||||
[index: string]: QueryableStorageFunction<CodecResult, SubscriptionResult>;
|
||||
}
|
||||
|
||||
export type QueryableStorageMultiArg<CodecResult, SubscriptionResult> =
|
||||
QueryableStorageFunction<CodecResult, SubscriptionResult> |
|
||||
[QueryableStorageFunction<CodecResult, SubscriptionResult>, ...Array<CodecArg>];
|
||||
|
||||
export type QueryableStorageMultiArgs<CodecResult, SubscriptionResult> = Array<QueryableStorageMultiArg<CodecResult, SubscriptionResult>>;
|
||||
|
||||
export interface QueryableStorageMultiBase<CodecResult, SubscriptionResult> {
|
||||
(calls: QueryableStorageMultiArgs<CodecResult, SubscriptionResult>): SubscriptionResult;
|
||||
}
|
||||
|
||||
export interface QueryableStorageMultiPromise<CodecResult, SubscriptionResult> {
|
||||
(calls: QueryableStorageMultiArgs<CodecResult, SubscriptionResult>, callback: CodecCallback): SubscriptionResult;
|
||||
}
|
||||
|
||||
export type QueryableStorageMulti<CodecResult, SubscriptionResult> =
|
||||
CodecResult extends Observable<any>
|
||||
? QueryableStorageMultiBase<CodecResult, SubscriptionResult>
|
||||
: QueryableStorageMultiPromise<CodecResult, SubscriptionResult>;
|
||||
|
||||
export interface QueryableStorage<CodecResult, SubscriptionResult> {
|
||||
[index: string]: QueryableModuleStorage<CodecResult, SubscriptionResult>;
|
||||
}
|
||||
@@ -83,6 +104,7 @@ export interface SubmittableModuleExtrinsics<CodecResult, SubscriptionResult> {
|
||||
}
|
||||
|
||||
export interface SubmittableExtrinsics<CodecResult, SubscriptionResult> {
|
||||
(extrinsic: Uint8Array | string): SubmittableExtrinsic<CodecResult, SubscriptionResult>;
|
||||
[index: string]: SubmittableModuleExtrinsics<CodecResult, SubscriptionResult>;
|
||||
}
|
||||
|
||||
@@ -149,6 +171,7 @@ export interface ApiInterface$Decorated<CodecResult, SubscriptionResult> {
|
||||
runtimeVersion: RuntimeVersion;
|
||||
derive: Derive<CodecResult, SubscriptionResult>;
|
||||
query: QueryableStorage<CodecResult, SubscriptionResult>;
|
||||
queryMulti: QueryableStorageMulti<CodecResult, SubscriptionResult>;
|
||||
rpc: DecoratedRpc<CodecResult, SubscriptionResult>;
|
||||
tx: SubmittableExtrinsics<CodecResult, SubscriptionResult>;
|
||||
signer?: Signer;
|
||||
@@ -167,11 +190,15 @@ export interface ApiBaseInterface<CodecResult, SubscriptionResult> extends Reado
|
||||
once: (type: ApiInterface$Events, handler: (...args: Array<any>) => any) => this;
|
||||
}
|
||||
|
||||
export type SignerOptions = SignatureOptions & {
|
||||
genesisHash: Hash
|
||||
};
|
||||
|
||||
export interface Signer {
|
||||
/**
|
||||
* @description Signs an extrinsic, returning an id (>0) that can be used to retrieve updates
|
||||
*/
|
||||
sign (extrinsic: IExtrinsic, address: string, options: SignatureOptions): Promise<number>;
|
||||
sign (extrinsic: IExtrinsic, address: string, options: SignerOptions): Promise<number>;
|
||||
|
||||
/**
|
||||
* @description Receives an update for the extrinsic signed by a `signer.sign`
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
{"name":"Flipper","deploy":{"args":[]},"messages":[{"name":"flip","selector":970692492,"mutates":true,"args":[],"return_type":null},{"name":"get","selector":4266279973,"mutates":false,"args":[],"return_type":"bool"}]}
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,102 @@
|
||||
{
|
||||
"name": "SharedVec",
|
||||
"deploy": {
|
||||
"args": []
|
||||
},
|
||||
"messages": [
|
||||
{
|
||||
"name": "push",
|
||||
"selector": 865577567,
|
||||
"mutates": true,
|
||||
"args": [
|
||||
{
|
||||
"name": "value",
|
||||
"type": "i32"
|
||||
}
|
||||
],
|
||||
"return_type": {
|
||||
"Result<T,E>": {
|
||||
"T": [],
|
||||
"E": "u32"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "register",
|
||||
"selector": 572030971,
|
||||
"mutates": true,
|
||||
"args": [
|
||||
{
|
||||
"name": "mutator",
|
||||
"type": "AccountId"
|
||||
},
|
||||
{
|
||||
"name": "begin",
|
||||
"type": {
|
||||
"Option<T>": {
|
||||
"T": "u32"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "end",
|
||||
"type": {
|
||||
"Option<T>": {
|
||||
"T": "u32"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"return_type": {
|
||||
"Result<T,E>": {
|
||||
"T": [],
|
||||
"E": "u32"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "set",
|
||||
"selector": 2028185770,
|
||||
"mutates": true,
|
||||
"args": [
|
||||
{
|
||||
"name": "at",
|
||||
"type": "u32"
|
||||
},
|
||||
{
|
||||
"name": "to",
|
||||
"type": "i32"
|
||||
}
|
||||
],
|
||||
"return_type": {
|
||||
"Result<T,E>": {
|
||||
"T": "i32",
|
||||
"E": "u32"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "get",
|
||||
"selector": 4266279973,
|
||||
"mutates": false,
|
||||
"args": [
|
||||
{
|
||||
"name": "at",
|
||||
"type": "u32"
|
||||
}
|
||||
],
|
||||
"return_type": {
|
||||
"Option<T>": {
|
||||
"T": "i32"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "len",
|
||||
"selector": 1403873684,
|
||||
"mutates": false,
|
||||
"args": [],
|
||||
"return_type": "u32"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
// 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 { Extrinsic, SignedBlock } from '@plugnet/types';
|
||||
|
||||
import Api from './../../src/promise';
|
||||
|
||||
// To run these tests locally you need to run a Alexander full archive node locally
|
||||
describe.skip('alex archive queries (local)', () => {
|
||||
let api: Api;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.setTimeout(30000);
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
api = await Api.create();
|
||||
|
||||
return api;
|
||||
});
|
||||
|
||||
// https://github.com/polkadot-js/api/issues/845
|
||||
it('retrieves block with no issues', async () => {
|
||||
const metadata = await api.rpc.state.getMetadata('0x6f6f9bba0eed8e3ae9446c37eee763f93118b52a315a7b46090453ba6288da1f');
|
||||
|
||||
console.error(JSON.stringify(metadata, null, 2));
|
||||
|
||||
const block = await api.rpc.chain.getBlock('0x6f6f9bba0eed8e3ae9446c37eee763f93118b52a315a7b46090453ba6288da1f');
|
||||
|
||||
console.error(block);
|
||||
|
||||
expect(block).toBeDefined();
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
// https://github.com/polkadot-js/api/issues/846
|
||||
it('handles toJSON with no issues', async (done) => {
|
||||
return (
|
||||
api.rpc.chain.getBlock('0x85c62b581f38cb81c3e443d34392672beb1fb877017fd7237cc87704113259dc', (result: SignedBlock) => {
|
||||
const failed: Array<Extrinsic> = result.block.extrinsics.filter((extrinsic: Extrinsic) => {
|
||||
try {
|
||||
const json = extrinsic.method.toJSON();
|
||||
|
||||
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();
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,104 +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 Api from '../../src/promise';
|
||||
import WsProvider from '../../../rpc-provider/src/ws';
|
||||
|
||||
describe.skip('alex queries', () => {
|
||||
let api;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.setTimeout(3000000);
|
||||
});
|
||||
|
||||
describe.skip('Remote RPC queries', () => {
|
||||
beforeAll(async () => {
|
||||
api = await Api.create({
|
||||
provider: new WsProvider('wss://poc3-rpc.polkadot.io/')
|
||||
});
|
||||
|
||||
return api;
|
||||
});
|
||||
|
||||
it('retrieves the list of validators', (done) => {
|
||||
api.query.staking.validators((res) => {
|
||||
console.log('api.query.staking.validators():', res.toJSON());
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('retrieves a single value', (done) => {
|
||||
api.query.staking.validators('5EF7wdkP9XZq38fu2ioAvxoEhHUJUc1E2KbuSQSMGofRKhCL', (res) => {
|
||||
console.log('api.query.staking.validators(id):', res.toJSON());
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('derives a list of the controllers', (done) => {
|
||||
api.derive.staking.controllers((res) => {
|
||||
console.log('api.derive.staking.controllers:', JSON.stringify(res));
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('retrieves the list of nominators', (done) => {
|
||||
let count = 0;
|
||||
api.query.staking.nominators((res) => {
|
||||
console.log(`[${++count}]:: nominators(${res[0].length}):`, res.toJSON());
|
||||
|
||||
// done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe.skip('Local archive queries (for debug)', () => {
|
||||
beforeAll(async () => {
|
||||
api = await Api.create();
|
||||
|
||||
return api;
|
||||
});
|
||||
|
||||
// https://github.com/polkadot-js/api/issues/845
|
||||
it('retrieves block with no issues', async () => {
|
||||
const metadata = await api.rpc.state.getMetadata('0x6f6f9bba0eed8e3ae9446c37eee763f93118b52a315a7b46090453ba6288da1f');
|
||||
|
||||
console.error(JSON.stringify(metadata, null, 2));
|
||||
|
||||
const block = await api.rpc.chain.getBlock('0x6f6f9bba0eed8e3ae9446c37eee763f93118b52a315a7b46090453ba6288da1f')
|
||||
|
||||
console.error(block);
|
||||
|
||||
expect(block).toBeDefined();
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
// https://github.com/polkadot-js/api/issues/846
|
||||
it('handles toJSON with no issues', async (done) => {
|
||||
const signedBlock = await api.rpc.chain.getBlock('0x85c62b581f38cb81c3e443d34392672beb1fb877017fd7237cc87704113259dc');
|
||||
const failed = signedBlock.block.extrinsics.filter((extrinsic) => {
|
||||
try {
|
||||
const json = extrinsic.method.toJSON();
|
||||
|
||||
console.error(json);
|
||||
|
||||
return false;
|
||||
} catch (error) {
|
||||
console.log(extrinsic.method);
|
||||
console.log(extrinsic.method.keys());
|
||||
console.error(error);
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
expect(failed).toHaveLength(0);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,92 @@
|
||||
// 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 WsProvider from '@plugnet/rpc-provider/ws';
|
||||
import { EventRecord, Hash, Header, Vector } from '@plugnet/types';
|
||||
|
||||
import Api from './../../src/promise';
|
||||
|
||||
const WS_URL = 'wss://poc3-rpc.polkadot.io/';
|
||||
// const WS_URL = 'wss://substrate-rpc.parity.io/';
|
||||
|
||||
describe.skip('alex queries', () => {
|
||||
let api: Api;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.setTimeout(30000);
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
api = await Api.create({
|
||||
provider: new WsProvider(WS_URL)
|
||||
});
|
||||
|
||||
return api;
|
||||
});
|
||||
|
||||
it('retrieves the list of validators', (done) => {
|
||||
return (
|
||||
api.query.staking.validators((res) => {
|
||||
console.error(res);
|
||||
console.log('api.query.staking.validators():', res.toJSON());
|
||||
|
||||
done();
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
describe('retrieves a single value', () => {
|
||||
it('retrieves the list of stash validators', (done) => {
|
||||
return (
|
||||
api.query.staking.validators('5DuiZFa184E9iCwbh4WjXYvJ88NHvWJbS8SARY8Ev1YEqrri', (res) => {
|
||||
console.error(res);
|
||||
console.log('api.query.staking.validators(id):', res.toJSON());
|
||||
done();
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('Gets the hash of the last finalized header', async (done) => {
|
||||
return (
|
||||
api.rpc.chain.getFinalizedHead((head) => {
|
||||
expect(head instanceof Hash).toBe(true);
|
||||
done();
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('Subscribes to the best finalized header on ALEX', async (done) => {
|
||||
return (
|
||||
api.rpc.chain.subscribeFinalizedHeads((head) => {
|
||||
expect(head instanceof Header).toBe(true);
|
||||
done();
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('derives a list of the controllers', (done) => {
|
||||
return (
|
||||
api.derive.staking.controllers((res) => {
|
||||
console.log('api.derive.staking.controllers:', JSON.stringify(res));
|
||||
|
||||
done();
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('makes a query at a latest block (specified)', async () => {
|
||||
const header = await api.rpc.chain.getHeader() as Header;
|
||||
const events = await api.query.system.events.at(header.hash) as Vector<EventRecord>;
|
||||
|
||||
expect(events.length).not.toEqual(0);
|
||||
});
|
||||
|
||||
it('subscribes to events', (done) => {
|
||||
api.query.system.events((events: Array<EventRecord>) => {
|
||||
expect(events).not.toHaveLength(0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,107 +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 fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
import { ContractAbi } from '@plugnet/types';
|
||||
import testingPairs from '@plugnet/keyring/testingPairs';
|
||||
|
||||
import incrementer from '../data/incrementer.json';
|
||||
import erc20 from '../data/erc20.json';
|
||||
import Api from '../../src/promise';
|
||||
|
||||
describe.skip('e2e contracts', () => {
|
||||
let address;
|
||||
let codeHash;
|
||||
let keyring;
|
||||
let api;
|
||||
|
||||
beforeEach(async (done) => {
|
||||
if (!api) {
|
||||
api = await Api.create();
|
||||
|
||||
keyring = testingPairs({ type: 'sr25519' });
|
||||
}
|
||||
|
||||
jest.setTimeout(10000);
|
||||
done();
|
||||
});
|
||||
|
||||
describe('incrementer', () => {
|
||||
let abi;
|
||||
|
||||
beforeAll(() => {
|
||||
abi = new ContractAbi(incrementer);
|
||||
});
|
||||
|
||||
it('allows putCode', (done) => {
|
||||
const code = fs.readFileSync(path.join(__dirname, '../data/incrementer-opt.wasm')).toString('hex');
|
||||
|
||||
api.tx.contract
|
||||
.putCode(200000, `0x${code}`)
|
||||
.signAndSend(keyring.eve, (result) => {
|
||||
console.error('putCode', JSON.stringify(result));
|
||||
|
||||
if (result.status.isFinalized) {
|
||||
const record = result.findRecord('contract', 'CodeStored');
|
||||
|
||||
if (record) {
|
||||
codeHash = record.event.data[0];
|
||||
|
||||
done();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('allows contract create', (done) => {
|
||||
expect(codeHash).toBeDefined();
|
||||
|
||||
api.tx.contract
|
||||
.create(12345, 500000, codeHash, abi.deploy(12345))
|
||||
.signAndSend(keyring.bob, (result) => {
|
||||
console.error('create', JSON.stringify(result));
|
||||
|
||||
if (result.status.isFinalized) {
|
||||
const record = result.findRecord('contract', 'Instantiated');
|
||||
|
||||
if (record) {
|
||||
address = record.event.data[1];
|
||||
|
||||
done();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('allows contract call', (done) => {
|
||||
expect(address).toBeDefined();
|
||||
|
||||
api.tx.contract
|
||||
.call(address, 12345, 500000, abi.messages.inc(123))
|
||||
.signAndSend(keyring.bob, (result) => {
|
||||
console.error('call', JSON.stringify(result));
|
||||
|
||||
if (result.status.isFinalized && result.findRecord('system', 'ExtrinsicSuccess')) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('erc20', () => {
|
||||
let abi;
|
||||
|
||||
beforeAll(() => {
|
||||
abi = abi = new ContractAbi(erc20);
|
||||
});
|
||||
|
||||
it('has the attached methods', () => {
|
||||
expect(Object.keys(abi.messages)).toEqual(
|
||||
['totalSupply', 'balanceOf', 'allowance', 'transfer', 'approve', 'transferFrom']
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,118 @@
|
||||
// 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 fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
import { ContractAbi, Address, Hash } from '@plugnet/types';
|
||||
import { KeyringPair } from '@plugnet/keyring/types';
|
||||
import testingPairs from '@plugnet/keyring/testingPairs';
|
||||
|
||||
import Api from './../../src/promise';
|
||||
import flipperAbi from '../data/flipper.json';
|
||||
import erc20Abi from '../data/erc20.json';
|
||||
import { SubmittableResult } from './../../src';
|
||||
|
||||
describe.skip('Promise e2e contracts', () => {
|
||||
let address: Address;
|
||||
let codeHash: Hash;
|
||||
let keyring: {
|
||||
[index: string]: KeyringPair
|
||||
};
|
||||
let api: Api;
|
||||
|
||||
beforeEach(async (done) => {
|
||||
if (!api) {
|
||||
api = await Api.create();
|
||||
|
||||
keyring = testingPairs({ type: 'sr25519' });
|
||||
}
|
||||
|
||||
jest.setTimeout(10000);
|
||||
done();
|
||||
});
|
||||
|
||||
describe('flipper', () => {
|
||||
const MAX_GAS = 500000;
|
||||
let abi: ContractAbi;
|
||||
|
||||
beforeEach(() => {
|
||||
abi = new ContractAbi(flipperAbi);
|
||||
});
|
||||
|
||||
it('allows putCode', (done) => {
|
||||
const code = fs.readFileSync(path.join(__dirname, '../data/flipper-pruned.wasm')).toString('hex');
|
||||
|
||||
return (
|
||||
api.tx.contract
|
||||
.putCode(MAX_GAS, `0x${code}`)
|
||||
.signAndSend(keyring.eve, (result: SubmittableResult) => {
|
||||
console.error('putCode', JSON.stringify(result));
|
||||
|
||||
if (result.status.isFinalized) {
|
||||
const record = result.findRecord('contract', 'CodeStored');
|
||||
|
||||
if (record) {
|
||||
codeHash = record.event.data[0] as Hash;
|
||||
|
||||
done();
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('allows contract create', (done) => {
|
||||
expect(codeHash).toBeDefined();
|
||||
|
||||
return (
|
||||
api.tx.contract
|
||||
.create(12345, MAX_GAS, codeHash, abi.deploy())
|
||||
.signAndSend(keyring.bob, (result: SubmittableResult) => {
|
||||
console.error('create', JSON.stringify(result));
|
||||
|
||||
if (result.status.isFinalized) {
|
||||
const record = result.findRecord('contract', 'Instantiated');
|
||||
|
||||
if (record) {
|
||||
address = record.event.data[1] as Address;
|
||||
|
||||
done();
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('allows contract call', (done) => {
|
||||
expect(address).toBeDefined();
|
||||
|
||||
return (
|
||||
api.tx.contract
|
||||
.call(address, 12345, MAX_GAS, abi.messages.flip())
|
||||
.signAndSend(keyring.bob, (result: SubmittableResult) => {
|
||||
console.error('call', JSON.stringify(result));
|
||||
|
||||
if (result.status.isFinalized && result.findRecord('system', 'ExtrinsicSuccess')) {
|
||||
done();
|
||||
}
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('erc20', () => {
|
||||
let abi: ContractAbi;
|
||||
|
||||
beforeEach(() => {
|
||||
abi = new ContractAbi(erc20Abi);
|
||||
});
|
||||
|
||||
it('has the attached methods', () => {
|
||||
expect(Object.keys(abi.messages)).toEqual(
|
||||
['totalSupply', 'balanceOf', 'allowance', 'transfer', 'approve', 'transferFrom']
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,111 +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 BN from 'bn.js';
|
||||
import testingPairs from '@plugnet/keyring/testingPairs';
|
||||
import { LinkageResult } from '@plugnet/types/codec/Linkage';
|
||||
|
||||
import Api from '../../src/promise';
|
||||
|
||||
describe.skip('e2e queries', () => {
|
||||
const keyring = testingPairs({ type: 'ed25519' });
|
||||
let api;
|
||||
|
||||
beforeEach(async (done) => {
|
||||
if (!api) {
|
||||
api = await Api.create();
|
||||
}
|
||||
|
||||
jest.setTimeout(30000);
|
||||
done();
|
||||
});
|
||||
|
||||
it('makes the runtime, rpc, state & extrinsics available', () => {
|
||||
expect(api.genesisHash).toBeDefined();
|
||||
expect(api.runtimeMetadata).toBeDefined();
|
||||
expect(api.runtimeVersion).toBeDefined();
|
||||
expect(api.rpc).toBeDefined();
|
||||
expect(api.query).toBeDefined();
|
||||
expect(api.tx).toBeDefined();
|
||||
expect(api.derive).toBeDefined();
|
||||
});
|
||||
|
||||
it('queries state for a balance', async () => {
|
||||
const balance = await api.query.balances.freeBalance(keyring.alice.address());
|
||||
|
||||
expect(
|
||||
balance.isZero()
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('subscribes to rpc', (done) => {
|
||||
api.rpc.chain.subscribeNewHead((header) => {
|
||||
expect(header.blockNumber.isZero()).toBe(false);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('subscribes to finalized', (done) => {
|
||||
api.rpc.chain.subscribeFinalizedHeads((header) => {
|
||||
expect(header.blockNumber.isZero()).toBe(false);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('subscribes to derive', (done) => {
|
||||
api.derive.chain.subscribeNewHead((header) => {
|
||||
expect(header.blockNumber.isZero()).toBe(false);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('subscribes to queries', (done) => {
|
||||
api.query.system.accountNonce(keyring.ferdie.address(), (nonce) => {
|
||||
expect(nonce instanceof BN).toBe(true);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('subscribes to queries (default)', (done) => {
|
||||
api.query.staking.validators(keyring.ferdie.address(), (prefs) => {
|
||||
expect(prefs.unstakeThreshold.toNumber()).toBe(3);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('subscribes to a linked map (staking.validators)', (done) => {
|
||||
api.query.staking.validators((prefs) => {
|
||||
expect(prefs instanceof LinkageResult).toBe(true);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('subscribes to multiple results (freeBalance.multi)', (done) => {
|
||||
api.query.balances.freeBalance.multi([
|
||||
keyring.alice.address(),
|
||||
keyring.bob.address(),
|
||||
keyring.ferdie.address(),
|
||||
'5DTestUPts3kjeXSTMyerHihn1uwMfLj8vU8sqF7qYrFabHE'
|
||||
], (balances) => {
|
||||
expect(balances).toHaveLength(4);
|
||||
|
||||
console.error(balances);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('makes a query at a specific block', async () => {
|
||||
const header = await api.rpc.chain.getHeader();
|
||||
const events = await api.query.system.events.at(header.hash);
|
||||
|
||||
expect(events.length).not.toEqual(0);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,373 @@
|
||||
// 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 BN from 'bn.js';
|
||||
|
||||
import WsProvider from '@plugnet/rpc-provider/ws';
|
||||
import testingPairs from '@plugnet/keyring/testingPairs';
|
||||
import { LinkageResult } from '@plugnet/types/codec/Linkage';
|
||||
import { Balance, EventRecord, Header, Option, Vector } from '@plugnet/types';
|
||||
|
||||
import Api from './../../src/promise';
|
||||
|
||||
const ZERO = new BN(0);
|
||||
const WS_URL = 'ws://127.0.0.1:9944';
|
||||
// const WS_URL = 'wss://poc3-rpc.polkadot.io/';
|
||||
|
||||
describe.skip('Promise e2e queries', () => {
|
||||
const keyring = testingPairs({ type: 'ed25519' });
|
||||
let api: Api;
|
||||
|
||||
beforeEach(async (done) => {
|
||||
if (!api) {
|
||||
api = await Api.create({
|
||||
provider: new WsProvider(WS_URL)
|
||||
});
|
||||
}
|
||||
|
||||
jest.setTimeout(30000);
|
||||
done();
|
||||
});
|
||||
|
||||
it('makes the runtime, rpc, state & extrinsics available', () => {
|
||||
expect(api.genesisHash).toBeDefined();
|
||||
expect(api.runtimeMetadata).toBeDefined();
|
||||
expect(api.runtimeVersion).toBeDefined();
|
||||
expect(api.rpc).toBeDefined();
|
||||
expect(api.query).toBeDefined();
|
||||
expect(api.tx).toBeDefined();
|
||||
expect(api.derive).toBeDefined();
|
||||
});
|
||||
|
||||
it('queries state for a balance', async () => {
|
||||
return (
|
||||
api.query.balances.freeBalance(keyring.alice.address(), (balance) => {
|
||||
expect(balance).toBeInstanceOf(BN);
|
||||
expect(balance.isZero()).toBe(false);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('subscribes to rpc', (done) => {
|
||||
return (
|
||||
api.rpc.chain.subscribeNewHead((header) => {
|
||||
expect(header.blockNumber.isZero()).toBe(false);
|
||||
|
||||
done();
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('subscribes to finalized', (done) => {
|
||||
return (
|
||||
api.rpc.chain.subscribeFinalizedHeads((header) => {
|
||||
expect(header.blockNumber.isZero()).toBe(false);
|
||||
|
||||
done();
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('subscribes to derive', (done) => {
|
||||
return (
|
||||
api.derive.chain.subscribeNewHead((header) => {
|
||||
expect(header.blockNumber.isZero()).toBe(false);
|
||||
|
||||
done();
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('subscribes to queries', (done) => {
|
||||
return (
|
||||
api.query.system.accountNonce(keyring.ferdie.address(), (nonce) => {
|
||||
expect(nonce instanceof BN).toBe(true);
|
||||
|
||||
done();
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it.skip('subscribes to queries (default)', (done) => {
|
||||
return (
|
||||
api.query.staking.validators(keyring.ferdie.address(), (prefs) => {
|
||||
expect(prefs.unstakeThreshold.toNumber()).toBe(3);
|
||||
|
||||
done();
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('subscribes to a linked map (staking.validators)', (done) => {
|
||||
return (
|
||||
api.query.staking.validators((prefs) => {
|
||||
expect(prefs instanceof LinkageResult).toBe(true);
|
||||
|
||||
done();
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('subscribes to multiple results (freeBalance.multi)', (done) => {
|
||||
return (
|
||||
api.query.balances.freeBalance.multi([
|
||||
keyring.alice.address(),
|
||||
keyring.bob.address(),
|
||||
'5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y',
|
||||
keyring.ferdie.address()
|
||||
], (balances) => {
|
||||
expect(balances).toHaveLength(4);
|
||||
|
||||
done();
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('subscribes to multiple results (api.queryMulti)', (done) => {
|
||||
return api.queryMulti([
|
||||
[api.query.balances.freeBalance, keyring.alice.address()],
|
||||
[api.query.balances.freeBalance, keyring.bob.address()],
|
||||
[api.query.balances.freeBalance, '5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y'],
|
||||
[api.query.balances.freeBalance, keyring.ferdie.address()]
|
||||
], (balances: Array<BN>) => {
|
||||
expect(balances).toHaveLength(4);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('subscribes to derived balances (balances.all)', (done) => {
|
||||
return (
|
||||
api.derive.balances.all(
|
||||
keyring.alice.address(),
|
||||
(all) => {
|
||||
expect(all.accountId.toString()).toEqual(keyring.alice.address());
|
||||
|
||||
expect(all.freeBalance).toBeDefined();
|
||||
expect(all.freeBalance.gt(ZERO)).toBe(true);
|
||||
expect(all.availableBalance).toBeDefined();
|
||||
expect(all.availableBalance.gt(ZERO)).toBe(true);
|
||||
expect(all.reservedBalance).toBeDefined();
|
||||
expect(all.lockedBalance).toBeDefined();
|
||||
expect(all.vestedBalance).toBeDefined();
|
||||
done();
|
||||
}
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
it('makes a query at a latest block (specified)', async () => {
|
||||
const header = await api.rpc.chain.getHeader() as Header;
|
||||
const events = await api.query.system.events.at(header.hash) as Vector<EventRecord>;
|
||||
|
||||
expect(events.length).not.toEqual(0);
|
||||
|
||||
events.forEach(({ event: { data, method, section }, phase, topics }: EventRecord) => {
|
||||
console.error(phase.toString(), `: ${section}.${method}`, data.toString(), topics.toString());
|
||||
});
|
||||
});
|
||||
|
||||
it('subscribes to events', (done) => {
|
||||
return (
|
||||
api.query.system.events((events) => {
|
||||
expect(events).not.toHaveLength(0);
|
||||
done();
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
describe('with plain type', () => {
|
||||
const EXISTENTIAL_DEPOSIT = 500;
|
||||
it('queries correct value', async () => {
|
||||
const existentialDeposit = await api.query.balances.existentialDeposit() as Balance;
|
||||
|
||||
expect(existentialDeposit.toNumber()).toEqual(EXISTENTIAL_DEPOSIT);
|
||||
});
|
||||
|
||||
it('queries correct value at a specified block', async () => {
|
||||
const header = await api.rpc.chain.getHeader() as Header;
|
||||
const existentialDepositAt = await api.query.balances.existentialDeposit.at(header.hash) as Balance;
|
||||
|
||||
expect(existentialDepositAt.toNumber()).toEqual(EXISTENTIAL_DEPOSIT);
|
||||
});
|
||||
|
||||
it('subscribes to query and get correct result', (done) => {
|
||||
return api.query.balances.existentialDeposit((existentialDeposit) => {
|
||||
expect(existentialDeposit.toNumber()).toEqual(EXISTENTIAL_DEPOSIT);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('queries correct hash', async () => {
|
||||
const hash = await api.query.balances.existentialDeposit.hash();
|
||||
|
||||
expect(hash).toBeDefined();
|
||||
});
|
||||
|
||||
it('gets correct key', async () => {
|
||||
const key = api.query.balances.existentialDeposit.key();
|
||||
const existentialDepositData = await api.rpc.state.getStorage(key) as Option<any>;
|
||||
const existentialDepositRPC = new Balance(existentialDepositData.unwrapOr(undefined));
|
||||
|
||||
expect(existentialDepositRPC.toNumber()).toEqual(EXISTENTIAL_DEPOSIT);
|
||||
});
|
||||
|
||||
it('queries correct size', async () => {
|
||||
const size = await api.query.balances.existentialDeposit.size();
|
||||
|
||||
expect(size.toNumber()).not.toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with map type', () => {
|
||||
it('queries correct value', async () => {
|
||||
const balance = await api.query.balances.freeBalance(keyring.alice.address()) as Balance;
|
||||
|
||||
expect(balance.isZero()).toBe(false);
|
||||
});
|
||||
|
||||
it('queries correct value at a specified block', async () => {
|
||||
// assume the account Alice is only used in test(the balance of Alice does not change in this test case)
|
||||
const balance = await api.query.balances.freeBalance(keyring.alice.address());
|
||||
const header = await api.rpc.chain.getHeader() as Header;
|
||||
const balanceAt = await api.query.balances.freeBalance.at(header.hash, keyring.alice.address()) as Balance;
|
||||
|
||||
expect(balanceAt.isZero()).toBe(false);
|
||||
expect(balanceAt.toString()).toEqual(balance.toString());
|
||||
});
|
||||
|
||||
it('subscribes to query and get correct result', async (done) => {
|
||||
// assume the account Alice is only used in test(the balance of Alice does not change in this test case)
|
||||
const balance = await api.query.balances.freeBalance(keyring.alice.address());
|
||||
|
||||
return api.query.balances.freeBalance(keyring.alice.address(), (balanceSubscribed) => {
|
||||
expect(balanceSubscribed.isZero()).toBe(false);
|
||||
expect(balanceSubscribed.toString()).toEqual(balance.toString());
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('queries correct hash', async () => {
|
||||
const hash = await api.query.balances.freeBalance.hash(keyring.alice.address());
|
||||
|
||||
expect(hash).toBeDefined();
|
||||
});
|
||||
|
||||
it('gets correct key', async () => {
|
||||
// assume the account Alice is only used in test(the balance of Alice does not change in this test case)
|
||||
const key = api.query.balances.freeBalance.key(keyring.alice.address());
|
||||
const balanceData = await api.rpc.state.getStorage(key) as Option<any>;
|
||||
const balanceRPC = new Balance(balanceData.unwrapOr(undefined));
|
||||
|
||||
const balance = await api.query.balances.freeBalance(keyring.alice.address());
|
||||
|
||||
expect(balanceRPC.isZero()).toBe(false);
|
||||
expect(balanceRPC.toString()).toEqual(balance.toString());
|
||||
});
|
||||
|
||||
it('queries multiple results', async () => {
|
||||
// assume the account Alice and Bob are only used in test(the balance of them do not change in this test case)
|
||||
const balanceAlice = await api.query.balances.freeBalance(keyring.alice.address());
|
||||
const balanceBob = await api.query.balances.freeBalance(keyring.bob.address());
|
||||
|
||||
const balances = await api.query.balances.freeBalance.multi([
|
||||
keyring.alice.address(),
|
||||
keyring.bob.address()
|
||||
]);
|
||||
|
||||
expect(balances).toHaveLength(2);
|
||||
expect((balances as any)[0].toString()).toEqual(balanceAlice.toString());
|
||||
expect((balances as any)[1].toString()).toEqual(balanceBob.toString());
|
||||
});
|
||||
|
||||
it('subscribes to multiple queries and get correct results', async (done) => {
|
||||
// assume the account Alice and Bob are only used in test(the balance of them do not change in this test case)
|
||||
const balanceAlice = await api.query.balances.freeBalance(keyring.alice.address());
|
||||
const balanceBob = await api.query.balances.freeBalance(keyring.bob.address());
|
||||
|
||||
return api.query.balances.freeBalance.multi([
|
||||
keyring.alice.address(),
|
||||
keyring.bob.address()
|
||||
], (balances) => {
|
||||
expect(balances).toHaveLength(2);
|
||||
expect(balances[0].toString()).toEqual(balanceAlice.toString());
|
||||
expect(balances[1].toString()).toEqual(balanceBob.toString());
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('queries correct size', async () => {
|
||||
const size = await api.query.balances.freeBalance.size(keyring.alice.address());
|
||||
|
||||
expect(size.toNumber()).not.toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
// TODO Update ['any', '0x1234'] to the key of a known event topic and update EXPECTED_VALUE to the expected value
|
||||
describe('with double map type', () => {
|
||||
const KEY1 = 'any';
|
||||
const KEY2 = '0x1234';
|
||||
it('queries correct value', async () => {
|
||||
const eventTopics = await api.query.system.eventTopics(KEY1, KEY2);
|
||||
|
||||
expect(eventTopics.toJSON()).toEqual([]);
|
||||
});
|
||||
|
||||
it('queries correct value at a specified block', async () => {
|
||||
const header = await api.rpc.chain.getHeader() as Header;
|
||||
|
||||
// TODO check: this will throw the error: Encoding for input doesn't match output, created 0x00 from 0x
|
||||
const eventTopicsAt = await api.query.system.eventTopics.at(header.hash, KEY1, KEY2);
|
||||
expect(eventTopicsAt).toEqual(undefined);
|
||||
|
||||
// const eventTopicsAt = await api.query.system.eventTopics.at(header.hash, KEY1, KEY2);
|
||||
// expect(eventTopicsAt.toJSON()).toEqual([]);
|
||||
});
|
||||
|
||||
it('subscribes to query and get correct result', async (done) => {
|
||||
return api.query.system.eventTopics(KEY1, KEY2, (eventTopicsAt) => {
|
||||
expect(eventTopicsAt.toJSON()).toEqual([]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('queries correct hash', async () => {
|
||||
const hash = await api.query.system.eventTopics(KEY1, KEY2);
|
||||
|
||||
expect(hash).toBeDefined();
|
||||
});
|
||||
|
||||
it('gets correct key', async () => {
|
||||
const key = api.query.system.eventTopics.key(KEY1, KEY2);
|
||||
const eventTopicsData = await api.rpc.state.getStorage(key) as Option<any>;
|
||||
|
||||
expect(eventTopicsData.unwrapOr(undefined)).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('queries multiple results', async () => {
|
||||
const eventTopicsList = await api.query.system.eventTopics.multi([
|
||||
[KEY1, KEY2]
|
||||
]);
|
||||
|
||||
expect(eventTopicsList).toHaveLength(1);
|
||||
expect((eventTopicsList as any)[0].toJSON()).toEqual([]);
|
||||
});
|
||||
|
||||
it('subscribes to multiple queries and get correct results', async (done) => {
|
||||
return api.query.system.eventTopics.multi([
|
||||
[KEY1, KEY2]
|
||||
], (eventTopicsList) => {
|
||||
expect(eventTopicsList).toHaveLength(1);
|
||||
expect(eventTopicsList[0].toJSON()).toEqual([]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('queries correct size', async () => {
|
||||
const size = await api.query.system.eventTopics.size(KEY1, KEY2);
|
||||
|
||||
expect(size.toNumber()).toEqual(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,214 +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 Keyring from '@plugnet/keyring';
|
||||
import testingPairs from '@plugnet/keyring/testingPairs';
|
||||
import { randomAsHex } from '@plugnet/util-crypto';
|
||||
|
||||
import Api from '../../src/promise';
|
||||
import WsProvider from '../../../rpc-provider/src/ws';
|
||||
import SingleAccountSigner from "../util/SingleAccountSigner";
|
||||
|
||||
describe.skip('e2e transactions', () => {
|
||||
const keyring = testingPairs({ type: 'ed25519' });
|
||||
let api;
|
||||
|
||||
beforeEach(async (done) => {
|
||||
if (!api) {
|
||||
api = await Api.create({
|
||||
provider: new WsProvider('ws://127.0.0.1:9944')
|
||||
});
|
||||
}
|
||||
|
||||
jest.setTimeout(30000);
|
||||
done();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.setTimeout(5000);
|
||||
});
|
||||
|
||||
it('makes a transfer (sign, then send)', async (done) => {
|
||||
const nonce = await api.query.system.accountNonce(keyring.dave.address());
|
||||
|
||||
return api.tx.balances
|
||||
.transfer(keyring.eve.address(), 12345)
|
||||
.sign(keyring.dave, { nonce })
|
||||
.send(({ events, status }) => {
|
||||
console.log('Transaction status:', status.type);
|
||||
|
||||
if (status.isFinalized) {
|
||||
console.log('Completed at block hash', status.value.toHex());
|
||||
console.log('Events:');
|
||||
|
||||
events.forEach(({ phase, event: { data, method, section } }) => {
|
||||
console.log('\t', phase.toString(), `: ${section}.${method}`, data.toString());
|
||||
});
|
||||
|
||||
if (events.length) {
|
||||
done();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('makes a transfer (sign, then send - compat version)', async (done) => {
|
||||
const nonce = await api.query.system.accountNonce(keyring.dave.address());
|
||||
|
||||
return api.tx.balances
|
||||
.transfer(keyring.eve.address(), 12345)
|
||||
.sign(keyring.dave, nonce)
|
||||
.send(({ events, status }) => {
|
||||
console.log('Transaction status:', status.type);
|
||||
|
||||
if (status.isFinalized) {
|
||||
console.log('Completed at block hash', status.value.toHex());
|
||||
console.log('Events:');
|
||||
|
||||
events.forEach(({ phase, event: { data, method, section } }) => {
|
||||
console.log('\t', phase.toString(), `: ${section}.${method}`, data.toString());
|
||||
});
|
||||
|
||||
if (events.length) {
|
||||
done();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('makes a transfer (signAndSend)', async (done) => {
|
||||
return api.tx.balances
|
||||
.transfer(keyring.eve.address(), 12345)
|
||||
.signAndSend(keyring.dave, ({ events, status }) => {
|
||||
console.log('Transaction status:', status.type);
|
||||
|
||||
if (status.isFinalized) {
|
||||
console.log('Completed at block hash', status.value.toHex());
|
||||
console.log('Events:');
|
||||
|
||||
events.forEach(({ phase, event: { data, method, section } }) => {
|
||||
console.log('\t', phase.toString(), `: ${section}.${method}`, data.toString());
|
||||
});
|
||||
|
||||
if (events.length) {
|
||||
done();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('makes a transfer (signAndSend via Signer)', async (done) => {
|
||||
const signer = new SingleAccountSigner(keyring.dave);
|
||||
|
||||
api.setSigner(signer);
|
||||
|
||||
return api.tx.balances
|
||||
.transfer(keyring.eve.address(), 12345)
|
||||
.signAndSend(keyring.dave.address(), ({ events, status }) => {
|
||||
console.log('Transaction status:', status.type);
|
||||
|
||||
if (status.isFinalized) {
|
||||
console.log('Completed at block hash', status.value.toHex());
|
||||
console.log('Events:');
|
||||
|
||||
events.forEach(({ phase, event: { data, method, section } }) => {
|
||||
console.log('\t', phase.toString(), `: ${section}.${method}`, data.toString());
|
||||
});
|
||||
|
||||
if (events.length) {
|
||||
done();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('makes a transfer (signAndSend via Signer) - sad path', async () => {
|
||||
//no signer
|
||||
api.setSigner();
|
||||
|
||||
await expect(api.tx.balances
|
||||
.transfer(keyring.eve.address(), 12345)
|
||||
.signAndSend(keyring.alice.address())).rejects.toThrow('no signer exists');
|
||||
|
||||
const signer = new SingleAccountSigner(keyring.dave);
|
||||
|
||||
api.setSigner(signer);
|
||||
|
||||
//no callback
|
||||
await expect(api.tx.balances
|
||||
.transfer(keyring.eve.address(), 12345)
|
||||
.signAndSend(keyring.alice.address())).rejects.toThrow('does not have the keyringPair');
|
||||
|
||||
//with callback
|
||||
await expect(api.tx.balances
|
||||
.transfer(keyring.eve.address(), 12345)
|
||||
.signAndSend(keyring.alice.address(), ({ events, status }) => {
|
||||
})).rejects.toThrow('does not have the keyringPair');
|
||||
});
|
||||
|
||||
it('makes a transfer (no callback)', async () => {
|
||||
const hash = await api.tx.balances
|
||||
.transfer(keyring.eve.address(), 12345)
|
||||
.signAndSend(keyring.dave);
|
||||
|
||||
expect(hash.toHex()).toHaveLength(66);
|
||||
});
|
||||
|
||||
it('makes a proposal', async () => {
|
||||
// don't wait for status, just get hash. Here we generate a large-ish payload
|
||||
// to ensure that we can sign with the hashed version as well (and have it accepted)
|
||||
const hash = await api.tx.democracy
|
||||
.propose(api.tx.consensus.setCode(randomAsHex(4096)), 10000)
|
||||
.signAndSend(keyring.bob);
|
||||
|
||||
expect(hash.toHex()).toHaveLength(66);
|
||||
});
|
||||
|
||||
it('makes a transfer, and uses new balance to transfers to new', async (done) => {
|
||||
const pair = new Keyring().addFromUri('testing123', {}, 'ed25519');
|
||||
|
||||
function doOne (cb) {
|
||||
return api.tx.balances
|
||||
.transfer(pair.address(), 123456)
|
||||
.signAndSend(keyring.dave, ({ events, status }) => {
|
||||
console.log('One: Transaction status:', status.type);
|
||||
|
||||
if (status.isFinalized) {
|
||||
console.log('Completed at block hash', status.value.toHex());
|
||||
console.log('Events:');
|
||||
|
||||
events.forEach(({ phase, event: { data, method, section } }) => {
|
||||
console.log('\t', phase.toString(), `: ${section}.${method}`, data.toString());
|
||||
});
|
||||
|
||||
cb()
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function doTwo (cb) {
|
||||
return api.tx.balances
|
||||
.transfer(keyring.alice.address(), 12345)
|
||||
.signAndSend(pair, ({ events, status }) => {
|
||||
console.log('One: Transaction status:', status.type);
|
||||
|
||||
if (status.isFinalized) {
|
||||
console.log('Completed at block hash', status.value.toHex());
|
||||
console.log('Events:');
|
||||
|
||||
events.forEach(({ phase, event: { data, method, section } }) => {
|
||||
console.log('\t', phase.toString(), `: ${section}.${method}`, data.toString());
|
||||
});
|
||||
|
||||
cb()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// return doTwo(done);
|
||||
return doOne(() => {
|
||||
doTwo(done)
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,203 @@
|
||||
// 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 Keyring from '@plugnet/keyring';
|
||||
import testingPairs from '@plugnet/keyring/testingPairs';
|
||||
import { randomAsHex } from '@plugnet/util-crypto';
|
||||
import WsProvider from '@plugnet/rpc-provider/ws';
|
||||
import { EventRecord, ExtrinsicEra, Hash, Index, SignedBlock } from '@plugnet/types';
|
||||
|
||||
import SingleAccountSigner from '../util/SingleAccountSigner';
|
||||
import { SubmittableResult } from './../../src';
|
||||
import { Signer } from './../../src/types';
|
||||
import Api from './../../src/promise';
|
||||
|
||||
// log all events for the transfare, calling done() when finalized
|
||||
const logEvents = (done: () => {}) =>
|
||||
({ events, status }: SubmittableResult) => {
|
||||
console.log('Transaction status:', status.type);
|
||||
|
||||
if (status.isFinalized) {
|
||||
console.log('Completed at block hash', status.value.toHex());
|
||||
console.log('Events:');
|
||||
|
||||
events.forEach(({ phase, event: { data, method, section } }: EventRecord) => {
|
||||
console.log('\t', phase.toString(), `: ${section}.${method}`, data.toString());
|
||||
});
|
||||
|
||||
if (events.length) {
|
||||
done();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
describe.skip('Promise e2e transactions', () => {
|
||||
const keyring = testingPairs({ type: 'ed25519' });
|
||||
let api: Api;
|
||||
|
||||
beforeEach(async (done) => {
|
||||
if (!api) {
|
||||
api = await Api.create({
|
||||
provider: new WsProvider('ws://127.0.0.1:9944')
|
||||
});
|
||||
}
|
||||
|
||||
jest.setTimeout(30000);
|
||||
done();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.setTimeout(5000);
|
||||
});
|
||||
|
||||
it('can submit an extrinsic from hex', async (done) => {
|
||||
const nonce = await api.query.system.accountNonce(keyring.dave.address()) as Index;
|
||||
const hex = api.tx.balances
|
||||
.transfer(keyring.eve.address(), 12345)
|
||||
.sign(keyring.dave, { nonce })
|
||||
.toHex();
|
||||
|
||||
return api.tx(hex).send(logEvents(done));
|
||||
});
|
||||
|
||||
it('makes a transfer (sign, then send)', async (done) => {
|
||||
const nonce = await api.query.system.accountNonce(keyring.dave.address()) as Index;
|
||||
|
||||
return api.tx.balances
|
||||
.transfer(keyring.eve.address(), 12345)
|
||||
.sign(keyring.dave, { nonce })
|
||||
.send(logEvents(done));
|
||||
});
|
||||
|
||||
it('makes a transfer (sign, then send - compat version)', async (done) => {
|
||||
const nonce = await api.query.system.accountNonce(keyring.dave.address()) as Index;
|
||||
|
||||
return api.tx.balances
|
||||
.transfer(keyring.eve.address(), 12345)
|
||||
.sign(keyring.dave, { nonce })
|
||||
.send(logEvents(done));
|
||||
});
|
||||
|
||||
it('makes a transfer (signAndSend)', async (done) => {
|
||||
return api.tx.balances
|
||||
.transfer(keyring.eve.address(), 12345)
|
||||
.signAndSend(keyring.dave, logEvents(done));
|
||||
});
|
||||
|
||||
it('makes a transfer (signAndSend via Signer)', async (done) => {
|
||||
const signer = new SingleAccountSigner(keyring.dave) as Signer;
|
||||
|
||||
api.setSigner(signer);
|
||||
|
||||
return api.tx.balances
|
||||
.transfer(keyring.eve.address(), 12345)
|
||||
.signAndSend(keyring.dave.address(), logEvents(done));
|
||||
});
|
||||
|
||||
it('makes a transfer (signAndSend via Signer) with undefined Signer', async () => {
|
||||
const signer: any = undefined;
|
||||
// no signer
|
||||
api.setSigner(signer);
|
||||
|
||||
await expect(api.tx.balances
|
||||
.transfer(keyring.eve.address(), 12345)
|
||||
.signAndSend(keyring.alice.address())).rejects.toThrow('no signer exists');
|
||||
});
|
||||
|
||||
it('makes a transfer (signAndSend via Signer) with the wrong keyring pair', async () => {
|
||||
const signer: Signer = new SingleAccountSigner(keyring.dave);
|
||||
|
||||
api.setSigner(signer);
|
||||
|
||||
// no callback
|
||||
await expect(api.tx.balances
|
||||
.transfer(keyring.eve.address(), 12345)
|
||||
.signAndSend(keyring.alice.address())).rejects.toThrow('does not have the keyringPair');
|
||||
});
|
||||
|
||||
it('makes a transfer (signAndSend via Signer) with the wrong keyring pair with a callback', async () => {
|
||||
// with callback
|
||||
await expect(api.tx.balances
|
||||
.transfer(keyring.eve.address(), 12345)
|
||||
.signAndSend(keyring.alice.address(), (cb: any) => { /*do nothing */ })).rejects.toThrow('does not have the keyringPair');
|
||||
});
|
||||
|
||||
it('makes a transfer (no callback)', async () => {
|
||||
const hash = await api.tx.balances
|
||||
.transfer(keyring.eve.address(), 12345)
|
||||
.signAndSend(keyring.dave);
|
||||
|
||||
expect(hash.toHex()).toHaveLength(66);
|
||||
});
|
||||
|
||||
it('makes a proposal', async () => {
|
||||
// don't wait for status, just get hash. Here we generate a large-ish payload
|
||||
// to ensure that we can sign with the hashed version as well (and have it accepted)
|
||||
const hash: Hash = await api.tx.democracy
|
||||
.propose(api.tx.consensus.setCode(randomAsHex(4096)), 10000)
|
||||
.signAndSend(keyring.bob);
|
||||
|
||||
expect(hash.toHex()).toHaveLength(66);
|
||||
});
|
||||
|
||||
it('makes a transfer, and uses new balance to transfers to new', async (done) => {
|
||||
const pair = new Keyring().addFromUri('testing123', {}, 'ed25519');
|
||||
|
||||
function doOne (cb: any) {
|
||||
return api.tx.balances
|
||||
.transfer(pair.address(), 123456)
|
||||
.signAndSend(keyring.dave, logEvents(cb));
|
||||
}
|
||||
|
||||
function doTwo (cb: any) {
|
||||
return api.tx.balances
|
||||
.transfer(keyring.alice.address(), 12345)
|
||||
.signAndSend(pair, logEvents(cb));
|
||||
}
|
||||
|
||||
// return doTwo(done);
|
||||
return doOne(() => {
|
||||
return doTwo(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('makes a transfer with ERA (signAndSend)', async (done) => {
|
||||
const nonce = await api.query.system.accountNonce(keyring.dave.address()) as Index;
|
||||
const signedBlock = await api.rpc.chain.getBlock();
|
||||
const currentHeight = (signedBlock as SignedBlock).block.header.number;
|
||||
const exERA = new ExtrinsicEra({ current: currentHeight, period: 10 });
|
||||
const eraBirth = exERA.asMortalEra.birth(currentHeight.toNumber());
|
||||
const eraDeath = exERA.asMortalEra.death(currentHeight.toNumber());
|
||||
console.log('STARTED AT :' + eraBirth + ' EXPIRED AT :' + eraDeath);
|
||||
const eraHash = await api.rpc.chain.getBlockHash(eraBirth);
|
||||
const ex = api.tx.balances.transfer(keyring.eve.address(), 12345);
|
||||
const hash = await ex.signAndSend(keyring.dave, { blockHash: eraHash, era: exERA, nonce } as any);
|
||||
|
||||
expect(hash.toHex()).toHaveLength(66);
|
||||
done();
|
||||
});
|
||||
|
||||
it('makes a transfer with ERA (signAndSend) with invalid time', async (done) => {
|
||||
const nonce = await api.query.system.accountNonce(keyring.alice.address()) as Index;
|
||||
const signedBlock = await api.rpc.chain.getBlock();
|
||||
const currentHeight = (signedBlock as SignedBlock).block.header.number;
|
||||
const exERA = new ExtrinsicEra({ current: currentHeight, period: 4 });
|
||||
const eraBirth = exERA.asMortalEra.birth(currentHeight.toNumber());
|
||||
const eraDeath = exERA.asMortalEra.death(currentHeight.toNumber());
|
||||
console.log('STARTED AT :' + eraBirth + ' EXPIRED AT :' + eraDeath);
|
||||
const eraHash = await api.rpc.chain.getBlockHash(eraBirth);
|
||||
const ex = api.tx.balances.transfer(keyring.eve.address(), 12345);
|
||||
|
||||
return (
|
||||
api.rpc.chain.subscribeNewHead(async (header) => {
|
||||
if (header.blockNumber.toNumber() === eraDeath - 1) {
|
||||
const hash = await ex.signAndSend(keyring.alice, { blockHash: eraHash, era: exERA, nonce } as any);
|
||||
|
||||
expect(hash).toBeUndefined();
|
||||
done();
|
||||
}
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
+12
-11
@@ -2,14 +2,18 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import BN from 'bn.js';
|
||||
import { Observable } from 'rxjs';
|
||||
import { switchMap } from 'rxjs/operators';
|
||||
|
||||
import { Balance, Header } from '@plugnet/types';
|
||||
import testingPairs from '@plugnet/keyring/testingPairs';
|
||||
|
||||
import Api from '../../src/rx';
|
||||
|
||||
describe.skip('e2e queries', () => {
|
||||
describe.skip('Rx e2e queries', () => {
|
||||
const keyring = testingPairs({ type: 'ed25519' });
|
||||
let api;
|
||||
let api: Api;
|
||||
|
||||
beforeEach(async (done) => {
|
||||
api = await Api.create().toPromise();
|
||||
@@ -29,23 +33,20 @@ describe.skip('e2e queries', () => {
|
||||
|
||||
it('queries state for a balance', (done) => {
|
||||
api.query.balances.freeBalance(keyring.alice.address()).subscribe((balance) => {
|
||||
expect(
|
||||
balance.isZero()
|
||||
).toBe(false);
|
||||
|
||||
expect(balance).toBeInstanceOf(BN);
|
||||
expect((balance as Balance).isZero()).toBe(false);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('makes a query at a specific block', (done) => {
|
||||
api.rpc.chain
|
||||
.getHeader()
|
||||
(api.rpc.chain.getHeader() as Observable<Header>)
|
||||
.pipe(
|
||||
switchMap((header) =>
|
||||
api.query.system.events.at(header.hash)
|
||||
switchMap(({ hash }: Header) =>
|
||||
api.query.system.events.at(hash)
|
||||
)
|
||||
)
|
||||
.subscribe((events) => {
|
||||
.subscribe((events: any) => {
|
||||
expect(events.length).not.toEqual(0);
|
||||
done();
|
||||
});
|
||||
@@ -2,19 +2,23 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { Observable } from 'rxjs';
|
||||
import { first, switchMap } from 'rxjs/operators';
|
||||
|
||||
import { Index } from '@plugnet/types';
|
||||
import testingPairs from '@plugnet/keyring/testingPairs';
|
||||
|
||||
import Api from '../../src/rx';
|
||||
import Api from './../../src/rx';
|
||||
import { SubmittableResult } from './../../src';
|
||||
|
||||
describe.skip('e2e transactions', () => {
|
||||
describe.skip('Rx e2e transactions', () => {
|
||||
const keyring = testingPairs({ type: 'ed25519' });
|
||||
let api;
|
||||
let api: Api;
|
||||
|
||||
beforeEach(async (done) => {
|
||||
api = await Api.create().toPromise();
|
||||
jest.setTimeout(30000);
|
||||
done()
|
||||
done();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -22,18 +26,17 @@ describe.skip('e2e transactions', () => {
|
||||
});
|
||||
|
||||
it('makes a transfer', (done) => {
|
||||
api.query.system
|
||||
.accountNonce(keyring.alice.address())
|
||||
(api.query.system.accountNonce(keyring.alice.address()) as Observable<Index>)
|
||||
.pipe(
|
||||
first(),
|
||||
switchMap((nonce) =>
|
||||
switchMap((nonce: Index) =>
|
||||
api.tx.balances
|
||||
.transfer(keyring.bob.address(), 12345)
|
||||
.sign(keyring.alice, { nonce })
|
||||
.send()
|
||||
)
|
||||
)
|
||||
.subscribe(({ status }) => {
|
||||
.subscribe(({ status }: SubmittableResult) => {
|
||||
if (status.isFinalized) {
|
||||
done();
|
||||
}
|
||||
@@ -41,18 +44,17 @@ describe.skip('e2e transactions', () => {
|
||||
});
|
||||
|
||||
it('makes a proposal', (done) => {
|
||||
api.query.system
|
||||
.accountNonce(keyring.alice.address())
|
||||
(api.query.system.accountNonce(keyring.alice.address()) as Observable<Index>)
|
||||
.pipe(
|
||||
first(),
|
||||
switchMap((nonce) =>
|
||||
switchMap((nonce: Index) =>
|
||||
api.tx.democracy
|
||||
.propose(api.tx.consensus.setCode('0xdeadbeef'), 10000)
|
||||
.sign(keyring.alice, { nonce })
|
||||
.send()
|
||||
)
|
||||
)
|
||||
.subscribe(({ status }) => {
|
||||
.subscribe(({ status }: SubmittableResult) => {
|
||||
if (status.isFinalized) {
|
||||
done();
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@plugnet/rpc-core",
|
||||
"version": "0.78.102",
|
||||
"version": "0.80.100",
|
||||
"description": "A JavaScript wrapper for the Plugnet JsonRPC interface",
|
||||
"main": "index.js",
|
||||
"keywords": [
|
||||
@@ -29,10 +29,10 @@
|
||||
},
|
||||
"homepage": "https://github.com/plugblockchain/api.js/tree/master/packages/rpc-core#readme",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.4.4",
|
||||
"@plugnet/jsonrpc": "^0.78.102",
|
||||
"@plugnet/rpc-provider": "^0.78.102",
|
||||
"@plugnet/types": "^0.78.102",
|
||||
"@plugnet/util": "^0.90.100"
|
||||
"@babel/runtime": "^7.4.5",
|
||||
"@plugnet/jsonrpc": "^0.80.100",
|
||||
"@plugnet/rpc-provider": "^0.80.100",
|
||||
"@plugnet/types": "^0.80.100",
|
||||
"@plugnet/util": "^0.92.100"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,78 +3,93 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import BN from 'bn.js';
|
||||
import storage from '@plugnet/storage/static';
|
||||
|
||||
import fromMetadata from '@plugnet/storage/fromMetadata';
|
||||
import { Storage } from '@plugnet/storage/types';
|
||||
import Metadata from '@plugnet/types/Metadata';
|
||||
import rpcMetadataV3 from '@plugnet/types/Metadata/v3/static';
|
||||
import rpcMetadataV4 from '@plugnet/types/Metadata/v4/static';
|
||||
|
||||
import Api from '.';
|
||||
|
||||
const ADDR_ONE = '5DkQbYAExs3M2sZgT1Ec3mKfZnAQCL4Dt9beTCknkCUn5jzo';
|
||||
const ADDR_TWO = '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCtGwF';
|
||||
|
||||
const ENC_ONE = '0x4af2c53fce3ec33c6ccccf22e926f1a7';
|
||||
const ENC_TWO = '0x3e62f7ed6e788e1337bce2a97b68a12a';
|
||||
function formattingTests (version: string, storage: Storage, encodedValues: [String, String]) {
|
||||
const [ENC_ONE, ENC_TWO] = encodedValues;
|
||||
|
||||
describe('formatting', () => {
|
||||
let api: Api;
|
||||
let provider: any;
|
||||
describe(`formatting with Metadata ${version}`, () => {
|
||||
let api: Api;
|
||||
let provider: any;
|
||||
|
||||
beforeEach(() => {
|
||||
provider = {
|
||||
send: jest.fn((method, params) =>
|
||||
Promise.resolve('0x0102')
|
||||
),
|
||||
subscribe: jest.fn((type, method, params, subscription) =>
|
||||
subscription(null, {
|
||||
block: '0x1234',
|
||||
changes: [
|
||||
[ENC_ONE, '0x0102'],
|
||||
[ENC_TWO, '0x0201']
|
||||
]
|
||||
})
|
||||
)
|
||||
};
|
||||
api = new Api(provider);
|
||||
beforeEach(() => {
|
||||
provider = {
|
||||
send: jest.fn((method, params) =>
|
||||
Promise.resolve('0x01020000000000000000000000000000')
|
||||
),
|
||||
subscribe: jest.fn((type, method, params, subscription) =>
|
||||
subscription(null, {
|
||||
block: '0x1234',
|
||||
changes: [
|
||||
[ENC_ONE, '0x01020000000000000000000000000000'],
|
||||
[ENC_TWO, '0x02010000000000000000000000000000']
|
||||
]
|
||||
})
|
||||
)
|
||||
};
|
||||
api = new Api(provider);
|
||||
});
|
||||
|
||||
it('encodes key (with params), decoding response', () => {
|
||||
return api.state
|
||||
.getStorage([storage.balances.freeBalance, ADDR_ONE])
|
||||
.then((value) => {
|
||||
expect(
|
||||
provider.send
|
||||
).toHaveBeenCalledWith(
|
||||
'state_getStorage',
|
||||
[ENC_ONE]
|
||||
);
|
||||
expect(value.toNumber()).toEqual(513);
|
||||
});
|
||||
});
|
||||
|
||||
it('encodes multiple keys, decoding multiple results', (done) => {
|
||||
return api.state.subscribeStorage(
|
||||
[
|
||||
[storage.balances.freeBalance, ADDR_ONE],
|
||||
[storage.balances.freeBalance, ADDR_TWO]
|
||||
],
|
||||
(value: any) => {
|
||||
console.error(value);
|
||||
|
||||
expect(
|
||||
provider.subscribe
|
||||
).toHaveBeenCalledWith(
|
||||
'state_storage',
|
||||
'state_subscribeStorage',
|
||||
[[ENC_ONE, ENC_TWO]],
|
||||
expect.anything()
|
||||
);
|
||||
expect(
|
||||
value.map((balance: BN) =>
|
||||
balance.toNumber()
|
||||
)
|
||||
).toEqual([513, 258]);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
it('encodes key (with params), decoding response', () => {
|
||||
return api.state
|
||||
.getStorage(
|
||||
[storage.balances.freeBalance, ADDR_ONE]
|
||||
)
|
||||
.then((value) => {
|
||||
expect(
|
||||
provider.send
|
||||
).toHaveBeenCalledWith(
|
||||
'state_getStorage',
|
||||
[ENC_ONE]
|
||||
);
|
||||
expect(value.toNumber()).toEqual(513);
|
||||
});
|
||||
});
|
||||
|
||||
it('encodes multiple keys, decoding multiple results', (done) => {
|
||||
return api.state.subscribeStorage(
|
||||
[
|
||||
[storage.balances.freeBalance, ADDR_ONE],
|
||||
[storage.balances.freeBalance, ADDR_TWO]
|
||||
],
|
||||
(value: any) => {
|
||||
console.error(value);
|
||||
|
||||
expect(
|
||||
provider.subscribe
|
||||
).toHaveBeenCalledWith(
|
||||
'state_storage',
|
||||
'state_subscribeStorage',
|
||||
[[ENC_ONE, ENC_TWO]],
|
||||
expect.anything()
|
||||
);
|
||||
expect(
|
||||
value.map((balance: BN) =>
|
||||
balance.toNumber()
|
||||
)
|
||||
).toEqual([513, 258]);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
formattingTests(
|
||||
'v3',
|
||||
fromMetadata(new Metadata(rpcMetadataV3)),
|
||||
['0x4af2c53fce3ec33c6ccccf22e926f1a7', '0x3e62f7ed6e788e1337bce2a97b68a12a']
|
||||
);
|
||||
formattingTests(
|
||||
'v4',
|
||||
fromMetadata(new Metadata(rpcMetadataV4)),
|
||||
['0xec8f96437274a883afcac82d01a9defeb68209cd4f2c084632813692aa5e65ad', '0x1dbb0224910f42a14e7f1406b24c6fe8157296691b02a78756e01946038fffab']
|
||||
);
|
||||
|
||||
@@ -14,6 +14,15 @@ import { ExtError, assert, isFunction, isNull, logger } from '@plugnet/util';
|
||||
|
||||
const l = logger('rpc-core');
|
||||
|
||||
const EMPTY_META = {
|
||||
fallback: undefined,
|
||||
modifier: { isOptional: true },
|
||||
type: {
|
||||
asMap: { isLinked: false },
|
||||
isMap: false
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @name Rpc
|
||||
* @summary The API may use a HTTP or WebSockets provider.
|
||||
@@ -187,20 +196,14 @@ export default class Rpc implements RpcInterface {
|
||||
const base = createType(method.type as string, result);
|
||||
|
||||
if (method.type === 'StorageData') {
|
||||
// single return value (via state.getStorage), decode the value based on the
|
||||
// outputType that we have specified. Fallback to Data on nothing
|
||||
const key = params[0] as StorageKey;
|
||||
const type = key.outputType || 'Data';
|
||||
const Clazz = createClass(type);
|
||||
const meta = key.meta || { fallback: undefined, modifier: { isOptional: true } };
|
||||
|
||||
if (key.meta && key.meta.type.isMap && key.meta.type.asMap.isLinked) {
|
||||
// linked map
|
||||
return new Clazz(base);
|
||||
} else {
|
||||
return meta.modifier.isOptional
|
||||
? new Option(Clazz, isNull(result) ? null : new Clazz(base))
|
||||
: new Clazz(base);
|
||||
try {
|
||||
return this.formatStorageData(key, base, isNull(result));
|
||||
} catch (error) {
|
||||
console.error(`Unable to decode storage ${key.section}.${key.method}:`, error.message);
|
||||
|
||||
throw error;
|
||||
}
|
||||
} else if (method.type === 'StorageChangeSet') {
|
||||
// multiple return values (via state.storage subscription), decode the values
|
||||
@@ -208,43 +211,62 @@ export default class Rpc implements RpcInterface {
|
||||
// - Base - There is a valid value, non-empty
|
||||
// - null - The storage key is empty (but in the resultset)
|
||||
// - undefined - The storage value is not in the resultset
|
||||
return (params[0] as Vector<StorageKey>).reduce((result, key: StorageKey) => {
|
||||
// Fallback to Data (i.e. just the encoding) if we don't have a specific type
|
||||
const type = key.outputType || 'Data';
|
||||
const Clazz = createClass(type);
|
||||
return (params[0] as Vector<StorageKey>).reduce((results, key: StorageKey) => {
|
||||
try {
|
||||
results.push(this.formatStorageSet(key, base as StorageChangeSet));
|
||||
} catch (error) {
|
||||
console.error(`Unable to decode storage ${key.section}.${key.method}:`, error.message);
|
||||
|
||||
// see if we have a result value for this specific key
|
||||
const hexKey = key.toHex();
|
||||
const { value } = (base as StorageChangeSet).changes.find((item) =>
|
||||
item.key.toHex() === hexKey
|
||||
) || { value: null };
|
||||
const meta = key.meta || { fallback: undefined, modifier: { isOptional: true } };
|
||||
|
||||
if (!value) {
|
||||
// if we don't have a value, do not fill in the entry, it will be up to the
|
||||
// caller to sort this out, either ignoring or having a cache for older values
|
||||
result.push(undefined);
|
||||
} else {
|
||||
if (key.meta && key.meta.type.isMap && key.meta.type.asMap.isLinked) {
|
||||
// linked map
|
||||
result.push(new Clazz(value.unwrapOr(null)));
|
||||
} else {
|
||||
if (meta.modifier.isOptional) {
|
||||
// create option either with the existing value, or empty when
|
||||
// there is no value returned
|
||||
result.push(new Option(Clazz, value.isNone ? null : new Clazz(value.unwrap())));
|
||||
} else {
|
||||
// for `null` we fallback to the default value, or create an empty type,
|
||||
// otherwise we return the actual value as retrieved
|
||||
result.push(new Clazz(value.unwrapOr(meta.fallback)));
|
||||
}
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
return result;
|
||||
}, [] as Array<Codec | null | undefined>);
|
||||
return results;
|
||||
}, [] as Array<Codec | undefined>);
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
private formatStorageData (key: StorageKey, base: Codec, isNull: boolean): Codec {
|
||||
// single return value (via state.getStorage), decode the value based on the
|
||||
// outputType that we have specified. Fallback to Data on nothing
|
||||
const type = key.outputType || 'Data';
|
||||
const meta = key.meta || EMPTY_META;
|
||||
|
||||
if (meta.type.isMap && meta.type.asMap.isLinked) {
|
||||
return createType(type, base, true);
|
||||
} else if (meta.modifier.isOptional) {
|
||||
return new Option(
|
||||
createClass(type),
|
||||
isNull ? null : createType(type, base, true)
|
||||
);
|
||||
}
|
||||
|
||||
return createType(type, base, true);
|
||||
}
|
||||
|
||||
private formatStorageSet (key: StorageKey, base: StorageChangeSet): Codec | undefined {
|
||||
// Fallback to Data (i.e. just the encoding) if we don't have a specific type
|
||||
const type = key.outputType || 'Data';
|
||||
|
||||
// see if we have a result value for this specific key
|
||||
const hexKey = key.toHex();
|
||||
const { value } = base.changes.find((item) =>
|
||||
item.key.toHex() === hexKey
|
||||
) || { value: null };
|
||||
const meta = key.meta || EMPTY_META;
|
||||
|
||||
if (!value) {
|
||||
return;
|
||||
} else if (meta.type.isMap && meta.type.asMap.isLinked) {
|
||||
return createType(type, value.unwrapOr(null), true);
|
||||
} else if (meta.modifier.isOptional) {
|
||||
return new Option(
|
||||
createClass(type),
|
||||
value.isNone ? null : createType(type, value.unwrap(), true)
|
||||
);
|
||||
}
|
||||
|
||||
return createType(type, value.unwrapOr(meta.fallback), true);
|
||||
}
|
||||
}
|
||||
|
||||
+25
-18
@@ -2,43 +2,50 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import storage from '@plugnet/storage';
|
||||
import Ws from '@plugnet/rpc-provider/ws';
|
||||
import { BlockNumber, Index, SignedBlock, StorageChangeSet } from '@plugnet/types';
|
||||
import storage from '@plugnet/storage/static';
|
||||
import WsProvider from '@plugnet/rpc-provider/ws';
|
||||
|
||||
import Rpc from '../../src';
|
||||
|
||||
describe.skip('e2e krumme lanke', () => {
|
||||
let api;
|
||||
const randomAccount = '5HTqyWJHAVUieZnpb1V8gK4T1E4mnhkrUVSSzWBQd6kYgsVJ';
|
||||
|
||||
describe.skip('e2e Alexander - Polkadot', () => {
|
||||
let api: Rpc;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.setTimeout(30000);
|
||||
api = new Rpc(new Ws('ws://127.0.0.1:9944'));
|
||||
api = new Rpc(new WsProvider('wss://poc3-rpc.polkadot.io/'));
|
||||
});
|
||||
|
||||
it('subscribes to storage', (done) => {
|
||||
api.state
|
||||
return (
|
||||
api.state
|
||||
.subscribeStorage(
|
||||
[
|
||||
[storage.system.accountNonce, '5Ejbye9R8ygByQPrDSasaUid1munedPZUmg4f118HGmtodGp'],
|
||||
[storage.balances.freeBalance, '5DTestUPts3kjeXSTMyerHihn1uwMfLj8vU8sqF7qYrFacT7']
|
||||
[storage.system.accountNonce, randomAccount],
|
||||
[storage.session.currentIndex]
|
||||
],
|
||||
(data) => {
|
||||
(data: StorageChangeSet) => {
|
||||
expect(data).toHaveLength(2);
|
||||
expect(data[0].toNumber()).toEqual(0);
|
||||
expect(data[1].toNumber()).not.toEqual(0);
|
||||
expect(data).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.any(BlockNumber),
|
||||
expect.any(Index)
|
||||
])
|
||||
);
|
||||
|
||||
done();
|
||||
}
|
||||
)
|
||||
.then((subscriptionId) => {
|
||||
console.log('stoarge subscriptionId =', subscriptionId);
|
||||
});
|
||||
}).then((subscriptionId: number) => {
|
||||
console.log('stoarge subscriptionId =', subscriptionId);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('retrieves a block by hash (krumme lanke #1)', () => {
|
||||
return api.chain
|
||||
.getBlock('0x627847bffdf5f3e01ac440d057dec6a37a12a6f329db7ef8367665574b76b5df')
|
||||
.then((block) => {
|
||||
.then((block: SignedBlock) => {
|
||||
expect(block).toBeDefined();
|
||||
})
|
||||
.catch((error) => {
|
||||
@@ -51,7 +58,7 @@ describe.skip('e2e krumme lanke', () => {
|
||||
it('retrieves a block by hash (krumme lanke #2)', () => {
|
||||
return api.chain
|
||||
.getBlock('0x53416d53a4b1dfcae9165a89d193608e4aa770414f02267f5b2c4015a2e66091')
|
||||
.then((block) => {
|
||||
.then((block: SignedBlock) => {
|
||||
expect(block).toBeDefined();
|
||||
})
|
||||
.catch((error) => {
|
||||
+8
-7
@@ -2,23 +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 Ws from '@plugnet/rpc-provider/ws';
|
||||
import { ChainProperties, PendingExtrinsics } from '@plugnet/types';
|
||||
import WsProvider from '@plugnet/rpc-provider/ws';
|
||||
|
||||
import Rpc from '../../src';
|
||||
|
||||
describe.skip('e2e basics', () => {
|
||||
let api;
|
||||
let api: Rpc;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.setTimeout(30000);
|
||||
api = new Rpc(new Ws('ws://127.0.0.1:9944'));
|
||||
api = new Rpc(new WsProvider('ws://127.0.0.1:9944'));
|
||||
});
|
||||
|
||||
it('retrieves the pending extrinsics', () => {
|
||||
return api.author
|
||||
.pendingExtrinsics()
|
||||
.then((extrinsics) => {
|
||||
console.error('extrinsics', extrinsics);
|
||||
.then((extrinsics: PendingExtrinsics) => {
|
||||
console.log('extrinsics', extrinsics);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
@@ -30,8 +31,8 @@ describe.skip('e2e basics', () => {
|
||||
it('retrieves the system properties', () => {
|
||||
return api.system
|
||||
.properties()
|
||||
.then((properties) => {
|
||||
console.error('properties', properties);
|
||||
.then((properties: ChainProperties) => {
|
||||
console.log('properties', properties);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
@@ -2,31 +2,32 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import Ws from '@plugnet/rpc-provider/ws';
|
||||
import { Header, RuntimeVersion } from '@plugnet/types';
|
||||
import WsProvider from '@plugnet/rpc-provider/ws';
|
||||
|
||||
import Rpc from '../../src';
|
||||
|
||||
describe.skip('e2e chain', () => {
|
||||
let api;
|
||||
let api: Rpc;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.setTimeout(30000);
|
||||
api = new Rpc(new Ws('ws://127.0.0.1:9944'));
|
||||
api = new Rpc(new WsProvider('ws://127.0.0.1:9944'));
|
||||
});
|
||||
|
||||
it('subscribes via subscribeNewHead', (done) => {
|
||||
let count = 0;
|
||||
let count: number = 0;
|
||||
|
||||
// tslint:disable-next-line
|
||||
api.chain
|
||||
.subscribeNewHead((header) => {
|
||||
.subscribeNewHead((header: Header) => {
|
||||
expect(header).toBeDefined();
|
||||
|
||||
if (++count === 3) {
|
||||
done();
|
||||
}
|
||||
})
|
||||
.then((subscriptionId) => {
|
||||
.then((subscriptionId: number) => {
|
||||
console.log('newHead: subscriptionId =', subscriptionId);
|
||||
});
|
||||
});
|
||||
@@ -34,7 +35,7 @@ describe.skip('e2e chain', () => {
|
||||
it('retrieves the runtime version', () => {
|
||||
return api.chain
|
||||
.getRuntimeVersion()
|
||||
.then((version) => {
|
||||
.then((version: RuntimeVersion) => {
|
||||
console.error('version', version);
|
||||
})
|
||||
.catch((error) => {
|
||||
@@ -2,19 +2,20 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import Ws from '@plugnet/rpc-provider/ws';
|
||||
import { Balance, Bytes, Metadata, Moment } from '@plugnet/types';
|
||||
import storage from '@plugnet/storage/static';
|
||||
import WsProvider from '@plugnet/rpc-provider/ws';
|
||||
|
||||
import Rpc from '../../src';
|
||||
|
||||
const ALICE = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
|
||||
|
||||
describe.skip('e2e state', () => {
|
||||
let api;
|
||||
let api: Rpc;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.setTimeout(30000);
|
||||
api = new Rpc(new Ws('ws://127.0.0.1:9944'));
|
||||
api = new Rpc(new WsProvider('ws://127.0.0.1:9944'));
|
||||
});
|
||||
|
||||
it('retrieves code', () => {
|
||||
@@ -22,7 +23,7 @@ describe.skip('e2e state', () => {
|
||||
.getStorage([
|
||||
storage.substrate.code
|
||||
])
|
||||
.then((code) => {
|
||||
.then((code: Bytes) => {
|
||||
console.error(code.toHex().substr(0, 256), '...');
|
||||
})
|
||||
.catch((error) => {
|
||||
@@ -35,7 +36,7 @@ describe.skip('e2e state', () => {
|
||||
it('retrieves the wasm metadata', () => {
|
||||
return api.state
|
||||
.getMetadata()
|
||||
.then((meta) => {
|
||||
.then((meta: Metadata) => {
|
||||
console.error(JSON.stringify(meta.toJSON()));
|
||||
})
|
||||
.catch((error) => {
|
||||
@@ -50,7 +51,7 @@ describe.skip('e2e state', () => {
|
||||
.getStorage([
|
||||
storage.balances.freeBalance, ALICE
|
||||
])
|
||||
.then((balance) => {
|
||||
.then((balance: Balance) => {
|
||||
console.error(balance);
|
||||
|
||||
expect(balance.isZero()).not.toEqual(true);
|
||||
@@ -67,7 +68,7 @@ describe.skip('e2e state', () => {
|
||||
.getStorage([
|
||||
storage.timestamp.now
|
||||
])
|
||||
.then((moment) => {
|
||||
.then((moment: Moment) => {
|
||||
console.error(moment);
|
||||
|
||||
expect(moment.toNumber()).not.toEqual(0);
|
||||
+7
-6
@@ -2,31 +2,32 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import Ws from '@plugnet/rpc-provider/ws';
|
||||
import { StorageChangeSet } from '@plugnet/types';
|
||||
import WsProvider from '@plugnet/rpc-provider/ws';
|
||||
import storage from '@plugnet/storage/static';
|
||||
|
||||
import Rpc from '../../src';
|
||||
|
||||
describe.skip('e2e subscriptions', () => {
|
||||
let api;
|
||||
let api: Rpc;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.setTimeout(30000);
|
||||
api = new Rpc(new Ws('ws://127.0.0.1:9944'));
|
||||
api = new Rpc(new WsProvider('ws://127.0.0.1:9944'));
|
||||
});
|
||||
|
||||
it('retrieves current timestamp', (done) => {
|
||||
let count = 0;
|
||||
let count: number = 0;
|
||||
|
||||
return api.state
|
||||
.subscribeStorage([[storage.timestamp.now]], (data) => {
|
||||
.subscribeStorage([[storage.timestamp.now]], (data: StorageChangeSet) => {
|
||||
expect(data).toBeDefined();
|
||||
|
||||
if (++count === 3) {
|
||||
done();
|
||||
}
|
||||
})
|
||||
.then((subscriptionId) => {
|
||||
.then((subscriptionId: number) => {
|
||||
console.log('newHead: subscriptionId =', subscriptionId);
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@plugnet/rpc-provider",
|
||||
"version": "0.78.102",
|
||||
"version": "0.80.100",
|
||||
"description": "Transport providers for the API",
|
||||
"main": "index.js",
|
||||
"keywords": [
|
||||
@@ -13,9 +13,6 @@
|
||||
],
|
||||
"contributors": [],
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=6.4"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://registry.npmjs.org"
|
||||
@@ -29,17 +26,17 @@
|
||||
},
|
||||
"homepage": "https://github.com/plugblockchain/api.js/tree/master/packages/rpc-provider#readme",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.4.4",
|
||||
"@plugnet/keyring": "^0.90.100",
|
||||
"@plugnet/storage": "^0.78.102",
|
||||
"@plugnet/util": "^0.90.100",
|
||||
"@plugnet/util-crypto": "^0.90.100",
|
||||
"@types/nock": "^9.3.1",
|
||||
"@babel/runtime": "^7.4.5",
|
||||
"@plugnet/storage": "^0.80.100",
|
||||
"@plugnet/util": "^0.92.100",
|
||||
"@plugnet/util-crypto": "^0.92.100",
|
||||
"@types/nock": "^10.0.3",
|
||||
"eventemitter3": "^3.1.0",
|
||||
"isomorphic-fetch": "^2.2.1",
|
||||
"websocket": "^1.0.28"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@plugnet/keyring": "^0.92.100",
|
||||
"mock-socket": "^8.0.5",
|
||||
"nock": "^10.0.4"
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { JsonRpcResponse } from '../types';
|
||||
import Coder from '.';
|
||||
import Coder from './';
|
||||
|
||||
describe('decodeResponse', () => {
|
||||
let coder: Coder;
|
||||
|
||||
@@ -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 Coder from '.';
|
||||
import Coder from './';
|
||||
|
||||
describe('encodeJson', () => {
|
||||
let coder: Coder;
|
||||
|
||||
@@ -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 Coder from '.';
|
||||
import Coder from './';
|
||||
|
||||
describe('encodeObject', () => {
|
||||
let coder: Coder;
|
||||
|
||||
+4
-5
@@ -3,11 +3,10 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { TEST_HTTP_URL } from '../../test/mockHttp';
|
||||
|
||||
import Http from '.';
|
||||
import Http from './';
|
||||
|
||||
describe('Http', () => {
|
||||
let http;
|
||||
let http: Http;
|
||||
|
||||
beforeEach(() => {
|
||||
http = new Http(TEST_HTTP_URL);
|
||||
@@ -30,13 +29,13 @@ describe('Http', () => {
|
||||
});
|
||||
|
||||
it('does not (yet) support subscribe', () => {
|
||||
return http.subscribe().catch((error) => {
|
||||
return http.subscribe('', '', [], (cb) => { expect(cb).toEqual(expect.anything()); }).catch((error) => {
|
||||
expect(error.message).toMatch(/does not have subscriptions/);
|
||||
});
|
||||
});
|
||||
|
||||
it('does not (yet) support unsubscribe', () => {
|
||||
return http.unsubscribe().catch((error) => {
|
||||
return http.unsubscribe('', '', 0).catch((error) => {
|
||||
expect(error.message).toMatch(/does not have subscriptions/);
|
||||
});
|
||||
});
|
||||
+12
-5
@@ -2,12 +2,15 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { Global } from './../mock/types';
|
||||
|
||||
declare const global: Global;
|
||||
|
||||
describe('http/polyfill', () => {
|
||||
let origFetch;
|
||||
let origFetch: GlobalFetch;
|
||||
|
||||
beforeEach(() => {
|
||||
origFetch = global.fetch;
|
||||
global.fetch = null;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -15,12 +18,16 @@ describe('http/polyfill', () => {
|
||||
});
|
||||
|
||||
it('polyfills with no exceptions (without fetch)', () => {
|
||||
expect(require('./polyfill')).toBeDefined();
|
||||
(global as any).fetch = undefined;
|
||||
require('./polyfill');
|
||||
|
||||
expect(global.fetch).toBeDefined();
|
||||
});
|
||||
|
||||
it('polyfills with no exceptions (with fetch)', () => {
|
||||
global.fetch = () => true;
|
||||
(global as any).fetch = () => true;
|
||||
require('./polyfill');
|
||||
|
||||
expect(require('./polyfill')).toBeDefined();
|
||||
expect(global.fetch).toBeDefined();
|
||||
});
|
||||
});
|
||||
+5
-6
@@ -2,13 +2,13 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import Http from './';
|
||||
import { Mock } from './../mock/types';
|
||||
import { mockHttp, TEST_HTTP_URL } from '../../test/mockHttp';
|
||||
|
||||
import Http from '.';
|
||||
|
||||
describe('send', () => {
|
||||
let http;
|
||||
let mock;
|
||||
let http: Http;
|
||||
let mock: Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
http = new Http(TEST_HTTP_URL);
|
||||
@@ -17,7 +17,6 @@ describe('send', () => {
|
||||
afterEach(() => {
|
||||
if (mock) {
|
||||
mock.done();
|
||||
mock = null;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -32,7 +31,7 @@ describe('send', () => {
|
||||
return http
|
||||
.send('test_body', ['param'])
|
||||
.then((result) => {
|
||||
expect(mock.body['test_body']).toEqual({
|
||||
expect((mock.body as any)['test_body']).toEqual({
|
||||
id: 1,
|
||||
jsonrpc: '2.0',
|
||||
method: 'test_body',
|
||||
@@ -11,8 +11,8 @@ import interfaces from '@plugnet/jsonrpc';
|
||||
import testKeyring from '@plugnet/keyring/testing';
|
||||
import storage from '@plugnet/storage/static';
|
||||
import { Codec } from '@plugnet/types/types';
|
||||
import metadata from '@plugnet/types/Metadata/v0/static';
|
||||
import { Header } from '@plugnet/types';
|
||||
import rpcMetadataV4 from '@plugnet/types/Metadata/v4/static';
|
||||
import { Header, RuntimeVersion } from '@plugnet/types';
|
||||
import { bnToU8a, logger, u8aToHex } from '@plugnet/util';
|
||||
import { randomAsU8a } from '@plugnet/util-crypto';
|
||||
|
||||
@@ -43,14 +43,14 @@ export default class Mock implements ProviderInterface {
|
||||
public isUpdating: boolean = true;
|
||||
private requests: { [index: string]: (...params: any[]) => string } = {
|
||||
'chain_getBlockHash': (blockNumber: number): string => '0x1234',
|
||||
'chain_getRuntimeVersion': (): string => '0x1234',
|
||||
'chain_getRuntimeVersion': (): string => new RuntimeVersion().toHex(),
|
||||
'state_getStorage': (storage: MockState$Db, params: Array<any>): string => {
|
||||
return u8aToHex(
|
||||
storage[(params[0] as string)]
|
||||
);
|
||||
},
|
||||
'system_chain': (): string => 'mockChain',
|
||||
'state_getMetadata': (): string => metadata,
|
||||
'state_getMetadata': (): string => rpcMetadataV4,
|
||||
'system_name': (): string => 'mockClient',
|
||||
'system_version': (): string => '9.8.7'
|
||||
};
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
import { ProviderInterface$Emitted } from '../types';
|
||||
|
||||
import Mock from '.';
|
||||
import Mock from './';
|
||||
|
||||
describe('on', () => {
|
||||
let mock: Mock;
|
||||
|
||||
@@ -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 Mock from '.';
|
||||
import Mock from './';
|
||||
|
||||
describe('send', () => {
|
||||
let mock: Mock;
|
||||
|
||||
@@ -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 Mock from '.';
|
||||
import Mock from './';
|
||||
|
||||
describe('subscribe', () => {
|
||||
let mock: Mock;
|
||||
|
||||
@@ -2,6 +2,21 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { Server } from 'mock-socket';
|
||||
import { Constructor } from '@plugnet/types/types';
|
||||
|
||||
export interface Global extends NodeJS.Global {
|
||||
WebSocket: Constructor<WebSocket>;
|
||||
fetch: GlobalFetch;
|
||||
}
|
||||
|
||||
export type Mock = {
|
||||
body: Object,
|
||||
requests: Number,
|
||||
server: Server
|
||||
done: () => {}
|
||||
};
|
||||
|
||||
export type MockState$Subscription$Callback = (error: Error | null, value: any) => void;
|
||||
|
||||
export type MockState$Subscriptions = {
|
||||
|
||||
@@ -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 Mock from '.';
|
||||
import Mock from './';
|
||||
|
||||
describe('unsubscribe', () => {
|
||||
let mock: Mock;
|
||||
|
||||
@@ -36,7 +36,9 @@ interface WSProviderInterface extends ProviderInterface {
|
||||
}
|
||||
|
||||
const ALIASSES: { [index: string]: string } = {
|
||||
'chain_finalisedHead': 'chain_finalizedHead'
|
||||
'chain_finalisedHead': 'chain_finalizedHead',
|
||||
'chain_subscribeFinalisedHeads': 'chain_subscribeFinalizedHeads',
|
||||
'chain_unsubscribeFinalisedHeads': 'chain_unsubscribeFinalizedHeads'
|
||||
};
|
||||
|
||||
const l = logger('api-ws');
|
||||
@@ -174,7 +176,7 @@ export default class WsProvider implements WSProviderInterface {
|
||||
* @param params Encoded paramaters as appliucable for the method
|
||||
* @param subscription Subscription details (internally used)
|
||||
*/
|
||||
async send (method: string, params: Array<any>, subscription?: SubscriptionHandler): Promise<any> {
|
||||
send (method: string, params: Array<any>, subscription?: SubscriptionHandler): Promise<any> {
|
||||
return new Promise((resolve, reject): void => {
|
||||
try {
|
||||
const json = this.coder.encodeJson(method, params);
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
// Copyright 2017-2019 @polkadot/rpc-provider authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { mockWs, TEST_WS_URL } from '../../test/mockWs';
|
||||
|
||||
import Ws from '.';
|
||||
|
||||
describe('onConnect', () => {
|
||||
let mock;
|
||||
|
||||
beforeEach(() => {
|
||||
mock = mockWs([]);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
if (mock) {
|
||||
mock.done();
|
||||
mock = null;
|
||||
}
|
||||
});
|
||||
|
||||
it('sets up the on* handlers', () => {
|
||||
const ws = new Ws(TEST_WS_URL, false);
|
||||
|
||||
ws.connect();
|
||||
|
||||
expect(ws.websocket.onclose[0]).toBeDefined();
|
||||
expect(ws.websocket.onerror[0]).toBeDefined();
|
||||
expect(ws.websocket.onmessage[0]).toBeDefined();
|
||||
expect(ws.websocket.onopen[0]).toBeDefined();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,63 @@
|
||||
// Copyright 2017-2019 @polkadot/rpc-provider authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import WsProvider from './';
|
||||
import { Mock } from './../mock/types';
|
||||
import { mockWs, TEST_WS_URL } from '../../test/mockWs';
|
||||
|
||||
describe('onConnect', () => {
|
||||
let mock: Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
mock = mockWs([]);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
if (mock) {
|
||||
mock.done();
|
||||
}
|
||||
});
|
||||
|
||||
it('Does not connect when autoConnect is false', () => {
|
||||
const provider: WsProvider = new WsProvider(TEST_WS_URL, false);
|
||||
// We need to access the private WsProvider property 'websocket' here which would otherwise trigger a tslint error.
|
||||
// @ts-ignore
|
||||
expect(provider.websocket).toBeNull();
|
||||
});
|
||||
|
||||
it('Does connect when autoConnect is true', () => {
|
||||
const provider: WsProvider = new WsProvider(TEST_WS_URL, true);
|
||||
// @ts-ignore
|
||||
expect(provider.websocket).not.toBeNull();
|
||||
});
|
||||
|
||||
it('Creates a new WebSocket instance by calling the connect() method', () => {
|
||||
const provider: WsProvider = new WsProvider(TEST_WS_URL, false);
|
||||
// @ts-ignore
|
||||
expect(provider.websocket).toBeNull();
|
||||
|
||||
provider.connect();
|
||||
|
||||
// @ts-ignore
|
||||
expect(provider.websocket).not.toBeNull();
|
||||
// @ts-ignore
|
||||
expect(provider.websocket instanceof WebSocket).toBe(true);
|
||||
});
|
||||
|
||||
it('Creates the on handlers', () => {
|
||||
const provider: WsProvider = new WsProvider(TEST_WS_URL);
|
||||
|
||||
// @ts-ignore
|
||||
expect(provider.websocket).not.toBeNull();
|
||||
// @ts-ignore
|
||||
expect(provider.websocket).toEqual(expect.objectContaining({
|
||||
listeners: expect.objectContaining({
|
||||
close: [expect.any(Function)],
|
||||
error: [expect.any(Function)],
|
||||
message: [expect.any(Function)],
|
||||
open: [expect.any(Function)]
|
||||
})
|
||||
}));
|
||||
});
|
||||
});
|
||||
+7
-9
@@ -2,17 +2,16 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import WsProvider from './';
|
||||
import { Mock } from './../mock/types';
|
||||
import { mockWs, TEST_WS_URL } from '../../test/mockWs';
|
||||
|
||||
import Ws from '.';
|
||||
let ws: WsProvider;
|
||||
let mock: Mock;
|
||||
|
||||
let ws;
|
||||
let mock;
|
||||
|
||||
function createWs (requests, autoConnect) {
|
||||
function createWs (requests: Array<any>, autoConnect: boolean | undefined) {
|
||||
mock = mockWs(requests);
|
||||
ws = new Ws(TEST_WS_URL, autoConnect);
|
||||
|
||||
ws = new WsProvider(TEST_WS_URL, autoConnect);
|
||||
return ws;
|
||||
}
|
||||
|
||||
@@ -20,13 +19,12 @@ describe('Ws', () => {
|
||||
afterEach(() => {
|
||||
if (mock) {
|
||||
mock.done();
|
||||
mock = null;
|
||||
}
|
||||
});
|
||||
|
||||
it('returns the connected state', () => {
|
||||
expect(
|
||||
createWs([]).isConnected()
|
||||
createWs([],true).isConnected()
|
||||
).toEqual(false);
|
||||
});
|
||||
});
|
||||
@@ -1,24 +0,0 @@
|
||||
// Copyright 2017-2019 @polkadot/rpc-provider authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import Ws from '.';
|
||||
|
||||
describe('onMessageResult', () => {
|
||||
let ws;
|
||||
|
||||
beforeEach(() => {
|
||||
ws = new Ws('ws://127.0.0.1:1234', false);
|
||||
});
|
||||
|
||||
it('calls the handler when found', (done) => {
|
||||
ws.handlers[5] = {
|
||||
callback: (_, result) => {
|
||||
expect(result).toEqual('test');
|
||||
done();
|
||||
}
|
||||
};
|
||||
|
||||
ws.onSocketMessage({ data: '{"jsonrpc":"2.0","id":5,"result":"test"}' });
|
||||
});
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user