diff --git a/packages/api/src/augment/rpc.ts b/packages/api/src/augment/rpc.ts index 0b8337b482..5126b3935d 100644 --- a/packages/api/src/augment/rpc.ts +++ b/packages/api/src/augment/rpc.ts @@ -201,6 +201,10 @@ declare module '@polkadot/rpc-core/types.jsonrpc' { * Query historical storage entries (by key) starting from a start block **/ queryStorage: AugmentedRpc<(keys: Vec | (StorageKey | string | Uint8Array | any)[], fromBlock?: Hash | Uint8Array | string, toBlock?: Hash | Uint8Array | string) => Observable<[Hash, T][]>>; + /** + * Query storage entries (by key) starting at block hash given as the second parameter + **/ + queryStorageAt: AugmentedRpc<(keys: Vec | (StorageKey | string | Uint8Array | any)[], at?: Hash | Uint8Array | string) => Observable>; /** * Retrieves the runtime version via subscription **/ diff --git a/packages/rpc-core/src/index.ts b/packages/rpc-core/src/index.ts index 0911009392..995894cb81 100644 --- a/packages/rpc-core/src/index.ts +++ b/packages/rpc-core/src/index.ts @@ -188,7 +188,7 @@ export default class Rpc implements RpcInterface { map(([params, result]): any => isRaw ? this.registry.createType('Raw', result) - : this.formatOutput(def, params, result) + : this.formatOutput(method, def, params, result) ), catchError((error): any => { logErrorMessage(method, def, error); @@ -247,7 +247,7 @@ export default class Rpc implements RpcInterface { observer.next( isRaw ? this.registry.createType('Raw', result) - : this.formatOutput(def, params, result) + : this.formatOutput(method, def, params, result) ); } catch (error) { observer.error(error); @@ -317,7 +317,7 @@ export default class Rpc implements RpcInterface { return ['0x3a636f6465'].includes(key.toHex()); } - private formatOutput (rpc: DefinitionRpc, params: Codec[], result?: any): Codec | Codec[] { + private formatOutput (method: string, rpc: DefinitionRpc, params: Codec[], result?: any): Codec | Codec[] { if (rpc.type === 'StorageData') { const key = params[0] as StorageKey; @@ -331,10 +331,15 @@ export default class Rpc implements RpcInterface { } else if (rpc.type === 'StorageChangeSet') { return this.formatStorageSet(params[0] as Vec, result.changes); } else if (rpc.type === 'Vec') { - return result.map(({ block, changes }: { block: string; changes: [string, string | null][] }): [Hash, Codec[]] => [ + const mapped = result.map(({ block, changes }: { block: string; changes: [string, string | null][] }): [Hash, Codec[]] => [ this.registry.createType('Hash', block), this.formatStorageSet(params[0] as Vec, changes) ]); + + // we only query at a specific block, not a range - flatten + return method === 'queryStorageAt' + ? mapped[0][1] + : mapped; } return createTypeUnsafe(this.registry, rpc.type, [result]); diff --git a/packages/typegen/src/generate/rpc.ts b/packages/typegen/src/generate/rpc.ts index b70584c5f5..9c2ec6b19a 100644 --- a/packages/typegen/src/generate/rpc.ts +++ b/packages/typegen/src/generate/rpc.ts @@ -34,6 +34,8 @@ export default function generateRpcTypes (dest = 'packages/api/src/augment/rpc.t return createDocComments(6, [def.description]) + indent(6)(`getStorage: AugmentedRpc<(key: ${StorageKeyTye}, block?: Hash | Uint8Array | string) => Observable>;`); } else if (methodName === 'queryStorage') { return createDocComments(6, [def.description]) + indent(6)(`queryStorage: AugmentedRpc<(keys: Vec | (${StorageKeyTye})[], fromBlock?: Hash | Uint8Array | string, toBlock?: Hash | Uint8Array | string) => Observable<[Hash, T][]>>;`); + } else if (methodName === 'queryStorageAt') { + return createDocComments(6, [def.description]) + indent(6)(`queryStorageAt: AugmentedRpc<(keys: Vec | (${StorageKeyTye})[], at?: Hash | Uint8Array | string) => Observable>;`); } else if (methodName === 'subscribeStorage') { return createDocComments(6, [def.description]) + indent(6)(`subscribeStorage: AugmentedRpc<(keys: Vec | (${StorageKeyTye})[]) => Observable>;`); } diff --git a/packages/types/src/interfaces/state/definitions.ts b/packages/types/src/interfaces/state/definitions.ts index bb737ec10a..f6446c01e5 100644 --- a/packages/types/src/interfaces/state/definitions.ts +++ b/packages/types/src/interfaces/state/definitions.ts @@ -268,9 +268,24 @@ export default { type: 'Vec' }, { - name: 'startBlock', + name: 'fromBlock', type: 'Hash' }, + { + name: 'toBlock', + type: 'BlockHash', + isOptional: true + } + ], + type: 'Vec' + }, + queryStorageAt: { + description: 'Query storage entries (by key) starting at block hash given as the second parameter', + params: [ + { + name: 'keys', + type: 'Vec' + }, { name: 'at', type: 'BlockHash',