diff --git a/CHANGELOG.md b/CHANGELOG.md index 40594def1b..a8e0b52d89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # CHANGELOG +## 2.3.2-x + +Changes: + +- Add `.keysAt` & `.entriesAt` to query maps at a specific blockHash + ## 2.3.1 Oct 19, 2020 Upgrade priority: Low. Recommended for `api-contracts` developers and those using large messages via Node.js WS. diff --git a/package.json b/package.json index 88ceff93d5..148ecd0bf4 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "@babel/core": "^7.12.3", "@babel/register": "^7.12.1", "@babel/runtime": "^7.12.1", - "@polkadot/dev": "^0.58.3", + "@polkadot/dev": "^0.58.4", "@polkadot/ts": "^0.3.49", "@polkadot/typegen": "workspace:packages/typegen", "@types/jest": "^26.0.14", diff --git a/packages/api-contract/src/base/Contract.ts b/packages/api-contract/src/base/Contract.ts index 3fd8373d10..14400d07df 100644 --- a/packages/api-contract/src/base/Contract.ts +++ b/packages/api-contract/src/base/Contract.ts @@ -6,7 +6,7 @@ import { SubmittableExtrinsic } from '@polkadot/api/submittable/types'; import { AccountId, ContractExecResult } from '@polkadot/types/interfaces'; import { AnyJson, CodecArg } from '@polkadot/types/types'; import { AbiMessage, ContractCallOutcome } from '../types'; -import { ContractCallResult, ContractRead, MapMessageExec, MapMessageRead } from './types'; +import { ContractRead, MapMessageExec, MapMessageRead } from './types'; import BN from 'bn.js'; import { map } from 'rxjs/operators'; @@ -79,7 +79,7 @@ export default class Contract extends Base { return { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - send: this._decorateMethod((origin: string | AccountId | Uint8Array): ContractCallResult<'rpc'> => + send: this._decorateMethod((origin: string | AccountId | Uint8Array) => this.api.rx.rpc.contracts.call({ dest: this.address, gasLimit: this.#getGas(gasLimit), diff --git a/packages/api-contract/src/base/types.ts b/packages/api-contract/src/base/types.ts index 92dc7886cb..6fa30eba77 100644 --- a/packages/api-contract/src/base/types.ts +++ b/packages/api-contract/src/base/types.ts @@ -1,7 +1,6 @@ // Copyright 2017-2020 @polkadot/api-contract authors & contributors // SPDX-License-Identifier: Apache-2.0 -import { SubmittableResult } from '@polkadot/api'; import { ApiTypes, ObsInnerType } from '@polkadot/api/types'; import { SubmittableExtrinsic } from '@polkadot/api/submittable/types'; import { AccountId } from '@polkadot/types/interfaces'; @@ -11,20 +10,14 @@ import { ContractCallOutcome } from '../types'; import BN from 'bn.js'; import { Observable } from 'rxjs'; -type ContractCallTypes = 'tx' | 'rpc'; - -type ContractCallResultSubscription = ApiType extends 'rxjs' - ? Observable> - : Promise>>; +type ContractCallResult = ApiType extends 'rxjs' + ? Observable> + : Promise>>; export interface ContractRead { - send (account: string | AccountId | Uint8Array): ContractCallResultSubscription; + send (account: string | AccountId | Uint8Array): ContractCallResult; } -export type ContractCallResult = CallType extends 'rpc' - ? Observable - : Observable; - export interface MapConstructorExec { [message: string]: (endowment: BigInt | string | number | BN, gasLimit: BigInt | string | number | BN, ...params: CodecArg[]) => SubmittableExtrinsic; } @@ -34,5 +27,5 @@ export interface MapMessageExec { } export interface MapMessageRead { - [message: string]: (origin: AccountId | string | Uint8Array, value: BigInt | BN | string | number, gasLimit: BigInt | BN | string | number, ...params: CodecArg[]) => ContractCallResultSubscription; + [message: string]: (origin: AccountId | string | Uint8Array, value: BigInt | BN | string | number, gasLimit: BigInt | BN | string | number, ...params: CodecArg[]) => ContractCallResult; } diff --git a/packages/api/src/base/Decorate.ts b/packages/api/src/base/Decorate.ts index 0af853541a..fb2fa038be 100644 --- a/packages/api/src/base/Decorate.ts +++ b/packages/api/src/base/Decorate.ts @@ -359,7 +359,11 @@ export default abstract class Decorate extends Events if (creator.iterKey && (creator.meta.type.isMap || creator.meta.type.isDoubleMap)) { decorated.entries = decorateMethod( memo(this.#instanceId, (doubleMapArg?: Arg): Observable<[StorageKey, Codec][]> => - this._retrieveMapEntries(creator, doubleMapArg))); + this._retrieveMapEntries(creator, null, doubleMapArg))); + + decorated.entriesAt = decorateMethod( + memo(this.#instanceId, (hash: Hash | Uint8Array | string, doubleMapArg?: Arg): Observable<[StorageKey, Codec][]> => + this._retrieveMapEntries(creator, hash, doubleMapArg))); decorated.entriesPaged = decorateMethod( memo(this.#instanceId, (opts: PaginationOptions): Observable<[StorageKey, Codec][]> => @@ -367,7 +371,11 @@ export default abstract class Decorate extends Events decorated.keys = decorateMethod( memo(this.#instanceId, (doubleMapArg?: Arg): Observable => - this._retrieveMapKeys(creator, doubleMapArg))); + this._retrieveMapKeys(creator, null, doubleMapArg))); + + decorated.keysAt = decorateMethod( + memo(this.#instanceId, (hash: Hash | Uint8Array | string, doubleMapArg?: Arg): Observable => + this._retrieveMapKeys(creator, hash, doubleMapArg))); decorated.keysPaged = decorateMethod( memo(this.#instanceId, (opts: PaginationOptions): Observable => @@ -416,15 +424,18 @@ export default abstract class Decorate extends Events )); } - private _retrieveMapKeys ({ iterKey, meta }: StorageEntry, arg?: Arg): Observable { + private _retrieveMapKeys ({ iterKey, meta }: 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'); const headKey = iterKey(arg).toHex(); const startSubject = new BehaviorSubject(headKey); + const query = at + ? (startKey: string) => this._rpcCore.state.getKeysPaged(headKey, PAGE_SIZE_KEYS, startKey, at) + : (startKey: string) => this._rpcCore.state.getKeysPaged(headKey, PAGE_SIZE_KEYS, startKey); return startSubject.pipe( switchMap((startKey) => - this._rpcCore.state.getKeysPaged(headKey, PAGE_SIZE_KEYS, startKey).pipe( + query(startKey).pipe( map((keys) => keys.map((key) => key.setMeta(meta))) ) ), @@ -448,21 +459,23 @@ export default abstract class Decorate extends Events ); } - private _retrieveMapEntries (entry: StorageEntry, arg?: Arg): Observable<[StorageKey, Codec][]> { - return this._retrieveMapKeys(entry, arg).pipe( + private _retrieveMapEntries (entry: StorageEntry, at: Hash | Uint8Array | string | null, arg?: Arg): Observable<[StorageKey, Codec][]> { + const query = at + ? this._rpcCore.state.queryStorageAt + ? (keyset: StorageKey[]) => this._rpcCore.state.queryStorageAt(keyset, at) + : (keyset: StorageKey[]) => this._rpcCore.state.queryStorageAt(keyset) + // this is horrible, but need older support (which now doesn't work with at) + : (keyset: StorageKey[]) => this._rpcCore.state.subscribeStorage(keyset).pipe(take(1)); + + return this._retrieveMapKeys(entry, at, arg).pipe( switchMap((keys) => combineLatest<[StorageKey[], ...Codec[][]]>([ of(keys), ...Array(Math.ceil(keys.length / PAGE_SIZE_VALS)) .fill(0) - .map((_, index): Observable => { - const keyset = keys.slice(index * PAGE_SIZE_VALS, (index * PAGE_SIZE_VALS) + PAGE_SIZE_VALS); - - return this._rpcCore.state.queryStorageAt - ? this._rpcCore.state.queryStorageAt(keyset) - // this is horrible, but need older support - : this._rpcCore.state.subscribeStorage(keyset).pipe(take(1)); - }) + .map((_, index): Observable => + query(keys.slice(index * PAGE_SIZE_VALS, (index * PAGE_SIZE_VALS) + PAGE_SIZE_VALS)) + ) ]) ), map(([keys, ...valsArr]): [StorageKey, Codec][] => diff --git a/packages/api/src/types/storage.ts b/packages/api/src/types/storage.ts index 39025ad703..1f80f53e28 100644 --- a/packages/api/src/types/storage.ts +++ b/packages/api/src/types/storage.ts @@ -40,11 +40,13 @@ export interface StorageEntryBase>>(hash: Hash | Uint8Array | string, ...args: Parameters) => PromiseOrObs; creator: StorageEntry; entries: >>(arg?: Parameters[0]) => PromiseOrObs; + entriesAt: >>(hash: Hash | Uint8Array | string, arg?: Parameters[0]) => PromiseOrObs; entriesPaged: >>(opts: PaginationOptions[0]>) => PromiseOrObs; hash: (...args: Parameters) => PromiseOrObs; key: (...args: Parameters) => string; keyPrefix: () => string; keys: (arg?: any) => PromiseOrObs; + keysAt: (hash: Hash | Uint8Array | string, arg?: any) => PromiseOrObs; keysPaged: (opts: PaginationOptions[0]>) => PromiseOrObs; range: >>([from, to]: [Hash | Uint8Array | string, Hash | Uint8Array | string | undefined] | [Hash | Uint8Array | string], ...args: Parameters) => PromiseOrObs; size: (...args: Parameters) => PromiseOrObs; diff --git a/yarn.lock b/yarn.lock index 1072c3fa5e..3ab48b6488 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1776,9 +1776,9 @@ __metadata: languageName: unknown linkType: soft -"@polkadot/dev@npm:^0.58.3": - version: 0.58.3 - resolution: "@polkadot/dev@npm:0.58.3" +"@polkadot/dev@npm:^0.58.4": + version: 0.58.4 + resolution: "@polkadot/dev@npm:0.58.4" dependencies: "@babel/cli": ^7.12.1 "@babel/core": ^7.12.3 @@ -1866,7 +1866,7 @@ __metadata: polkadot-exec-typedoc: scripts/polkadot-exec-typedoc.js polkadot-exec-vuepress: scripts/polkadot-exec-vuepress.js polkadot-exec-webpack: scripts/polkadot-exec-webpack.js - checksum: babd9d98cc9c15fed28444d19042ff09246794389549461446cb44db3b452bb65741452befe9b3abf5714d11a89fb6ba5f94d17eda5b9159e33151bea28b41f3 + checksum: 7ac26d529399338efd23e7cc9c026b3682e6677ef5a6596782357dccea562f39532a416e04ad0a764a5bfa727283e6e323c7ea05d4ec0b3e6a7e275609ac6c27 languageName: node linkType: hard @@ -10366,7 +10366,7 @@ fsevents@^1.2.7: "@babel/core": ^7.12.3 "@babel/register": ^7.12.1 "@babel/runtime": ^7.12.1 - "@polkadot/dev": ^0.58.3 + "@polkadot/dev": ^0.58.4 "@polkadot/ts": ^0.3.49 "@polkadot/typegen": "workspace:packages/typegen" "@types/jest": ^26.0.14