diff --git a/packages/api-derive/src/staking/index.ts b/packages/api-derive/src/staking/index.ts index 591ca27977..1e3da9f76b 100644 --- a/packages/api-derive/src/staking/index.ts +++ b/packages/api-derive/src/staking/index.ts @@ -3,4 +3,5 @@ // of the Apache-2.0 license. See the LICENSE file for details. export * from './controllers'; +export * from './info'; export * from './intentionsBalances'; diff --git a/packages/api-derive/src/staking/info.ts b/packages/api-derive/src/staking/info.ts new file mode 100644 index 0000000000..926f8ed586 --- /dev/null +++ b/packages/api-derive/src/staking/info.ts @@ -0,0 +1,91 @@ +// 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 { DerivedStaking } from '../types'; + +import { Observable, of } from 'rxjs'; +import { map, switchMap } from 'rxjs/operators'; +import { ApiInterface$Rx } from '@polkadot/api/types'; +import { AccountId, Option, StakingLedger, ValidatorPrefs } from '@polkadot/types'; + +import { drr } from '../util/drr'; + +function withStashController (api: ApiInterface$Rx, accountId: AccountId, controllerId: AccountId): Observable { + const stashId = accountId; + + return ( + api.queryMulti([ + [api.query.staking.ledger, controllerId], + [api.query.staking.validators, stashId], + [api.query.session.nextKeyFor, controllerId] + ]) as any as Observable<[Option, [ValidatorPrefs], Option]> + ).pipe( + map(([stakingLedger, [validatorPrefs], nextKeyFor]) => ({ + accountId, + controllerId, + nextSessionId: nextKeyFor.isSome + ? nextKeyFor.unwrap() + : undefined, + stakingLedger: stakingLedger.isSome + ? stakingLedger.unwrap() + : undefined, + stashId, + validatorPrefs + })), + drr() + ); +} + +function withControllerLedger (api: ApiInterface$Rx, accountId: AccountId, stakingLedger: StakingLedger): Observable { + const controllerId = accountId; + const stashId = stakingLedger.stash; + + return ( + api.queryMulti([ + [api.query.staking.validators, stashId], + [api.query.session.nextKeyFor, controllerId] + ]) as any as Observable<[[ValidatorPrefs], Option]> + ).pipe( + map(([[validatorPrefs], nextKeyFor]) => ({ + accountId, + controllerId, + nextSessionId: nextKeyFor.isSome + ? nextKeyFor.unwrap() + : undefined, + stakingLedger, + stashId, + validatorPrefs + })), + 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 => { + 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, Option]> + ).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({ accountId }) + ) + ), + drr() + ); + }; +} diff --git a/packages/api-derive/src/types.ts b/packages/api-derive/src/types.ts index bf39dbfdac..daea44a0b8 100644 --- a/packages/api-derive/src/types.ts +++ b/packages/api-derive/src/types.ts @@ -3,7 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import BN from 'bn.js'; -import { AccountId, Balance, BlockNumber, Vote } from '@polkadot/types'; +import { AccountId, Balance, BlockNumber, StakingLedger, ValidatorPrefs, Vote } from '@polkadot/types'; export type DerivedBalances = { accountId: AccountId, @@ -15,24 +15,10 @@ export type DerivedBalances = { vestedBalance: Balance }; -export type DerivedFees = { - creationFee: BN, - existentialDeposit: BN, - transactionBaseFee: BN, - transactionByteFee: BN, - transferFee: BN -}; - export type DerivedBalancesMap = { [index: string]: DerivedBalances }; -export type DerivedReferendumVote = { - accountId: AccountId, - balance: Balance, - vote: Vote -}; - export type DerivedContractFees = { callBaseFee: BN, contractFee: BN, @@ -46,6 +32,20 @@ export type DerivedContractFees = { tombstoneDeposit: BN }; +export type DerivedFees = { + creationFee: BN, + existentialDeposit: BN, + transactionBaseFee: BN, + transactionByteFee: BN, + transferFee: BN +}; + +export type DerivedReferendumVote = { + accountId: AccountId, + balance: Balance, + vote: Vote +}; + export type DerivedSessionInfo = { currentIndex: BlockNumber, eraLength: BN, @@ -56,3 +56,12 @@ export type DerivedSessionInfo = { sessionsPerEra: BlockNumber, sessionProgress: BN }; + +export type DerivedStaking = { + accountId: AccountId, + controllerId?: AccountId, + nextSessionId?: AccountId, + stakingLedger?: StakingLedger, + stashId?: AccountId, + validatorPrefs?: ValidatorPrefs +}; diff --git a/packages/api-derive/test/e2e/promise.spec.ts b/packages/api-derive/test/e2e/promise.spec.ts index cb94644ba6..cac86b0ae5 100644 --- a/packages/api-derive/test/e2e/promise.spec.ts +++ b/packages/api-derive/test/e2e/promise.spec.ts @@ -48,6 +48,16 @@ describe.skip('derive e2e', () => { }); }); + 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 all session info', (done) => { let count = 0; @@ -61,13 +71,33 @@ describe.skip('derive e2e', () => { }); }); - it('retrieves the balances', (done) => { - return api.derive.balances.all('5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y', (balance) => { - console.error(JSON.stringify(balance)); + it('retrieves all staking info (for controller)', (done) => { + const accountId = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'; - if (balance.freeBalance.gtn(1)) { - done(); - } + 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) => { + console.error(JSON.stringify(info)); + + 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(); }); }); });