diff --git a/packages/api-derive/src/balances/all.ts b/packages/api-derive/src/balances/all.ts index 98d452396b..d4898c90de 100644 --- a/packages/api-derive/src/balances/all.ts +++ b/packages/api-derive/src/balances/all.ts @@ -15,9 +15,10 @@ import { drr } from '../util/drr'; type Result = [AccountId | undefined, BlockNumber | undefined, [Balance?, Balance?, Array?, Option?, Index?]]; const EMPTY_ACCOUNT = new AccountId(); +const ZERO = new Balance(0); -function calcBalances ([accountId = EMPTY_ACCOUNT, bestNumber = new BlockNumber(0), [freeBalance = new Balance(0), reservedBalance = new Balance(0), locks = [], vesting = new Option(VestingSchedule, null), accountNonce = new Index(0)]]: Result): DerivedBalances { - let lockedBalance = new Balance(0); +function calcBalances ([accountId = EMPTY_ACCOUNT, bestNumber = ZERO, [freeBalance = ZERO, reservedBalance = ZERO, locks = [], vesting = new Option(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 @@ -25,12 +26,22 @@ function calcBalances ([accountId = EMPTY_ACCOUNT, bestNumber = new BlockNumber( // 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 - : new Balance(0); + : ZERO; } + // offset = balance locked at genesis, perBlock is the unlock amount const { offset, perBlock } = vesting.unwrapOr(new VestingSchedule()); - const vestedBalance = new Balance(perBlock.mul(bestNumber).add(freeBalance.sub(offset))); - const availableBalance = new Balance(vestedBalance.sub(lockedBalance)); + 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, @@ -39,10 +50,8 @@ function calcBalances ([accountId = EMPTY_ACCOUNT, bestNumber = new BlockNumber( freeBalance, lockedBalance, reservedBalance, - vestedBalance: vestedBalance.lt(new Balance(0)) - ? new Balance(0) - : vestedBalance, - votingBalance: new Balance(freeBalance.add(reservedBalance)) + vestedBalance, + votingBalance: freeBalance.add(reservedBalance) }) as DerivedBalances; } diff --git a/packages/api-derive/src/types.ts b/packages/api-derive/src/types.ts index edc3dc99ac..f226f65089 100644 --- a/packages/api-derive/src/types.ts +++ b/packages/api-derive/src/types.ts @@ -8,12 +8,12 @@ import { AccountId, Balance, BlockNumber, Exposure, Index, StakingLedger, Struct export interface DerivedBalances extends StructAny { accountId: AccountId; accountNonce: Index; - freeBalance: Balance; - lockedBalance: Balance; - availableBalance: Balance; - reservedBalance: Balance; - votingBalance: Balance; - vestedBalance: Balance; + freeBalance: BN; + lockedBalance: BN; + availableBalance: BN; + reservedBalance: BN; + votingBalance: BN; + vestedBalance: BN; } export type DerivedBalancesMap = { diff --git a/packages/api-derive/test/e2e/promise.spec.ts b/packages/api-derive/test/e2e/promise.spec.ts index f4c290f8b0..5c8dc87d5e 100644 --- a/packages/api-derive/test/e2e/promise.spec.ts +++ b/packages/api-derive/test/e2e/promise.spec.ts @@ -10,8 +10,8 @@ import ApiPromise from '@polkadot/api/promise/Api'; import testKeyring from '@polkadot/keyring/testing'; import { WsProvider } from '@polkadot/rpc-provider'; -const WS = 'ws://127.0.0.1:9944/'; -// const WS = 'wss://poc3-rpc.polkadot.io/'; +// const WS = 'ws://127.0.0.1:9944/'; +const WS = 'wss://poc3-rpc.polkadot.io/'; describe.skip('derive e2e', () => { let api: ApiPromise; @@ -62,6 +62,16 @@ describe.skip('derive e2e', () => { }); }); + 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; @@ -144,5 +154,4 @@ describe.skip('derive e2e', () => { }); }); }); - });