diff --git a/package.json b/package.json index abbc108c68..b60e4d12ce 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "@babel/register": "^7.12.1", "@babel/runtime": "^7.12.5", "@polkadot/dev": "^0.60.6", - "@polkadot/ts": "^0.3.55", + "@polkadot/ts": "^0.3.56", "@polkadot/typegen": "workspace:packages/typegen", "@types/jest": "^26.0.15", "copyfiles": "^2.4.0" diff --git a/packages/api-derive/src/accounts/identity.ts b/packages/api-derive/src/accounts/identity.ts index 8c0104f4dc..815261440a 100644 --- a/packages/api-derive/src/accounts/identity.ts +++ b/packages/api-derive/src/accounts/identity.ts @@ -79,15 +79,13 @@ function getParent (api: ApiInterfaceRx, identityOfOpt: Option | u return of([undefined, undefined]); } -export function _identityBase (instanceId: string, api: ApiInterfaceRx): (accountId?: AccountId | Uint8Array | string) => Observable<[Option | undefined, Option> | undefined]> { - return memo(instanceId, (accountId?: AccountId | Uint8Array | string): Observable<[Option | undefined, Option> | undefined]> => - accountId && api.query.identity?.identityOf - ? api.queryMulti<[Option, Option>]>([ - [api.query.identity.identityOf, accountId], - [api.query.identity.superOf, accountId] - ]) - : of([undefined, undefined]) - ); +function getBase (api: ApiInterfaceRx, accountId?: AccountId | Uint8Array | string): Observable<[Option | undefined, Option> | undefined]> { + return accountId && api.query.identity?.identityOf + ? api.queryMulti<[Option, Option>]>([ + [api.query.identity.identityOf, accountId], + [api.query.identity.superOf, accountId] + ]) + : of([undefined, undefined]); } /** @@ -96,7 +94,7 @@ export function _identityBase (instanceId: string, api: ApiInterfaceRx): (accoun */ export function identity (instanceId: string, api: ApiInterfaceRx): (accountId?: AccountId | Uint8Array | string) => Observable { return memo(instanceId, (accountId?: AccountId | Uint8Array | string): Observable => - api.derive.accounts._identityBase(accountId).pipe( + getBase(api, accountId).pipe( switchMap(([identityOfOpt, superOfOpt]) => getParent(api, identityOfOpt, superOfOpt)), map(([identityOfOpt, superOf]) => extractIdentity(identityOfOpt, superOf)) ) @@ -105,23 +103,31 @@ export function identity (instanceId: string, api: ApiInterfaceRx): (accountId?: export function hasIdentity (instanceId: string, api: ApiInterfaceRx): (accountId: AccountId | Uint8Array | string) => Observable { return memo(instanceId, (accountId: AccountId | Uint8Array | string): Observable => - api.derive.accounts._identityBase(accountId).pipe( - map(([identityOfOpt, superOfOpt]: [Option | undefined, Option> | undefined]): DeriveHasIdentity => { - const parentId = superOfOpt && superOfOpt.isSome - ? superOfOpt.unwrap()[0].toString() - : null; - const hasIdentity = !!parentId || !!(identityOfOpt && identityOfOpt.isSome); - - return { hasIdentity, parentId }; - }) + api.derive.accounts.hasIdentityMulti([accountId]).pipe( + map(([first]) => first) ) ); } export function hasIdentityMulti (instanceId: string, api: ApiInterfaceRx): (accountIds: (AccountId | Uint8Array | string)[]) => Observable { return memo(instanceId, (accountIds: (AccountId | Uint8Array | string)[]): Observable => - combineLatest( - accountIds.map((accountId) => api.derive.accounts.hasIdentity(accountId)) - ) + api.query.identity?.identityOf + ? combineLatest([ + api.query.identity.identityOf.multi>(accountIds), + api.query.identity.superOf.multi>>(accountIds) + ]).pipe( + map(([identities, supers]) => + identities.map((identityOfOpt, index): DeriveHasIdentity => { + const superOfOpt = supers[index]; + const parentId = superOfOpt && superOfOpt.isSome + ? superOfOpt.unwrap()[0].toString() + : null; + const hasIdentity = !!parentId || !!(identityOfOpt && identityOfOpt.isSome); + + return { hasIdentity, parentId }; + }) + ) + ) + : of(accountIds.map(() => ({ hasIdentity: false, parentId: null }))) ); } diff --git a/packages/api-derive/src/staking/account.ts b/packages/api-derive/src/staking/account.ts index cc535f6e3a..155cfec483 100644 --- a/packages/api-derive/src/staking/account.ts +++ b/packages/api-derive/src/staking/account.ts @@ -63,7 +63,7 @@ export function accounts (instanceId: string, api: ApiInterfaceRx): (accountIds: switchMap((sessionInfo) => combineLatest([ api.derive.staking.keysMulti(accountIds), - api.derive.staking.queryMulti(accountIds) + api.derive.staking.queryMulti(accountIds, true) ]).pipe( map(([keys, queries]) => queries.map((query, index) => parseResult(api, sessionInfo, keys[index], query))) ) diff --git a/packages/api-derive/src/staking/electedInfo.ts b/packages/api-derive/src/staking/electedInfo.ts index 81ec805b15..ff25ac83fd 100644 --- a/packages/api-derive/src/staking/electedInfo.ts +++ b/packages/api-derive/src/staking/electedInfo.ts @@ -18,7 +18,7 @@ export function electedInfo (instanceId: string, api: ApiInterfaceRx): () => Obs return memo(instanceId, (): Observable => api.derive.staking.validators().pipe( switchMap(({ nextElected, validators }): Observable => - api.derive.staking.queryMulti(combineAccounts(nextElected, validators)).pipe( + api.derive.staking.queryMulti(combineAccounts(nextElected, validators), false).pipe( map((info): DeriveStakingElected => ({ info, nextElected, diff --git a/packages/api-derive/src/staking/query.ts b/packages/api-derive/src/staking/query.ts index be49ca2925..f58e340716 100644 --- a/packages/api-derive/src/staking/query.ts +++ b/packages/api-derive/src/staking/query.ts @@ -14,14 +14,16 @@ import { isFunction } from '@polkadot/util'; import { memo } from '../util'; -type MultiResult = [Option, Option | Nominations>, RewardDestination, ITuple<[ValidatorPrefs]> | ValidatorPrefs, Exposure]; +type MultiResultPrev = [Option, Option>, RewardDestination, ITuple<[ValidatorPrefs]> | ValidatorPrefs, Exposure]; -function parseController (stashId: AccountId, [controllerIdOpt, nominatorsOpt, rewardDestination, validatorPrefs, exposure]: MultiResult, stakingLedgerOpt: Option): DeriveStakingQuery { - const nominators = nominatorsOpt.unwrapOr(null); +type MultiResultCombo = [Option, Option | Nominations>, RewardDestination, ITuple<[ValidatorPrefs]> | ValidatorPrefs, Exposure]; + +function parseDetails (stashId: AccountId, [controllerIdOpt, nominatorsOpt, rewardDestination, validatorPrefs, exposure]: MultiResultCombo, stakingLedgerOpt: Option): DeriveStakingQuery { + const nominators = nominatorsOpt && nominatorsOpt.unwrapOr(null); return { accountId: stashId, - controllerId: controllerIdOpt.unwrapOr(null), + controllerId: controllerIdOpt && controllerIdOpt.unwrapOr(null), exposure, nominators: nominators ? Array.isArray(nominators) @@ -37,8 +39,8 @@ function parseController (stashId: AccountId, [controllerIdOpt, nominatorsOpt, r }; } -function retrievePrev (api: ApiInterfaceRx, stashId: AccountId): Observable { - return api.queryMulti([ +function retrievePrev (api: ApiInterfaceRx, stashId: AccountId): Observable { + return api.queryMulti([ [api.query.staking.bonded, stashId], [api.query.staking.nominators, stashId], [api.query.staking.payee, stashId], @@ -47,18 +49,26 @@ function retrievePrev (api: ApiInterfaceRx, stashId: AccountId): Observable { +function retrieveCurr (api: ApiInterfaceRx, stashIds: AccountId[], activeEra: EraIndex, withFull: boolean): Observable { + const emptyCont = api.registry.createType('Option'); + const emptyNoms = api.registry.createType('Option'); + const emptyRewa = api.registry.createType('RewardDestination'); + return combineLatest([ - api.query.staking.bonded.multi>(stashIds), - api.query.staking.nominators + withFull + ? api.query.staking.bonded.multi>(stashIds) + : of(stashIds.map(() => emptyCont)), + withFull && api.query.staking.nominators ? api.query.staking.nominators.multi>(stashIds) - : of(stashIds.map(() => api.registry.createType('Option'))), - api.query.staking.payee.multi(stashIds), + : of(stashIds.map(() => emptyNoms)), + withFull + ? api.query.staking.payee.multi(stashIds) + : of(stashIds.map(() => emptyRewa)), api.query.staking.validators.multi(stashIds), api.query.staking.erasStakers.multi(stashIds.map((stashId) => [activeEra, stashId])) ]).pipe( - map(([controllerIdOpt, nominatorsOpt, rewardDestination, validatorPrefs, exposure]): MultiResult[] => - controllerIdOpt.map((controllerIdOpt, index): MultiResult => + map(([controllerIdOpt, nominatorsOpt, rewardDestination, validatorPrefs, exposure]): MultiResultCombo[] => + controllerIdOpt.map((controllerIdOpt, index): MultiResultCombo => [controllerIdOpt, nominatorsOpt[index], rewardDestination[index], validatorPrefs[index], exposure[index]] ) ) @@ -67,9 +77,10 @@ function retrieveCurr (api: ApiInterfaceRx, stashIds: AccountId[], activeEra: Er function retrieveControllers (api: ApiInterfaceRx, optControllerIds: Option[]): Observable[]> { const ids = optControllerIds.filter((opt) => opt.isSome).map((opt) => opt.unwrap()); + const emptyLed = api.registry.createType('Option'); if (!ids.length) { - return of(optControllerIds.map(() => api.registry.createType('Option'))); + return of(optControllerIds.map(() => emptyLed)); } return api.query.staking.ledger.multi>(ids).pipe( @@ -79,7 +90,7 @@ function retrieveControllers (api: ApiInterfaceRx, optControllerIds: Option => opt.isSome ? optLedgers[++offset] - : api.registry.createType('Option') + : emptyLed ); }) ); @@ -88,33 +99,38 @@ function retrieveControllers (api: ApiInterfaceRx, optControllerIds: Option Observable { - return memo(instanceId, (accountId: Uint8Array | string): Observable => - api.derive.staking.queryMulti([accountId]).pipe( +export function query (instanceId: string, api: ApiInterfaceRx): (accountId: Uint8Array | string, withFull?: boolean) => Observable { + return memo(instanceId, (accountId: Uint8Array | string, withFull?: boolean): Observable => + api.derive.staking.queryMulti([accountId], withFull).pipe( map(([first]) => first) ) ); } -export function queryMulti (instanceId: string, api: ApiInterfaceRx): (accountIds: (Uint8Array | string)[]) => Observable { - return memo(instanceId, (accountIds: (Uint8Array | string)[]): Observable => +export function queryMulti (instanceId: string, api: ApiInterfaceRx): (accountIds: (Uint8Array | string)[], withFull?: boolean) => Observable { + return memo(instanceId, (accountIds: (Uint8Array | string)[], withFull = true): Observable => accountIds.length ? api.derive.session.indexes().pipe( switchMap(({ activeEra }): Observable => { const stashIds = accountIds.map((accountId) => api.registry.createType('AccountId', accountId)); + const emptyLed = api.registry.createType('Option'); return ( isFunction(api.query.staking.erasStakers) - ? retrieveCurr(api, stashIds, activeEra) + ? retrieveCurr(api, stashIds, activeEra, withFull) : combineLatest(stashIds.map((stashId) => retrievePrev(api, stashId))) ).pipe( - switchMap((results): Observable => - retrieveControllers(api, results.map(([optController]) => optController)).pipe( - map((stakingLedgerOpts) => - stashIds.map((stashId, index) => parseController(stashId, results[index], stakingLedgerOpts[index])) + withFull + ? switchMap((results): Observable => + retrieveControllers(api, results.map(([optController]) => optController)).pipe( + map((stakingLedgerOpts) => + stashIds.map((stashId, index) => parseDetails(stashId, results[index], stakingLedgerOpts[index])) + ) ) ) - ) + : map((results): DeriveStakingQuery[] => + stashIds.map((stashId, index) => parseDetails(stashId, results[index], emptyLed)) + ) ); }) ) diff --git a/packages/api-derive/src/staking/stakerRewards.ts b/packages/api-derive/src/staking/stakerRewards.ts index bf90ca7537..873fb51be5 100644 --- a/packages/api-derive/src/staking/stakerRewards.ts +++ b/packages/api-derive/src/staking/stakerRewards.ts @@ -127,7 +127,7 @@ function filterRewards (api: ApiInterfaceRx, eras: EraIndex[], { migrateEra, rew return ( isFunction(api.tx.staking.payoutStakers) - ? api.derive.staking.queryMulti(validators) + ? api.derive.staking.queryMulti(validators, false) : of([]) ).pipe( map((queryValidators): DeriveStakerReward[] => @@ -150,7 +150,7 @@ function filterRewards (api: ApiInterfaceRx, eras: EraIndex[], { migrateEra, rew .filter(({ validators }) => Object.keys(validators).length !== 0) .map((reward) => ({ ...reward, - nominators: reward.nominating.filter((n) => !!reward.validators[n.validatorId]) + nominators: reward.nominating.filter((n) => reward.validators[n.validatorId]) })) ) ); diff --git a/packages/api-derive/src/staking/validatorsFrom.ts b/packages/api-derive/src/staking/validatorsFrom.ts index 51f21cb752..12d56f43fe 100644 --- a/packages/api-derive/src/staking/validatorsFrom.ts +++ b/packages/api-derive/src/staking/validatorsFrom.ts @@ -27,7 +27,8 @@ export function validatorsFrom (instanceId: string, api: ApiInterfaceRx): (stash return validatorIds; }, validatorIds); - }, []) + }, []), + false ) ) ) diff --git a/packages/api-derive/src/staking/waitingInfo.ts b/packages/api-derive/src/staking/waitingInfo.ts index 4a3b853d46..40d9bdf40a 100644 --- a/packages/api-derive/src/staking/waitingInfo.ts +++ b/packages/api-derive/src/staking/waitingInfo.ts @@ -20,7 +20,7 @@ export function waitingInfo (instanceId: string, api: ApiInterfaceRx): () => Obs const elected = nextElected.map((a) => a.toString()); const waiting = stashes.filter((v) => !elected.includes(v.toString())); - return api.derive.staking.queryMulti(waiting).pipe( + return api.derive.staking.queryMulti(waiting, false).pipe( map((info): DeriveStakingWaiting => ({ info, waiting diff --git a/packages/api/src/base/Decorate.ts b/packages/api/src/base/Decorate.ts index 90ad046320..c0d9c492cc 100644 --- a/packages/api/src/base/Decorate.ts +++ b/packages/api/src/base/Decorate.ts @@ -392,8 +392,7 @@ export abstract class Decorate extends Events { if (this.hasSubscriptions) { // When using double map storage function, user need to pass double map key as an array decorated.multi = decorateMethod((args: (Arg | Arg[])[]): Observable => - this._rpcCore.state.subscribeStorage( - args.map((arg: Arg[] | Arg): [StorageEntry, Arg | Arg[]] => [creator, arg]))); + this._retrieveMulti(args.map((arg) => [creator, arg]))); } /* eslint-enable @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-assignment */ @@ -430,6 +429,21 @@ export abstract class Decorate extends Events { )); } + // retrieve a set of values for a specific set of keys - here we chunk the keys into PAGE_SIZE_VALS sizes + private _retrieveMulti (keys: [StorageEntry, Arg | Arg[]][]): Observable { + return combineLatest( + ...Array(Math.ceil(keys.length / PAGE_SIZE_VALS)) + .fill(0) + .map((_, index): Observable => + this._rpcCore.state.subscribeStorage(keys.slice(index * PAGE_SIZE_VALS, (index * PAGE_SIZE_VALS) + PAGE_SIZE_VALS)) + ) + ).pipe( + map((valsArr): Codec[] => + valsArr.reduce((result: Codec[], vals) => result.concat(vals), []) + ) + ); + } + private _retrieveMapKeys ({ iterKey, meta, method, section }: StorageEntry, at: Hash | Uint8Array | string | null, arg?: Arg): Observable { assert(iterKey && (meta.type.isMap || meta.type.isDoubleMap), 'keys can only be retrieved on maps, linked maps and double maps'); diff --git a/yarn.lock b/yarn.lock index de80e224c2..e12f3a5ecc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1926,12 +1926,12 @@ __metadata: languageName: unknown linkType: soft -"@polkadot/ts@npm:^0.3.55": - version: 0.3.55 - resolution: "@polkadot/ts@npm:0.3.55" +"@polkadot/ts@npm:^0.3.56": + version: 0.3.56 + resolution: "@polkadot/ts@npm:0.3.56" dependencies: "@types/chrome": ^0.0.126 - checksum: 36083562f98cdd0651045472147ae23283633fe9f792a967e9b6d2c52fcbf8f66789219f0d325aca7b1372f7588e5b846becceb45a90d1ea2b00648456a8f32c + checksum: 0b2e94c65ff27e5300ad7162e8d73bb7c1b476c351a02b84045a2ce5e5895c46b1daa1c059f50fb7f4c49f05966b1c12385014366d36d9311fd2c8d014501cc9 languageName: node linkType: hard @@ -9260,7 +9260,7 @@ fsevents@~2.1.2: "@babel/register": ^7.12.1 "@babel/runtime": ^7.12.5 "@polkadot/dev": ^0.60.6 - "@polkadot/ts": ^0.3.55 + "@polkadot/ts": ^0.3.56 "@polkadot/typegen": "workspace:packages/typegen" "@types/jest": ^26.0.15 copyfiles: ^2.4.0