Skip to content

Commit

Permalink
Rework api.derive.balance.{account, all} to work with the new frame a…
Browse files Browse the repository at this point in the history
…ccount data (#5955)

* Rework api.derive.balance.account to work with the new frame account data

* Ensure api.derive.balances.all correctly returns transferable

* Remove null from transferable

* set back to null
  • Loading branch information
TarikGul authored Aug 18, 2024
1 parent 1921d75 commit e2b4e0d
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 18 deletions.
71 changes: 53 additions & 18 deletions packages/api-derive/src/balances/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import type { Observable } from 'rxjs';
import type { QueryableStorageEntry } from '@polkadot/api-base/types';
import type { AccountData, AccountId, AccountIndex, AccountInfo, Address, Balance, Index } from '@polkadot/types/interfaces';
import type { FrameSystemAccountInfo, PalletBalancesAccountData } from '@polkadot/types/lookup';
import type { ITuple } from '@polkadot/types/types';
import type { DeriveApi, DeriveBalancesAccount, DeriveBalancesAccountData } from '../types.js';

Expand All @@ -15,7 +16,9 @@ import { memo } from '../util/index.js';

type BalanceResult = [Balance, Balance, Balance, Balance];

type Result = [Index, BalanceResult[]];
type Result = [Index, BalanceResult[], AccountType];

interface AccountType { isFrameAccountData: boolean }

type DeriveCustomAccount = DeriveApi['derive'] & Record<string, {
customAccount?: DeriveApi['query']['balances']['account']
Expand All @@ -25,24 +28,38 @@ function zeroBalance (api: DeriveApi) {
return api.registry.createType('Balance');
}

function getBalance (api: DeriveApi, [freeBalance, reservedBalance, frozenFee, frozenMisc]: BalanceResult): DeriveBalancesAccountData {
function getBalance (api: DeriveApi, [freeBalance, reservedBalance, frozenFeeOrFrozen, frozenMiscOrFlags]: BalanceResult, accType: AccountType): DeriveBalancesAccountData {
const votingBalance = api.registry.createType('Balance', freeBalance.toBn());

if (accType.isFrameAccountData) {
return {
frameSystemAccountInfo: {
flags: frozenMiscOrFlags,
frozen: frozenFeeOrFrozen
},
freeBalance,
frozenFee: api.registry.createType('Balance', 0),
frozenMisc: api.registry.createType('Balance', 0),
reservedBalance,
votingBalance
};
}

return {
freeBalance,
frozenFee,
frozenMisc,
frozenFee: frozenFeeOrFrozen,
frozenMisc: frozenMiscOrFlags,
reservedBalance,
votingBalance
};
}

function calcBalances (api: DeriveApi, [accountId, [accountNonce, [primary, ...additional]]]: [AccountId, Result]): DeriveBalancesAccount {
function calcBalances (api: DeriveApi, [accountId, [accountNonce, [primary, ...additional], accType]]: [AccountId, Result]): DeriveBalancesAccount {
return objectSpread({
accountId,
accountNonce,
additional: additional.map((b) => getBalance(api, b))
}, getBalance(api, primary));
additional: additional.map((b) => getBalance(api, b, accType))
}, getBalance(api, primary, accType));
}

// old
Expand All @@ -54,15 +71,17 @@ function queryBalancesFree (api: DeriveApi, accountId: AccountId): Observable<Re
]).pipe(
map(([freeBalance, reservedBalance, accountNonce]): Result => [
accountNonce,
[[freeBalance, reservedBalance, zeroBalance(api), zeroBalance(api)]]
[[freeBalance, reservedBalance, zeroBalance(api), zeroBalance(api)]],
{ isFrameAccountData: false }
])
);
}

