@dedot/api 0.17.1-next.db98ff20.1 → 0.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -325,7 +325,7 @@ class BaseSubstrateClient extends index_js_2.JsonRpcClient {
325
325
  setSigner(signer) {
326
326
  this._options.signer = signer;
327
327
  }
328
- async queryMulti(queries, callback) {
328
+ async internalQueryMulti(queries, callback, blockHash) {
329
329
  // Extract keys from queries
330
330
  const keys = queries.map((q) => q.fn.rawKey(...(q.args || [])));
331
331
  const decodeValue = (query, rawValue) => {
@@ -341,10 +341,19 @@ class BaseSubstrateClient extends index_js_2.JsonRpcClient {
341
341
  });
342
342
  }
343
343
  else {
344
- const results = await this.getStorageQuery().query(keys);
344
+ const results = await this.getStorageQuery().query(keys, blockHash);
345
345
  return queries.map((q, i) => decodeValue(q.fn, results[keys[i]]));
346
346
  }
347
347
  }
348
+ async queryMulti(queries, callback) {
349
+ // If a callback is provided, set up a subscription
350
+ if (callback) {
351
+ return this.internalQueryMulti(queries, callback);
352
+ }
353
+ else {
354
+ return this.internalQueryMulti(queries);
355
+ }
356
+ }
348
357
  getStorageQuery() {
349
358
  throw new Error('Unimplemented!');
350
359
  }
@@ -186,24 +186,42 @@ class DedotClient// prettier-end-here
186
186
  const cached = this._apiAtCache.get(hash);
187
187
  if (cached)
188
188
  return cached;
189
- let targetVersion;
189
+ let parentVersion;
190
+ let parentHash;
190
191
  // Try to get block info from ChainHead first (for pinned blocks)
191
192
  const targetBlock = this.chainHead.findBlock(hash);
192
193
  if (targetBlock) {
193
- targetVersion = targetBlock.runtime;
194
- if (!targetVersion) {
195
- // fallback to fetching on-chain runtime if we can't find it in the block
196
- targetVersion = this.toSubstrateRuntimeVersion(await this.callAt(hash).core.version());
194
+ if (hash === this.genesisHash) {
195
+ parentHash = hash;
196
+ parentVersion = targetBlock.runtime;
197
+ }
198
+ else {
199
+ parentHash = targetBlock.parent;
200
+ const parentBlock = this.chainHead.findBlock(parentHash);
201
+ parentVersion = parentBlock?.runtime;
202
+ }
203
+ // fallback to fetching on-chain runtime if we can't find it in the block
204
+ if (!parentVersion) {
205
+ parentVersion = this.toSubstrateRuntimeVersion(await this.callAt(parentHash).core.version());
197
206
  }
198
207
  }
199
208
  else {
200
209
  // Block not pinned, try via Archive fallback if supported
201
210
  if (this._archive && (await this._archive.supported())) {
202
211
  try {
212
+ if (hash === this.genesisHash) {
213
+ parentHash = hash;
214
+ }
215
+ else {
216
+ const rawHeader = await this._archive.header(hash);
217
+ (0, utils_1.assert)(rawHeader, `Header for block ${hash} not found`);
218
+ const header = codecs_1.$Header.tryDecode(rawHeader);
219
+ parentHash = header.parentHash;
220
+ }
203
221
  // Fetch runtime version via Archive
204
- const runtimeRaw = await this._archive.call('Core_version', '0x', hash);
222
+ const runtimeRaw = await this._archive.call('Core_version', '0x', parentHash);
205
223
  (0, utils_1.assert)(runtimeRaw, 'Runtime Version Not Found');
206
- targetVersion = this.toSubstrateRuntimeVersion(codecs_1.$RuntimeVersion.tryDecode(runtimeRaw));
224
+ parentVersion = this.toSubstrateRuntimeVersion(codecs_1.$RuntimeVersion.tryDecode(runtimeRaw));
207
225
  }
208
226
  catch (error) {
209
227
  throw new utils_1.DedotError(`Unable to fetch runtime version for block ${hash}: ${error}`);
@@ -215,8 +233,8 @@ class DedotClient// prettier-end-here
215
233
  }
216
234
  let metadata = this.metadata;
217
235
  let registry = this.registry;
218
- if (targetVersion && targetVersion.specVersion !== this.runtimeVersion.specVersion) {
219
- metadata = await this.fetchMetadata(hash, targetVersion);
236
+ if (parentVersion && parentVersion.specVersion !== this.runtimeVersion.specVersion) {
237
+ metadata = await this.fetchMetadata(parentHash, parentVersion);
220
238
  registry = new codecs_1.PortableRegistry(metadata.latest, this.options.hasher);
221
239
  }
222
240
  const api = {
@@ -224,7 +242,7 @@ class DedotClient// prettier-end-here
224
242
  atBlockHash: hash,
225
243
  options: this.options,
226
244
  genesisHash: this.genesisHash,
227
- runtimeVersion: targetVersion,
245
+ runtimeVersion: parentVersion,
228
246
  metadata,
229
247
  registry,
230
248
  rpc: this.rpc,
@@ -235,6 +253,10 @@ class DedotClient// prettier-end-here
235
253
  api.query = (0, proxychain_js_1.newProxyChain)({ executor: new index_js_1.StorageQueryExecutorV2(api, this.chainHead) });
236
254
  api.call = (0, proxychain_js_1.newProxyChain)({ executor: new index_js_1.RuntimeApiExecutorV2(api, this.chainHead) });
237
255
  api.view = (0, proxychain_js_1.newProxyChain)({ executor: new index_js_1.ViewFunctionExecutorV2(api, this.chainHead) });
256
+ // @ts-ignore Add queryMulti implementation for at-block queries
257
+ api.queryMulti = (queries) => {
258
+ return this.internalQueryMulti(queries, undefined, hash);
259
+ };
238
260
  this._apiAtCache.set(hash, api);
239
261
  return api;
240
262
  }
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LegacyClient = void 0;
4
4
  const codecs_1 = require("@dedot/codecs");
5
+ const utils_1 = require("@dedot/utils");
5
6
  const index_js_1 = require("../executor/index.js");
6
7
  const proxychain_js_1 = require("../proxychain.js");
7
8
  const index_js_2 = require("../storage/index.js");
@@ -233,11 +234,12 @@ class LegacyClient// prettier-end-here
233
234
  const cached = this._apiAtCache.get(hash);
234
235
  if (cached)
235
236
  return cached;
236
- const targetVersion = await this.#getRuntimeVersion(hash);
237
+ const parentHash = await this.#findParentHash(hash);
238
+ const targetVersion = await this.#getRuntimeVersion(parentHash);
237
239
  let metadata = this.metadata;
238
240
  let registry = this.registry;
239
241
  if (targetVersion.specVersion !== this.runtimeVersion.specVersion) {
240
- metadata = await this.fetchMetadata(hash, targetVersion);
242
+ metadata = await this.fetchMetadata(parentHash, targetVersion);
241
243
  registry = new codecs_1.PortableRegistry(metadata.latest, this.options.hasher);
242
244
  }
243
245
  const api = {
@@ -255,11 +257,25 @@ class LegacyClient// prettier-end-here
255
257
  api.call = (0, proxychain_js_1.newProxyChain)({ executor: new index_js_1.RuntimeApiExecutor(api) });
256
258
  api.events = (0, proxychain_js_1.newProxyChain)({ executor: new index_js_1.EventExecutor(api) });
257
259
  api.errors = (0, proxychain_js_1.newProxyChain)({ executor: new index_js_1.ErrorExecutor(api) });
260
+ // @ts-ignore Add queryMulti implementation for at-block queries
261
+ api.queryMulti = (queries) => {
262
+ return this.internalQueryMulti(queries, undefined, hash);
263
+ };
258
264
  this._apiAtCache.set(hash, api);
259
265
  return api;
260
266
  }
261
267
  getStorageQuery() {
262
268
  return new index_js_2.LegacyStorageQuery(this);
263
269
  }
270
+ async #findParentHash(hash) {
271
+ if (hash === this.genesisHash) {
272
+ return this.genesisHash;
273
+ }
274
+ else {
275
+ const header = await this.rpc.chain_getHeader(hash);
276
+ (0, utils_1.assert)(header, `Header for ${hash} not found`);
277
+ return header.parentHash;
278
+ }
279
+ }
264
280
  }
265
281
  exports.LegacyClient = LegacyClient;
@@ -22,6 +22,15 @@ class SubmittableExtrinsicV2 extends BaseSubmittableExtrinsic_js_1.BaseSubmittab
22
22
  const txHash = this.hash;
23
23
  // validate the transaction
24
24
  // https://github.com/paritytech/json-rpc-interface-spec/issues/55#issuecomment-1609011150
25
+ // ensure at least 2 finalized blocks, this is to make sure the api.at can find the parent hash & block to fetch runtime
26
+ while (true) {
27
+ const block = await this.client.chainHead.finalizedBlock();
28
+ const parentBlock = this.client.chainHead.findBlock(block.parent);
29
+ if (parentBlock) {
30
+ break;
31
+ }
32
+ await (0, utils_1.waitFor)(100);
33
+ }
25
34
  const finalizedHash = await this.client.chainHead.finalizedHash();
26
35
  const validateTx = async (hash) => {
27
36
  const apiAt = await api.at(hash);
@@ -192,10 +192,19 @@ class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
192
192
  }
193
193
  case 'finalized': {
194
194
  const { finalizedBlockHashes, prunedBlockHashes } = result;
195
- this.#finalizedHash = finalizedBlockHashes.at(-1);
196
- const finalizedRuntime = this.#findRuntimeAt(this.#finalizedHash);
197
- if (finalizedRuntime) {
198
- this.#finalizedRuntime = finalizedRuntime;
195
+ const currentFinalizedNumber = this.findBlock(this.#finalizedHash).number;
196
+ for (const hash of finalizedBlockHashes) {
197
+ const block = this.findBlock(hash);
198
+ // Skip if block not found OR blocks that are already finalized (block number <= previous finalized number)
199
+ if (!block || block.number <= currentFinalizedNumber) {
200
+ continue;
201
+ }
202
+ this.#finalizedHash = hash;
203
+ const finalizedRuntime = this.#findRuntimeAt(hash);
204
+ if (finalizedRuntime) {
205
+ this.#finalizedRuntime = finalizedRuntime;
206
+ }
207
+ this.emit('finalizedBlock', block);
199
208
  }
200
209
  // push new finalized hashes into the queue, we'll adjust the size later if needed
201
210
  finalizedBlockHashes.forEach((hash) => {
@@ -204,7 +213,6 @@ class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
204
213
  this.#finalizedQueue.push(hash);
205
214
  });
206
215
  const currentFinalizedBlock = this.findBlock(this.#finalizedHash);
207
- this.emit('finalizedBlock', currentFinalizedBlock);
208
216
  // TODO account for operations that haven't received its operationId yet
209
217
  Object.values(this.#handlers).forEach(({ defer, hash, operationId }) => {
210
218
  if (prunedBlockHashes.includes(hash)) {
@@ -77,6 +77,10 @@ export declare abstract class BaseSubstrateClient<Rv extends RpcVersion, ChainAp
77
77
  get tx(): ChainApi[Rv]['tx'];
78
78
  at<ChainApiAt extends GenericSubstrateApi = ChainApi[Rv]>(hash: BlockHash): Promise<ISubstrateClientAt<ChainApiAt>>;
79
79
  setSigner(signer?: InjectedSigner): void;
80
+ protected internalQueryMulti(queries: {
81
+ fn: GenericStorageQuery;
82
+ args?: any[];
83
+ }[], callback?: Callback<any[]>, blockHash?: BlockHash): Promise<any[] | Unsub>;
80
84
  /**
81
85
  * Query multiple storage items in a single call
82
86
  *
@@ -321,7 +321,7 @@ export class BaseSubstrateClient extends JsonRpcClient {
321
321
  setSigner(signer) {
322
322
  this._options.signer = signer;
323
323
  }
324
- async queryMulti(queries, callback) {
324
+ async internalQueryMulti(queries, callback, blockHash) {
325
325
  // Extract keys from queries
326
326
  const keys = queries.map((q) => q.fn.rawKey(...(q.args || [])));
327
327
  const decodeValue = (query, rawValue) => {
@@ -337,10 +337,19 @@ export class BaseSubstrateClient extends JsonRpcClient {
337
337
  });
338
338
  }
339
339
  else {
340
- const results = await this.getStorageQuery().query(keys);
340
+ const results = await this.getStorageQuery().query(keys, blockHash);
341
341
  return queries.map((q, i) => decodeValue(q.fn, results[keys[i]]));
342
342
  }
343
343
  }
344
+ async queryMulti(queries, callback) {
345
+ // If a callback is provided, set up a subscription
346
+ if (callback) {
347
+ return this.internalQueryMulti(queries, callback);
348
+ }
349
+ else {
350
+ return this.internalQueryMulti(queries);
351
+ }
352
+ }
344
353
  getStorageQuery() {
345
354
  throw new Error('Unimplemented!');
346
355
  }
@@ -1,7 +1,7 @@
1
- import { $H256, $RuntimeVersion, PortableRegistry } from '@dedot/codecs';
1
+ import { $H256, $Header, $RuntimeVersion, PortableRegistry } from '@dedot/codecs';
2
2
  import { u32 } from '@dedot/shape';
3
- import { assert, concatU8a, noop, twox64Concat, u8aToHex, xxhashAsU8a, DedotError } from '@dedot/utils';
4
- import { ConstantExecutor, ErrorExecutor, EventExecutor, RuntimeApiExecutorV2, StorageQueryExecutorV2, ViewFunctionExecutorV2, TxExecutorV2, } from '../executor/index.js';
3
+ import { assert, concatU8a, DedotError, noop, twox64Concat, u8aToHex, xxhashAsU8a } from '@dedot/utils';
4
+ import { ConstantExecutor, ErrorExecutor, EventExecutor, RuntimeApiExecutorV2, StorageQueryExecutorV2, TxExecutorV2, ViewFunctionExecutorV2, } from '../executor/index.js';
5
5
  import { Archive, ChainHead, ChainSpec, Transaction, TransactionWatch } from '../json-rpc/index.js';
6
6
  import { newProxyChain } from '../proxychain.js';
7
7
  import { NewStorageQuery } from '../storage/index.js';
@@ -183,24 +183,42 @@ export class DedotClient// prettier-end-here
183
183
  const cached = this._apiAtCache.get(hash);
184
184
  if (cached)
185
185
  return cached;
186
- let targetVersion;
186
+ let parentVersion;
187
+ let parentHash;
187
188
  // Try to get block info from ChainHead first (for pinned blocks)
188
189
  const targetBlock = this.chainHead.findBlock(hash);
189
190
  if (targetBlock) {
190
- targetVersion = targetBlock.runtime;
191
- if (!targetVersion) {
192
- // fallback to fetching on-chain runtime if we can't find it in the block
193
- targetVersion = this.toSubstrateRuntimeVersion(await this.callAt(hash).core.version());
191
+ if (hash === this.genesisHash) {
192
+ parentHash = hash;
193
+ parentVersion = targetBlock.runtime;
194
+ }
195
+ else {
196
+ parentHash = targetBlock.parent;
197
+ const parentBlock = this.chainHead.findBlock(parentHash);
198
+ parentVersion = parentBlock?.runtime;
199
+ }
200
+ // fallback to fetching on-chain runtime if we can't find it in the block
201
+ if (!parentVersion) {
202
+ parentVersion = this.toSubstrateRuntimeVersion(await this.callAt(parentHash).core.version());
194
203
  }
195
204
  }
196
205
  else {
197
206
  // Block not pinned, try via Archive fallback if supported
198
207
  if (this._archive && (await this._archive.supported())) {
199
208
  try {
209
+ if (hash === this.genesisHash) {
210
+ parentHash = hash;
211
+ }
212
+ else {
213
+ const rawHeader = await this._archive.header(hash);
214
+ assert(rawHeader, `Header for block ${hash} not found`);
215
+ const header = $Header.tryDecode(rawHeader);
216
+ parentHash = header.parentHash;
217
+ }
200
218
  // Fetch runtime version via Archive
201
- const runtimeRaw = await this._archive.call('Core_version', '0x', hash);
219
+ const runtimeRaw = await this._archive.call('Core_version', '0x', parentHash);
202
220
  assert(runtimeRaw, 'Runtime Version Not Found');
203
- targetVersion = this.toSubstrateRuntimeVersion($RuntimeVersion.tryDecode(runtimeRaw));
221
+ parentVersion = this.toSubstrateRuntimeVersion($RuntimeVersion.tryDecode(runtimeRaw));
204
222
  }
205
223
  catch (error) {
206
224
  throw new DedotError(`Unable to fetch runtime version for block ${hash}: ${error}`);
@@ -212,8 +230,8 @@ export class DedotClient// prettier-end-here
212
230
  }
213
231
  let metadata = this.metadata;
214
232
  let registry = this.registry;
215
- if (targetVersion && targetVersion.specVersion !== this.runtimeVersion.specVersion) {
216
- metadata = await this.fetchMetadata(hash, targetVersion);
233
+ if (parentVersion && parentVersion.specVersion !== this.runtimeVersion.specVersion) {
234
+ metadata = await this.fetchMetadata(parentHash, parentVersion);
217
235
  registry = new PortableRegistry(metadata.latest, this.options.hasher);
218
236
  }
219
237
  const api = {
@@ -221,7 +239,7 @@ export class DedotClient// prettier-end-here
221
239
  atBlockHash: hash,
222
240
  options: this.options,
223
241
  genesisHash: this.genesisHash,
224
- runtimeVersion: targetVersion,
242
+ runtimeVersion: parentVersion,
225
243
  metadata,
226
244
  registry,
227
245
  rpc: this.rpc,
@@ -232,6 +250,10 @@ export class DedotClient// prettier-end-here
232
250
  api.query = newProxyChain({ executor: new StorageQueryExecutorV2(api, this.chainHead) });
233
251
  api.call = newProxyChain({ executor: new RuntimeApiExecutorV2(api, this.chainHead) });
234
252
  api.view = newProxyChain({ executor: new ViewFunctionExecutorV2(api, this.chainHead) });
253
+ // @ts-ignore Add queryMulti implementation for at-block queries
254
+ api.queryMulti = (queries) => {
255
+ return this.internalQueryMulti(queries, undefined, hash);
256
+ };
235
257
  this._apiAtCache.set(hash, api);
236
258
  return api;
237
259
  }
@@ -1,4 +1,5 @@
1
1
  import { PortableRegistry } from '@dedot/codecs';
2
+ import { assert } from '@dedot/utils';
2
3
  import { ConstantExecutor, ErrorExecutor, EventExecutor, RuntimeApiExecutor, StorageQueryExecutor, TxExecutor, ViewFunctionExecutor, } from '../executor/index.js';
3
4
  import { newProxyChain } from '../proxychain.js';
4
5
  import { LegacyStorageQuery } from '../storage/index.js';
@@ -230,11 +231,12 @@ export class LegacyClient// prettier-end-here
230
231
  const cached = this._apiAtCache.get(hash);
231
232
  if (cached)
232
233
  return cached;
233
- const targetVersion = await this.#getRuntimeVersion(hash);
234
+ const parentHash = await this.#findParentHash(hash);
235
+ const targetVersion = await this.#getRuntimeVersion(parentHash);
234
236
  let metadata = this.metadata;
235
237
  let registry = this.registry;
236
238
  if (targetVersion.specVersion !== this.runtimeVersion.specVersion) {
237
- metadata = await this.fetchMetadata(hash, targetVersion);
239
+ metadata = await this.fetchMetadata(parentHash, targetVersion);
238
240
  registry = new PortableRegistry(metadata.latest, this.options.hasher);
239
241
  }
240
242
  const api = {
@@ -252,10 +254,24 @@ export class LegacyClient// prettier-end-here
252
254
  api.call = newProxyChain({ executor: new RuntimeApiExecutor(api) });
253
255
  api.events = newProxyChain({ executor: new EventExecutor(api) });
254
256
  api.errors = newProxyChain({ executor: new ErrorExecutor(api) });
257
+ // @ts-ignore Add queryMulti implementation for at-block queries
258
+ api.queryMulti = (queries) => {
259
+ return this.internalQueryMulti(queries, undefined, hash);
260
+ };
255
261
  this._apiAtCache.set(hash, api);
256
262
  return api;
257
263
  }
258
264
  getStorageQuery() {
259
265
  return new LegacyStorageQuery(this);
260
266
  }
267
+ async #findParentHash(hash) {
268
+ if (hash === this.genesisHash) {
269
+ return this.genesisHash;
270
+ }
271
+ else {
272
+ const header = await this.rpc.chain_getHeader(hash);
273
+ assert(header, `Header for ${hash} not found`);
274
+ return header.parentHash;
275
+ }
276
+ }
261
277
  }
@@ -1,4 +1,4 @@
1
- import { AsyncQueue, noop } from '@dedot/utils';
1
+ import { AsyncQueue, noop, waitFor } from '@dedot/utils';
2
2
  import { BaseSubmittableExtrinsic } from './BaseSubmittableExtrinsic.js';
3
3
  import { SubmittableResult } from './SubmittableResult.js';
4
4
  import { InvalidTxError } from './errors.js';
@@ -19,6 +19,15 @@ export class SubmittableExtrinsicV2 extends BaseSubmittableExtrinsic {
19
19
  const txHash = this.hash;
20
20
  // validate the transaction
21
21
  // https://github.com/paritytech/json-rpc-interface-spec/issues/55#issuecomment-1609011150
22
+ // ensure at least 2 finalized blocks, this is to make sure the api.at can find the parent hash & block to fetch runtime
23
+ while (true) {
24
+ const block = await this.client.chainHead.finalizedBlock();
25
+ const parentBlock = this.client.chainHead.findBlock(block.parent);
26
+ if (parentBlock) {
27
+ break;
28
+ }
29
+ await waitFor(100);
30
+ }
22
31
  const finalizedHash = await this.client.chainHead.finalizedHash();
23
32
  const validateTx = async (hash) => {
24
33
  const apiAt = await api.at(hash);
@@ -189,10 +189,19 @@ export class ChainHead extends JsonRpcGroup {
189
189
  }
190
190
  case 'finalized': {
191
191
  const { finalizedBlockHashes, prunedBlockHashes } = result;
192
- this.#finalizedHash = finalizedBlockHashes.at(-1);
193
- const finalizedRuntime = this.#findRuntimeAt(this.#finalizedHash);
194
- if (finalizedRuntime) {
195
- this.#finalizedRuntime = finalizedRuntime;
192
+ const currentFinalizedNumber = this.findBlock(this.#finalizedHash).number;
193
+ for (const hash of finalizedBlockHashes) {
194
+ const block = this.findBlock(hash);
195
+ // Skip if block not found OR blocks that are already finalized (block number <= previous finalized number)
196
+ if (!block || block.number <= currentFinalizedNumber) {
197
+ continue;
198
+ }
199
+ this.#finalizedHash = hash;
200
+ const finalizedRuntime = this.#findRuntimeAt(hash);
201
+ if (finalizedRuntime) {
202
+ this.#finalizedRuntime = finalizedRuntime;
203
+ }
204
+ this.emit('finalizedBlock', block);
196
205
  }
197
206
  // push new finalized hashes into the queue, we'll adjust the size later if needed
198
207
  finalizedBlockHashes.forEach((hash) => {
@@ -201,7 +210,6 @@ export class ChainHead extends JsonRpcGroup {
201
210
  this.#finalizedQueue.push(hash);
202
211
  });
203
212
  const currentFinalizedBlock = this.findBlock(this.#finalizedHash);
204
- this.emit('finalizedBlock', currentFinalizedBlock);
205
213
  // TODO account for operations that haven't received its operationId yet
206
214
  Object.values(this.#handlers).forEach(({ defer, hash, operationId }) => {
207
215
  if (prunedBlockHashes.includes(hash)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dedot/api",
3
- "version": "0.17.1-next.db98ff20.1+db98ff20",
3
+ "version": "0.18.1",
4
4
  "description": "A delightful JavaScript/TypeScript client for Polkadot & Substrate",
5
5
  "author": "Thang X. Vu <thang@dedot.dev>",
6
6
  "homepage": "https://dedot.dev",
@@ -13,13 +13,13 @@
13
13
  "type": "module",
14
14
  "sideEffects": false,
15
15
  "dependencies": {
16
- "@dedot/codecs": "0.17.1-next.db98ff20.1+db98ff20",
17
- "@dedot/providers": "0.17.1-next.db98ff20.1+db98ff20",
18
- "@dedot/runtime-specs": "0.17.1-next.db98ff20.1+db98ff20",
19
- "@dedot/shape": "0.17.1-next.db98ff20.1+db98ff20",
20
- "@dedot/storage": "0.17.1-next.db98ff20.1+db98ff20",
21
- "@dedot/types": "0.17.1-next.db98ff20.1+db98ff20",
22
- "@dedot/utils": "0.17.1-next.db98ff20.1+db98ff20"
16
+ "@dedot/codecs": "0.18.1",
17
+ "@dedot/providers": "0.18.1",
18
+ "@dedot/runtime-specs": "0.18.1",
19
+ "@dedot/shape": "0.18.1",
20
+ "@dedot/storage": "0.18.1",
21
+ "@dedot/types": "0.18.1",
22
+ "@dedot/utils": "0.18.1"
23
23
  },
24
24
  "scripts": {
25
25
  "build": "tsc --project tsconfig.build.json && tsc --project tsconfig.build.cjs.json",
@@ -48,7 +48,7 @@
48
48
  "node": ">=18"
49
49
  },
50
50
  "license": "Apache-2.0",
51
- "gitHead": "db98ff20684d2a74b5e5402cabf43ef601d05d31",
51
+ "gitHead": "3ffda568232b50a3993d13c6ccac2894b0759dd7",
52
52
  "module": "./index.js",
53
53
  "types": "./index.d.ts"
54
54
  }
package/types.d.ts CHANGED
@@ -99,6 +99,28 @@ export interface IGenericSubstrateClient<ChainApi extends GenericSubstrateApi =
99
99
  events: ChainApi['events'];
100
100
  errors: ChainApi['errors'];
101
101
  view: ChainApi['view'];
102
+ /**
103
+ * Query multiple storage items in a single call
104
+ *
105
+ * This method allows you to query multiple storage items in a single call with type safety
106
+ * for both the query functions and their results.
107
+ *
108
+ * @example
109
+ * // One-time query with type-safe results
110
+ * const [balance, blockNumber] = await client.queryMulti([
111
+ * { fn: client.query.system.account, args: [ALICE] },
112
+ * { fn: client.query.system.number, args: [] }
113
+ * ]);
114
+ *
115
+ * @template Fns Array of storage query functions
116
+ * @param queries - Array of query specifications, each with a function and optional arguments
117
+ * @returns Array of decoded values with proper types
118
+ */
119
+ queryMulti<Fns extends GenericStorageQuery[]>(queries: {
120
+ [K in keyof Fns]: Query<Fns[K]>;
121
+ }): Promise<{
122
+ [K in keyof Fns]: QueryFnResult<Fns[K]>;
123
+ }>;
102
124
  }
103
125
  /**
104
126
  * A generic interface for Substrate clients at a specific block
@@ -128,10 +150,30 @@ export interface ISubstrateClient<ChainApi extends GenericSubstrateApi = Generic
128
150
  */
129
151
  setSigner(signer?: InjectedSigner): void;
130
152
  /**
131
- * Perform multiple storage queries in parallel
132
- * @param queries - An array of query objects
133
- * @param callback - Optional callback function to handle results
134
- * @returns A promise resolving to an array of results or an Unsub function
153
+ * Query multiple storage items in a single call or subscribe to multiple storage items
154
+ *
155
+ * This method allows you to query multiple storage items in a single call or set up a subscription
156
+ * to multiple storage items. It provides type safety for both the query functions and their results.
157
+ *
158
+ * @example
159
+ * // One-time query with type-safe results
160
+ * const [balance, blockNumber] = await client.queryMulti([
161
+ * { fn: client.query.system.account, args: [ALICE] },
162
+ * { fn: client.query.system.number, args: [] }
163
+ * ]);
164
+ *
165
+ * // Subscription with callback
166
+ * const unsub = await client.queryMulti([
167
+ * { fn: client.query.system.account, args: [ALICE] },
168
+ * { fn: client.query.system.number, args: [] }
169
+ * ], (results) => {
170
+ * console.log('Balance:', results[0], 'Block number:', results[1]);
171
+ * });
172
+ *
173
+ * @template Fns Array of storage query functions
174
+ * @param queries - Array of query specifications, each with a function and optional arguments
175
+ * @param callback - Optional callback for subscription-based queries
176
+ * @returns For one-time queries: Array of decoded values with proper types; For subscriptions: Unsubscribe function
135
177
  */
136
178
  queryMulti<Fns extends GenericStorageQuery[]>(queries: {
137
179
  [K in keyof Fns]: Query<Fns[K]>;