@dedot/api 0.9.5-next.11b0ca8e.3 → 0.9.5-next.4fd5def5.41

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.
@@ -26,8 +26,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
26
26
  exports.ExtraSignedExtension = void 0;
27
27
  const $ = __importStar(require("@dedot/shape"));
28
28
  const utils_1 = require("@dedot/utils");
29
- const SignedExtension_js_1 = require("./SignedExtension.js");
30
29
  const FallbackSignedExtension_js_1 = require("./FallbackSignedExtension.js");
30
+ const SignedExtension_js_1 = require("./SignedExtension.js");
31
31
  const index_js_1 = require("./known/index.js");
32
32
  class ExtraSignedExtension extends SignedExtension_js_1.SignedExtension {
33
33
  #signedExtensions;
@@ -83,13 +83,20 @@ class ExtraSignedExtension extends SignedExtension_js_1.SignedExtension {
83
83
  * @returns boolean
84
84
  */
85
85
  isRequireNoExternalInputs(extDef) {
86
- return ((0, FallbackSignedExtension_js_1.isEmptyStructOrTuple)(this.registry, extDef.typeId) &&
86
+ return ((0, FallbackSignedExtension_js_1.isEmptyStructOrTuple)(this.registry, extDef.typeId) && // prettier-end-here
87
87
  (0, FallbackSignedExtension_js_1.isEmptyStructOrTuple)(this.registry, extDef.additionalSigned));
88
88
  }
89
89
  toPayload(call = '0x') {
90
90
  const signedExtensions = this.#signedExtensions.map((se) => se.identifier);
91
91
  const { version } = this.registry.metadata.extrinsic;
92
- return Object.assign({ address: this.options.signerAddress, signedExtensions, version, method: call }, ...this.#signedExtensions.map((se) => se.toPayload()));
92
+ const { signerAddress } = this.options;
93
+ return Object.assign({
94
+ address: signerAddress,
95
+ signedExtensions,
96
+ version,
97
+ method: call,
98
+ withSignedTransaction: true, // allow signer/wallet to alter transaction by default
99
+ }, ...this.#signedExtensions.map((se) => se.toPayload()));
93
100
  }
94
101
  toRawPayload(call = '0x') {
95
102
  const payload = this.toPayload(call);
@@ -91,10 +91,6 @@ class BaseSubmittableExtrinsic extends codecs_1.Extrinsic {
91
91
  if (!alteredTx.signed) {
92
92
  throw new utils_1.DedotError('Altered transaction from signer is not signed');
93
93
  }
94
- // Signer's not allow the change the call data
95
- if (alteredTx.callHex !== this.callHex) {
96
- throw new utils_1.DedotError('Call data does not match, signer is not allowed to change tx call data.');
97
- }
98
94
  }
99
95
  #getSigner(options) {
100
96
  return options?.signer || this.client.options.signer;
@@ -22,6 +22,7 @@ class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
22
22
  #recovering;
23
23
  #blockUsage;
24
24
  #cache;
25
+ #operationQueue;
25
26
  constructor(client, options) {
26
27
  super(client, { prefix: 'chainHead', supportedVersions: ['unstable', 'v1'], ...options });
27
28
  this.#handlers = {};
@@ -32,6 +33,8 @@ class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
32
33
  this.#retryQueue = new utils_1.AsyncQueue();
33
34
  this.#blockUsage = new BlockUsage_js_1.BlockUsage();
34
35
  this.#cache = new Map();
36
+ // This helps us to not accidentally putting too much stress on the JSON-RPC server, especially smoldot/light-client
37
+ this.#operationQueue = new utils_1.ThrottleQueue(this.#__unsafe__isSmoldot() ? 25 : 250);
35
38
  }
36
39
  async runtimeVersion() {
37
40
  await this.#ensureFollowed();
@@ -236,7 +239,7 @@ class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
236
239
  .then(() => {
237
240
  // 3. Resolve the recovering promise
238
241
  // This means to continue all pending requests while the chainHead started recovering mode at step 1.
239
- this.#recovering.resolve();
242
+ this.#recovering?.resolve();
240
243
  // 4. Recover stale operations
241
244
  // 4.1. Operations that's going on & waiting to receiving its operationId
242
245
  // will eventually get an `limitedReached` error for using a stale followSubscriptionId
@@ -252,7 +255,7 @@ class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
252
255
  .catch((e) => {
253
256
  console.error(e);
254
257
  // TODO we should retry a few attempts
255
- this.#recovering.reject(new error_js_1.ChainHeadError('Cannot recover from stop event!'));
258
+ this.#recovering?.reject(new error_js_1.ChainHeadError('Cannot recover from stop event!'));
256
259
  Object.values(this.#handlers).forEach(({ defer, operationId }) => {
257
260
  defer.reject(new error_js_1.ChainHeadError('Cannot recover from stop event!'));
258
261
  delete this.#handlers[operationId];
@@ -400,6 +403,7 @@ class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
400
403
  this.#retryQueue.clear();
401
404
  this.#blockUsage.clear();
402
405
  this.#cache.clear();
406
+ this.#operationQueue.cancel();
403
407
  }
404
408
  async #ensureFollowed() {
405
409
  if (this.#recovering) {
@@ -485,7 +489,7 @@ class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
485
489
  const resp = await this.send('body', this.#subscriptionId, hash);
486
490
  return this.#awaitOperation(resp, hash);
487
491
  };
488
- const resp = await this.#performOperationWithRetry(operation, atHash);
492
+ const resp = await this.#operationQueue.add(() => this.#performOperationWithRetry(operation, atHash));
489
493
  this.#cache.set(cacheKey, resp);
490
494
  return resp;
491
495
  }
@@ -514,7 +518,7 @@ class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
514
518
  const resp = await this.send('call', this.#subscriptionId, hash, func, params);
515
519
  return this.#awaitOperation(resp, hash);
516
520
  };
517
- const resp = await this.#performOperationWithRetry(operation, atHash);
521
+ const resp = await this.#operationQueue.add(() => this.#performOperationWithRetry(operation, atHash));
518
522
  this.#cache.set(cacheKey, resp);
519
523
  return resp;
520
524
  }
@@ -557,9 +561,7 @@ class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
557
561
  }
558
562
  this.#blockUsage.use(hash);
559
563
  let results = [];
560
- // @ts-ignore a trick internally to check whether a provider is using smoldot connection
561
- const isSmoldot = typeof this.client.provider['chain'] === 'function';
562
- if (isSmoldot) {
564
+ if (this.#__unsafe__isSmoldot()) {
563
565
  const fetchItem = async (item) => {
564
566
  const [batch, newDiscardedItems] = await this.#getStorage([item], childTrie ?? null, hash);
565
567
  if (newDiscardedItems.length > 0) {
@@ -592,7 +594,7 @@ class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
592
594
  }
593
595
  async #getStorage(items, childTrie, at) {
594
596
  const operation = () => this.#getStorageOperation(items, childTrie, this.#ensurePinnedHash(at));
595
- return this.#performOperationWithRetry(operation, at);
597
+ return this.#operationQueue.add(() => this.#performOperationWithRetry(operation, at));
596
598
  }
597
599
  async #getStorageOperation(items, childTrie, at) {
598
600
  await this.#ensureFollowed();
@@ -629,5 +631,9 @@ class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
629
631
  return;
630
632
  await this.send('unpin', this.#subscriptionId, hashes);
631
633
  }
634
+ #__unsafe__isSmoldot() {
635
+ // @ts-ignore a trick internally to check whether a provider is using smoldot connection
636
+ return typeof this.client.provider['chain'] === 'function';
637
+ }
632
638
  }
633
639
  exports.ChainHead = ChainHead;
@@ -83,7 +83,13 @@ class NewStorageQuery extends BaseStorageQuery_js_1.BaseStorageQuery {
83
83
  await pull(best);
84
84
  // Subscribe to best block events
85
85
  const unsub = this.client.on('bestBlock', (block) => {
86
- pullQueue.enqueue(() => pull(block)).catch(console.error);
86
+ // Here we're handling each pull one by one,
87
+ // If the queue get too long, it might take a long time for us to get the fresh & latest data
88
+ // This is a precaution in such case, if the queue size >= 3 we skip all the pending pull and jump to the latest pull
89
+ if (pullQueue.size >= 3)
90
+ pullQueue.clear();
91
+ // TODO timing out for a pull to prevent it took too long to fetch
92
+ pullQueue.enqueue(() => pull(block)).catch(utils_1.noop);
87
93
  });
88
94
  return async () => {
89
95
  unsub();
@@ -63,8 +63,8 @@ export declare abstract class Executor<ChainApi extends GenericSubstrateApi = Ge
63
63
  } | {
64
64
  type: "BitSequence";
65
65
  value: {
66
- bitOrderType: number;
67
66
  bitStoreType: number;
67
+ bitOrderType: number;
68
68
  };
69
69
  };
70
70
  docs: string[];
@@ -1,7 +1,7 @@
1
1
  import * as $ from '@dedot/shape';
2
2
  import { ensurePresence, u8aToHex } from '@dedot/utils';
3
- import { SignedExtension } from './SignedExtension.js';
4
3
  import { FallbackSignedExtension, isEmptyStructOrTuple } from './FallbackSignedExtension.js';
4
+ import { SignedExtension } from './SignedExtension.js';
5
5
  import { knownSignedExtensions } from './known/index.js';
6
6
  export class ExtraSignedExtension extends SignedExtension {
7
7
  #signedExtensions;
@@ -57,13 +57,20 @@ export class ExtraSignedExtension extends SignedExtension {
57
57
  * @returns boolean
58
58
  */
59
59
  isRequireNoExternalInputs(extDef) {
60
- return (isEmptyStructOrTuple(this.registry, extDef.typeId) &&
60
+ return (isEmptyStructOrTuple(this.registry, extDef.typeId) && // prettier-end-here
61
61
  isEmptyStructOrTuple(this.registry, extDef.additionalSigned));
62
62
  }
63
63
  toPayload(call = '0x') {
64
64
  const signedExtensions = this.#signedExtensions.map((se) => se.identifier);
65
65
  const { version } = this.registry.metadata.extrinsic;
66
- return Object.assign({ address: this.options.signerAddress, signedExtensions, version, method: call }, ...this.#signedExtensions.map((se) => se.toPayload()));
66
+ const { signerAddress } = this.options;
67
+ return Object.assign({
68
+ address: signerAddress,
69
+ signedExtensions,
70
+ version,
71
+ method: call,
72
+ withSignedTransaction: true, // allow signer/wallet to alter transaction by default
73
+ }, ...this.#signedExtensions.map((se) => se.toPayload()));
67
74
  }
68
75
  toRawPayload(call = '0x') {
69
76
  const payload = this.toPayload(call);
@@ -88,10 +88,6 @@ export class BaseSubmittableExtrinsic extends Extrinsic {
88
88
  if (!alteredTx.signed) {
89
89
  throw new DedotError('Altered transaction from signer is not signed');
90
90
  }
91
- // Signer's not allow the change the call data
92
- if (alteredTx.callHex !== this.callHex) {
93
- throw new DedotError('Call data does not match, signer is not allowed to change tx call data.');
94
- }
95
91
  }
96
92
  #getSigner(options) {
97
93
  return options?.signer || this.client.options.signer;
@@ -1,5 +1,5 @@
1
1
  import { $Header } from '@dedot/codecs';
2
- import { assert, AsyncQueue, deferred, ensurePresence, noop, waitFor } from '@dedot/utils';
2
+ import { assert, AsyncQueue, deferred, ensurePresence, noop, ThrottleQueue, waitFor, } from '@dedot/utils';
3
3
  import { JsonRpcGroup } from '../JsonRpcGroup.js';
4
4
  import { BlockUsage } from './BlockUsage.js';
5
5
  import { ChainHeadBlockNotPinnedError, ChainHeadBlockPrunedError, ChainHeadError, ChainHeadLimitReachedError, ChainHeadOperationError, ChainHeadOperationInaccessibleError, ChainHeadStopError, RetryStrategy, } from './error.js';
@@ -19,6 +19,7 @@ export class ChainHead extends JsonRpcGroup {
19
19
  #recovering;
20
20
  #blockUsage;
21
21
  #cache;
22
+ #operationQueue;
22
23
  constructor(client, options) {
23
24
  super(client, { prefix: 'chainHead', supportedVersions: ['unstable', 'v1'], ...options });
24
25
  this.#handlers = {};
@@ -29,6 +30,8 @@ export class ChainHead extends JsonRpcGroup {
29
30
  this.#retryQueue = new AsyncQueue();
30
31
  this.#blockUsage = new BlockUsage();
31
32
  this.#cache = new Map();
33
+ // This helps us to not accidentally putting too much stress on the JSON-RPC server, especially smoldot/light-client
34
+ this.#operationQueue = new ThrottleQueue(this.#__unsafe__isSmoldot() ? 25 : 250);
32
35
  }
33
36
  async runtimeVersion() {
34
37
  await this.#ensureFollowed();
@@ -233,7 +236,7 @@ export class ChainHead extends JsonRpcGroup {
233
236
  .then(() => {
234
237
  // 3. Resolve the recovering promise
235
238
  // This means to continue all pending requests while the chainHead started recovering mode at step 1.
236
- this.#recovering.resolve();
239
+ this.#recovering?.resolve();
237
240
  // 4. Recover stale operations
238
241
  // 4.1. Operations that's going on & waiting to receiving its operationId
239
242
  // will eventually get an `limitedReached` error for using a stale followSubscriptionId
@@ -249,7 +252,7 @@ export class ChainHead extends JsonRpcGroup {
249
252
  .catch((e) => {
250
253
  console.error(e);
251
254
  // TODO we should retry a few attempts
252
- this.#recovering.reject(new ChainHeadError('Cannot recover from stop event!'));
255
+ this.#recovering?.reject(new ChainHeadError('Cannot recover from stop event!'));
253
256
  Object.values(this.#handlers).forEach(({ defer, operationId }) => {
254
257
  defer.reject(new ChainHeadError('Cannot recover from stop event!'));
255
258
  delete this.#handlers[operationId];
@@ -397,6 +400,7 @@ export class ChainHead extends JsonRpcGroup {
397
400
  this.#retryQueue.clear();
398
401
  this.#blockUsage.clear();
399
402
  this.#cache.clear();
403
+ this.#operationQueue.cancel();
400
404
  }
401
405
  async #ensureFollowed() {
402
406
  if (this.#recovering) {
@@ -482,7 +486,7 @@ export class ChainHead extends JsonRpcGroup {
482
486
  const resp = await this.send('body', this.#subscriptionId, hash);
483
487
  return this.#awaitOperation(resp, hash);
484
488
  };
485
- const resp = await this.#performOperationWithRetry(operation, atHash);
489
+ const resp = await this.#operationQueue.add(() => this.#performOperationWithRetry(operation, atHash));
486
490
  this.#cache.set(cacheKey, resp);
487
491
  return resp;
488
492
  }
@@ -511,7 +515,7 @@ export class ChainHead extends JsonRpcGroup {
511
515
  const resp = await this.send('call', this.#subscriptionId, hash, func, params);
512
516
  return this.#awaitOperation(resp, hash);
513
517
  };
514
- const resp = await this.#performOperationWithRetry(operation, atHash);
518
+ const resp = await this.#operationQueue.add(() => this.#performOperationWithRetry(operation, atHash));
515
519
  this.#cache.set(cacheKey, resp);
516
520
  return resp;
517
521
  }
@@ -554,9 +558,7 @@ export class ChainHead extends JsonRpcGroup {
554
558
  }
555
559
  this.#blockUsage.use(hash);
556
560
  let results = [];
557
- // @ts-ignore a trick internally to check whether a provider is using smoldot connection
558
- const isSmoldot = typeof this.client.provider['chain'] === 'function';
559
- if (isSmoldot) {
561
+ if (this.#__unsafe__isSmoldot()) {
560
562
  const fetchItem = async (item) => {
561
563
  const [batch, newDiscardedItems] = await this.#getStorage([item], childTrie ?? null, hash);
562
564
  if (newDiscardedItems.length > 0) {
@@ -589,7 +591,7 @@ export class ChainHead extends JsonRpcGroup {
589
591
  }
590
592
  async #getStorage(items, childTrie, at) {
591
593
  const operation = () => this.#getStorageOperation(items, childTrie, this.#ensurePinnedHash(at));
592
- return this.#performOperationWithRetry(operation, at);
594
+ return this.#operationQueue.add(() => this.#performOperationWithRetry(operation, at));
593
595
  }
594
596
  async #getStorageOperation(items, childTrie, at) {
595
597
  await this.#ensureFollowed();
@@ -626,4 +628,8 @@ export class ChainHead extends JsonRpcGroup {
626
628
  return;
627
629
  await this.send('unpin', this.#subscriptionId, hashes);
628
630
  }
631
+ #__unsafe__isSmoldot() {
632
+ // @ts-ignore a trick internally to check whether a provider is using smoldot connection
633
+ return typeof this.client.provider['chain'] === 'function';
634
+ }
629
635
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dedot/api",
3
- "version": "0.9.5-next.11b0ca8e.3+11b0ca8",
3
+ "version": "0.9.5-next.4fd5def5.41+4fd5def5",
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.9.5-next.11b0ca8e.3+11b0ca8",
17
- "@dedot/providers": "0.9.5-next.11b0ca8e.3+11b0ca8",
18
- "@dedot/runtime-specs": "0.9.5-next.11b0ca8e.3+11b0ca8",
19
- "@dedot/shape": "0.9.5-next.11b0ca8e.3+11b0ca8",
20
- "@dedot/storage": "0.9.5-next.11b0ca8e.3+11b0ca8",
21
- "@dedot/types": "0.9.5-next.11b0ca8e.3+11b0ca8",
22
- "@dedot/utils": "0.9.5-next.11b0ca8e.3+11b0ca8"
16
+ "@dedot/codecs": "0.9.5-next.4fd5def5.41+4fd5def5",
17
+ "@dedot/providers": "0.9.5-next.4fd5def5.41+4fd5def5",
18
+ "@dedot/runtime-specs": "0.9.5-next.4fd5def5.41+4fd5def5",
19
+ "@dedot/shape": "0.9.5-next.4fd5def5.41+4fd5def5",
20
+ "@dedot/storage": "0.9.5-next.4fd5def5.41+4fd5def5",
21
+ "@dedot/types": "0.9.5-next.4fd5def5.41+4fd5def5",
22
+ "@dedot/utils": "0.9.5-next.4fd5def5.41+4fd5def5"
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": "11b0ca8e2df2c59e12259dc2ba0c61ec91e181cf",
51
+ "gitHead": "4fd5def58b4308a9c05483e226a6a43ff6405667",
52
52
  "module": "./index.js",
53
53
  "types": "./index.d.ts"
54
54
  }
@@ -1,4 +1,4 @@
1
- import { AsyncQueue } from '@dedot/utils';
1
+ import { AsyncQueue, noop } from '@dedot/utils';
2
2
  import { BaseStorageQuery } from './BaseStorageQuery.js';
3
3
  /**
4
4
  * @name NewStorageQuery
@@ -80,7 +80,13 @@ export class NewStorageQuery extends BaseStorageQuery {
80
80
  await pull(best);
81
81
  // Subscribe to best block events
82
82
  const unsub = this.client.on('bestBlock', (block) => {
83
- pullQueue.enqueue(() => pull(block)).catch(console.error);
83
+ // Here we're handling each pull one by one,
84
+ // If the queue get too long, it might take a long time for us to get the fresh & latest data
85
+ // This is a precaution in such case, if the queue size >= 3 we skip all the pending pull and jump to the latest pull
86
+ if (pullQueue.size >= 3)
87
+ pullQueue.clear();
88
+ // TODO timing out for a pull to prevent it took too long to fetch
89
+ pullQueue.enqueue(() => pull(block)).catch(noop);
84
90
  });
85
91
  return async () => {
86
92
  unsub();