function queryNonceOnly (api: DeriveApi, accountId: AccountId): Observable<Result> {
const fill = (nonce: Index): Result => [
nonce,
[[zeroBalance(api), zeroBalance(api), zeroBalance(api), zeroBalance(api)]]
[[zeroBalance(api), zeroBalance(api), zeroBalance(api), zeroBalance(api)]],
{ isFrameAccountData: false }
];

return isFunction(api.query.system.account)
Expand All @@ -83,7 +102,8 @@ function queryBalancesAccount (api: DeriveApi, accountId: AccountId, modules: st

const extract = (nonce: Index, data: AccountData[]): Result => [
nonce,
data.map(({ feeFrozen, free, miscFrozen, reserved }): BalanceResult => [free, reserved, feeFrozen, miscFrozen])
data.map(({ feeFrozen, free, miscFrozen, reserved }): BalanceResult => [free, reserved, feeFrozen, miscFrozen]),
{ isFrameAccountData: false }
];

// NOTE this is for the first case where we do have instances specified
Expand All @@ -106,7 +126,7 @@ function queryBalancesAccount (api: DeriveApi, accountId: AccountId, modules: st

function querySystemAccount (api: DeriveApi, accountId: AccountId): Observable<Result> {
// AccountInfo is current, support old, eg. Edgeware
return api.query.system.account<AccountInfo | ITuple<[Index, AccountData]>>(accountId).pipe(
return api.query.system.account<AccountInfo | FrameSystemAccountInfo | ITuple<[Index, AccountData]>>(accountId).pipe(
map((infoOrTuple): Result => {
const data = (infoOrTuple as AccountInfo).nonce
? (infoOrTuple as AccountInfo).data
Expand All @@ -117,16 +137,30 @@ function querySystemAccount (api: DeriveApi, accountId: AccountId): Observable<R
if (!data || data.isEmpty) {
return [
nonce,
[[zeroBalance(api), zeroBalance(api), zeroBalance(api), zeroBalance(api)]]
[[zeroBalance(api), zeroBalance(api), zeroBalance(api), zeroBalance(api)]],
{ isFrameAccountData: false }
];
}

const { feeFrozen, free, miscFrozen, reserved } = data;
const isFrameType = !!(infoOrTuple as FrameSystemAccountInfo).data.frozen;

if (isFrameType) {
const { flags, free, frozen, reserved } = (data as unknown as PalletBalancesAccountData);

return [
nonce,
[[free, reserved, feeFrozen, miscFrozen]]
];
return [
nonce,
[[free, reserved, frozen, flags]],
{ isFrameAccountData: true }
];
} else {
const { feeFrozen, free, miscFrozen, reserved } = data;

return [
nonce,
[[free, reserved, feeFrozen, miscFrozen]],
{ isFrameAccountData: false }
];
}
})
);
}
Expand Down Expand Up @@ -168,7 +202,8 @@ export function account (instanceId: string, api: DeriveApi): (address: AccountI
])
: of([api.registry.createType('AccountId'), [
api.registry.createType('Index'),
[[zeroBalance(api), zeroBalance(api), zeroBalance(api), zeroBalance(api)]]
[[zeroBalance(api), zeroBalance(api), zeroBalance(api), zeroBalance(api)]],
{ isFrameAccountData: false }
]])
)
),
Expand Down
8 changes: 8 additions & 0 deletions packages/api-derive/src/balances/all.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,19 @@ function calcLocked (api: DeriveApi, bestNumber: BlockNumber, locks: (PalletBala

function calcShared (api: DeriveApi, bestNumber: BlockNumber, data: DeriveBalancesAccountData, locks: (PalletBalancesBalanceLock | BalanceLockTo212)[]): DeriveBalancesAllAccountData {
const { allLocked, lockedBalance, lockedBreakdown, vestingLocked } = calcLocked(api, bestNumber, locks);
let transferable = null;

if (data.frameSystemAccountInfo?.frozen) {
const frozenReserveDiff = data.frameSystemAccountInfo.frozen.sub(data.reservedBalance);

transferable = api.registry.createType('Balance', allLocked ? 0 : data?.freeBalance.sub(bnMax(frozenReserveDiff, api.consts.balances.existentialDeposit)));
}

return objectSpread({}, data, {
availableBalance: api.registry.createType('Balance', allLocked ? 0 : bnMax(new BN(0), data?.freeBalance ? data.freeBalance.sub(lockedBalance) : new BN(0))),
lockedBalance,
lockedBreakdown,
transferable,
vestingLocked
});
}
Expand Down
28 changes: 28 additions & 0 deletions packages/api-derive/src/balances/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import type { PalletBalancesBalanceLock, PalletBalancesReserveData } from '@polk
import type { BN } from '@polkadot/util';

export interface DeriveBalancesAccountData {
frameSystemAccountInfo?: {
frozen: Balance;
flags: Balance;
}
freeBalance: Balance;
frozenFee: Balance;
frozenMisc: Balance;
Expand All @@ -20,9 +24,33 @@ export interface DeriveBalancesAccount extends DeriveBalancesAccountData {
}

export interface DeriveBalancesAllAccountData extends DeriveBalancesAccountData {
/**
* Calculated available balance. This uses the formula: max(0, free - locked)
* This is only correct when the return type of `api.query.system.account` is `AccountInfo` which was replaced by `FrameSystemAccountInfo`.
* See `transferable` for the correct balance calculation.
*
* ref: https://github.com/paritytech/substrate/pull/12951
*/
availableBalance: Balance;
/**
* The amount of balance locked away.
*/
lockedBalance: Balance;
/**
* The breakdown of locked balances.
*/
lockedBreakdown: (PalletBalancesBalanceLock | BalanceLockTo212)[];
/**
* Calculated transferable balance. This uses the formula: free - max(frozen - reserve, ed)
* This is only correct when the return type of `api.query.system.account` is `FrameSystemAccountInfo`.
* Which is the most up to date calulcation for transferrable balances.
*
* ref: https://github.com/paritytech/polkadot-sdk/issues/1833
*/
transferable: Balance | null;
/**
* Amount locked in vesting.
*/
vestingLocked: Balance;
}

Expand Down

0 comments on commit e2b4e0d

Please sign in to comment.