Introduce instances, apply to balance derives (multi-result) (#3094)

* Introducce instances, apply to balance derives (multi-result)

* Nonce extraction

* Optimize
This commit is contained in:
Jaco Greeff
2021-01-22 22:24:10 +01:00
committed by GitHub
parent c9deb0d88f
commit 1f55f38bd2
7 changed files with 80 additions and 39 deletions
+52 -25
View File
@@ -1,24 +1,24 @@
// Copyright 2017-2021 @polkadot/api-derive authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { ApiInterfaceRx } from '@polkadot/api/types';
import type { ApiInterfaceRx, QueryableStorageMultiArg } from '@polkadot/api/types';
import type { AccountData, AccountId, AccountIndex, AccountInfo, Address, Balance, Index } from '@polkadot/types/interfaces';
import type { ITuple } from '@polkadot/types/types';
import type { Observable } from '@polkadot/x-rxjs';
import type { DeriveBalancesAccount } from '../types';
import type { DeriveBalancesAccount, DeriveBalancesAccountData } from '../types';
import { isFunction } from '@polkadot/util';
import { assert, isFunction } from '@polkadot/util';
import { combineLatest, of } from '@polkadot/x-rxjs';
import { map, switchMap } from '@polkadot/x-rxjs/operators';
import { memo } from '../util';
type Result = [Balance, Balance, Balance, Balance, Index];
type BalanceResult = [Balance, Balance, Balance, Balance];
function calcBalances (api: ApiInterfaceRx, [accountId, [freeBalance, reservedBalance, frozenFee, frozenMisc, accountNonce]]: [AccountId, Result]): DeriveBalancesAccount {
type Result = [Index, BalanceResult[]];
function getBalance (api: ApiInterfaceRx, [freeBalance, reservedBalance, frozenFee, frozenMisc]: BalanceResult): DeriveBalancesAccountData {
return {
accountId,
accountNonce,
freeBalance,
frozenFee,
frozenMisc,
@@ -27,6 +27,19 @@ function calcBalances (api: ApiInterfaceRx, [accountId, [freeBalance, reservedBa
};
}
function calcBalances (api: ApiInterfaceRx, [accountId, [accountNonce, balances]]: [AccountId, Result]): DeriveBalancesAccount {
const primary = balances[0];
assert(primary, 'No balances retrieved for account');
return {
accountId,
accountNonce,
additional: balances.filter((_, index) => index !== 0).map((b) => getBalance(api, b)),
...getBalance(api, primary)
};
}
// old
function queryBalancesFree (api: ApiInterfaceRx, accountId: AccountId): Observable<Result> {
return api.queryMulti<[Balance, Balance, Index]>([
@@ -34,21 +47,25 @@ function queryBalancesFree (api: ApiInterfaceRx, accountId: AccountId): Observab
[api.query.balances.reservedBalance, accountId],
[api.query.system.accountNonce, accountId]
]).pipe(
map(([freeBalance, reservedBalance, accountNonce]): Result =>
[freeBalance, reservedBalance, api.registry.createType('Balance'), api.registry.createType('Balance'), accountNonce]
)
map(([freeBalance, reservedBalance, accountNonce]): Result => [
accountNonce,
[[freeBalance, reservedBalance, api.registry.createType('Balance'), api.registry.createType('Balance')]]
])
);
}
function queryBalancesAccount (api: ApiInterfaceRx, accountId: AccountId): Observable<Result> {
return api.queryMulti<[AccountData, Index]>([
[api.query.balances.account, accountId],
[api.query.system.accountNonce, accountId]
]).pipe(
map(([{ feeFrozen, free, miscFrozen, reserved }, accountNonce]): Result =>
[free, reserved, feeFrozen, miscFrozen, accountNonce]
function queryBalancesAccount (api: ApiInterfaceRx, accountId: AccountId, modules: string[] = ['balances']): Observable<Result> {
const balances = modules.map((m): QueryableStorageMultiArg<'rxjs'> => [api.query[m].account, accountId]);
const extract = (data: AccountData[]) =>
data.map(({ feeFrozen, free, miscFrozen, reserved }): BalanceResult => [free, reserved, feeFrozen, miscFrozen]);
return isFunction(api.query.system.account)
? api.queryMulti<[AccountInfo, ...(AccountData[])]>([[api.query.system.account, accountId], ...balances]).pipe(
map(([{ nonce }, ...balances]): Result => [nonce, extract(balances)])
)
);
: api.queryMulti<[Index, ...(AccountData[])]>([[api.query.system.accountNonce, accountId], ...balances]).pipe(
map(([nonce, ...balances]): Result => [nonce, extract(balances)])
);
}
function queryCurrent (api: ApiInterfaceRx, accountId: AccountId): Observable<Result> {
@@ -60,7 +77,7 @@ function queryCurrent (api: ApiInterfaceRx, accountId: AccountId): Observable<Re
: (infoOrTuple as [Index, AccountData])[1];
const accountNonce = (infoOrTuple as AccountInfo).nonce || (infoOrTuple as [Index, AccountData])[0];
return [free, reserved, feeFrozen, miscFrozen, accountNonce];
return [accountNonce, [[free, reserved, feeFrozen, miscFrozen]]];
})
);
}
@@ -81,19 +98,29 @@ function queryCurrent (api: ApiInterfaceRx, accountId: AccountId): Observable<Re
* ```
*/
export function account (instanceId: string, api: ApiInterfaceRx): (address: AccountIndex | AccountId | Address | string) => Observable<DeriveBalancesAccount> {
const balanceInstances = api.registry.getModuleInstances(api.runtimeVersion.specName.toString(), 'balances');
return memo(instanceId, (address: AccountIndex | AccountId | Address | string): Observable<DeriveBalancesAccount> =>
api.derive.accounts.accountId(address).pipe(
switchMap((accountId): Observable<[AccountId, Result]> =>
(accountId
? combineLatest([
of(accountId),
isFunction(api.query.system.account)
? queryCurrent(api, accountId)
: isFunction(api.query.balances.account)
? queryBalancesAccount(api, accountId)
: queryBalancesFree(api, accountId)
balanceInstances
? queryBalancesAccount(api, accountId, balanceInstances)
: isFunction(api.query.system.account)
? queryCurrent(api, accountId)
: isFunction(api.query.balances.account)
? queryBalancesAccount(api, accountId)
: queryBalancesFree(api, accountId)
])
: of([
api.registry.createType('AccountId'),
[
api.registry.createType('Index'),
[[api.registry.createType('Balance'), api.registry.createType('Balance'), api.registry.createType('Balance'), api.registry.createType('Balance')]]
]
])
: of([api.registry.createType('AccountId'), [api.registry.createType('Balance'), api.registry.createType('Balance'), api.registry.createType('Balance'), api.registry.createType('Balance'), api.registry.createType('Index')]])
)
),
map((result): DeriveBalancesAccount => calcBalances(api, result))
+10 -6
View File
@@ -9,7 +9,7 @@ import type { DeriveBalancesAccount, DeriveBalancesAll } from '../types';
import BN from 'bn.js';
import { bnMax, isFunction } from '@polkadot/util';
import { bnMax } from '@polkadot/util';
import { combineLatest, of } from '@polkadot/x-rxjs';
import { map, switchMap } from '@polkadot/x-rxjs/operators';
@@ -67,6 +67,7 @@ function calcBalances (api: ApiInterfaceRx, [{ accountId, accountNonce, freeBala
return {
accountId,
accountNonce,
additional: [],
availableBalance,
freeBalance,
frozenFee,
@@ -113,11 +114,14 @@ function queryCurrent (api: ApiInterfaceRx, accountId: AccountId): Observable<Re
[api.query.balances.locks, accountId],
[api.query.vesting.vesting, accountId]
])
: api.query.balances.locks(accountId).pipe(
map((locks): [Vec<BalanceLock>, Option<VestingInfo>] =>
[locks, api.registry.createType('Option<VestingInfo>')]
// TODO We need to check module instances here as well, not only the balances module
: api.query.balances?.locks
? api.query.balances.locks(accountId).pipe(
map((locks): [Vec<BalanceLock>, Option<VestingInfo>] =>
[locks, api.registry.createType('Option<VestingInfo>')]
)
)
)
: of([api.registry.createType('Vec<BalanceLock>'), api.registry.createType('Option<VestingInfo>')] as [Vec<BalanceLock>, Option<VestingInfo>])
).pipe(
map(([locks, optVesting]): ResultBalance =>
[optVesting.unwrapOr(null), locks]
@@ -148,7 +152,7 @@ export function all (instanceId: string, api: ApiInterfaceRx): (address: Account
? combineLatest([
of(account),
api.derive.chain.bestNumber(),
isFunction(api.query.system.account) || isFunction(api.query.balances.account)
api.query.system?.account || api.query.balances?.account
? queryCurrent(api, account.accountId)
: queryOld(api, account.accountId)
])
+7 -3
View File
@@ -13,9 +13,7 @@ export * from './parachains/types';
export * from './session/types';
export * from './staking/types';
export interface DeriveBalancesAccount {
accountId: AccountId;
accountNonce: Index;
export interface DeriveBalancesAccountData {
freeBalance: Balance;
frozenFee: Balance;
frozenMisc: Balance;
@@ -23,6 +21,12 @@ export interface DeriveBalancesAccount {
votingBalance: Balance;
}
export interface DeriveBalancesAccount extends DeriveBalancesAccountData {
accountId: AccountId;
accountNonce: Index;
additional: DeriveBalancesAccountData[];
}
export interface DeriveBalancesAll extends DeriveBalancesAccount {
isVesting: boolean;
lockedBalance: Balance;
+1 -1
View File
@@ -297,7 +297,7 @@ export abstract class Init<ApiType extends ApiTypes> extends Decorate<ApiType> {
this._rx.extrinsicType = this._extrinsicType;
this._rx.genesisHash = this._genesisHash;
this._rx.runtimeVersion = this._runtimeVersion;
this._rx.runtimeVersion = this._runtimeVersion as RuntimeVersion; // must be set here
// inject metadata and adjust the types as detected
this.injectMetadata(metadata, true);
+1 -1
View File
@@ -81,7 +81,7 @@ export interface ApiInterfaceRx {
hasSubscriptions: boolean;
registry: Registry;
runtimeMetadata: Metadata;
runtimeVersion?: RuntimeVersion;
runtimeVersion: RuntimeVersion;
query: QueryableStorage<'rxjs'>;
queryMulti: QueryableStorageMulti<'rxjs'>;
rpc: DecoratedRpc<'rxjs', RpcInterface>;
+6 -2
View File
@@ -268,8 +268,12 @@ export class TypeRegistry implements Registry {
: undefined;
}
public getDefinition (name: string): string | undefined {
return this.#definitions.get(name);
public getDefinition (typeName: string): string | undefined {
return this.#definitions.get(typeName);
}
public getModuleInstances (specName: string, moduleName: string): string[] | undefined {
return this.#knownTypes?.typesBundle?.spec?.[specName]?.instances?.[moduleName];
}
public getOrThrow <T extends Codec = Codec> (name: string, msg?: string): Constructor<T> {
+3 -1
View File
@@ -47,6 +47,7 @@ export type DeriveCustom = Record<string, Record<string, (instanceId: string, ap
export interface OverrideBundleDefinition {
alias?: Record<string, OverrideModuleType>;
derives?: DeriveCustom;
instances?: Record<string, string[]>;
rpc?: Record<string, Record<string, DefinitionRpc | DefinitionRpcSub>>;
types?: OverrideVersionedType[];
}
@@ -98,7 +99,8 @@ export interface Registry {
get <T extends Codec = Codec> (name: string, withUnknown?: boolean): Constructor<T> | undefined;
getChainProperties (): ChainProperties | undefined;
getClassName (clazz: Constructor): string | undefined;
getDefinition (name: string): string | undefined;
getDefinition (typeName: string): string | undefined;
getModuleInstances (specName: string, moduleName: string): string[] | undefined;
getOrThrow <T extends Codec = Codec> (name: string, msg?: string): Constructor<T>;
getOrUnknown <T extends Codec = Codec> (name: string): Constructor<T>;
setKnownTypes (types: RegisteredTypes): void;