@polkadot/rpc-core 9.14.2 → 10.0.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.
package/bundle.js CHANGED
@@ -1,51 +1,31 @@
1
- // Copyright 2017-2023 @polkadot/rpc-core authors & contributors
2
- // SPDX-License-Identifier: Apache-2.0
3
-
4
1
  import { Observable, publishReplay, refCount } from 'rxjs';
5
2
  import { rpcDefinitions } from '@polkadot/types';
6
3
  import { hexToU8a, isFunction, isNull, isUndefined, lazyMethod, logger, memoize, objectSpread, u8aConcat, u8aToU8a } from '@polkadot/util';
7
- import { drr, refCountDelay } from "./util/index.js";
8
- export { packageInfo } from "./packageInfo.js";
9
- export * from "./util/index.js";
4
+ import { drr, refCountDelay } from './util/index.js';
5
+ export { packageInfo } from './packageInfo.js';
6
+ export * from './util/index.js';
10
7
  const l = logger('rpc-core');
11
8
  const EMPTY_META = {
12
- fallback: undefined,
13
- modifier: {
14
- isOptional: true
15
- },
16
- type: {
17
- asMap: {
18
- linked: {
19
- isTrue: false
20
- }
21
- },
22
- isMap: false
23
- }
9
+ fallback: undefined,
10
+ modifier: { isOptional: true },
11
+ type: {
12
+ asMap: { linked: { isTrue: false } },
13
+ isMap: false
14
+ }
24
15
  };
25
-
26
- // utility method to create a nicely-formatted error
27
16
  /** @internal */
28
- function logErrorMessage(method, {
29
- noErrorLog,
30
- params,
31
- type
32
- }, error) {
33
- if (noErrorLog) {
34
- return;
35
- }
36
- const inputs = params.map(({
37
- isOptional,
38
- name,
39
- type
40
- }) => `${name}${isOptional ? '?' : ''}: ${type}`).join(', ');
41
- l.error(`${method}(${inputs}): ${type}:: ${error.message}`);
17
+ function logErrorMessage(method, { noErrorLog, params, type }, error) {
18
+ if (noErrorLog) {
19
+ return;
20
+ }
21
+ const inputs = params.map(({ isOptional, name, type }) => `${name}${isOptional ? '?' : ''}: ${type}`).join(', ');
22
+ l.error(`${method}(${inputs}): ${type}:: ${error.message}`);
42
23
  }
43
24
  function isTreatAsHex(key) {
44
- // :code is problematic - it does not have the length attached, which is
45
- // unlike all other storage entries where it is indeed properly encoded
46
- return ['0x3a636f6465'].includes(key.toHex());
25
+ // :code is problematic - it does not have the length attached, which is
26
+ // unlike all other storage entries where it is indeed properly encoded
27
+ return ['0x3a636f6465'].includes(key.toHex());
47
28
  }
48
-
49
29
  /**
50
30
  * @name Rpc
51
31
  * @summary The API may use a HTTP or WebSockets provider.
@@ -70,340 +50,332 @@ function isTreatAsHex(key) {
70
50
  * ```
71
51
  */
72
52
  export class RpcCore {
73
- #instanceId;
74
- #registryDefault;
75
- #getBlockRegistry;
76
- #getBlockHash;
77
- #storageCache = new Map();
78
- mapping = new Map();
79
- sections = [];
80
-
81
- /**
82
- * @constructor
83
- * Default constructor for the Api Object
84
- * @param {ProviderInterface} provider An API provider using HTTP or WebSocket
85
- */
86
- constructor(instanceId, registry, provider, userRpc = {}) {
87
- // eslint-disable-next-line @typescript-eslint/unbound-method
88
- if (!provider || !isFunction(provider.send)) {
89
- throw new Error('Expected Provider to API create');
90
- }
91
- this.#instanceId = instanceId;
92
- this.#registryDefault = registry;
93
- this.provider = provider;
94
- const sectionNames = Object.keys(rpcDefinitions);
95
-
96
- // these are the base keys (i.e. part of jsonrpc)
97
- this.sections.push(...sectionNames);
98
-
99
- // decorate all interfaces, defined and user on this instance
100
- this.addUserInterfaces(userRpc);
101
- }
102
-
103
- /**
104
- * @description Returns the connected status of a provider
105
- */
106
- get isConnected() {
107
- return this.provider.isConnected;
108
- }
109
-
110
- /**
111
- * @description Manually connect from the attached provider
112
- */
113
- connect() {
114
- return this.provider.connect();
115
- }
116
-
117
- /**
118
- * @description Manually disconnect from the attached provider
119
- */
120
- disconnect() {
121
- return this.provider.disconnect();
122
- }
123
-
124
- /**
125
- * @description Sets a registry swap (typically from Api)
126
- */
127
- setRegistrySwap(registrySwap) {
128
- this.#getBlockRegistry = memoize(registrySwap, {
129
- getInstanceId: () => this.#instanceId
130
- });
131
- }
132
-
133
- /**
134
- * @description Sets a function to resolve block hash from block number
135
- */
136
- setResolveBlockHash(resolveBlockHash) {
137
- this.#getBlockHash = memoize(resolveBlockHash, {
138
- getInstanceId: () => this.#instanceId
139
- });
140
- }
141
- addUserInterfaces(userRpc) {
142
- // add any extra user-defined sections
143
- this.sections.push(...Object.keys(userRpc).filter(k => !this.sections.includes(k)));
144
- for (let s = 0; s < this.sections.length; s++) {
145
- const section = this.sections[s];
146
- const defs = objectSpread({}, rpcDefinitions[section], userRpc[section]);
147
- const methods = Object.keys(defs);
148
- for (let m = 0; m < methods.length; m++) {
149
- const method = methods[m];
150
- const def = defs[method];
151
- const jsonrpc = def.endpoint || `${section}_${method}`;
152
- if (!this.mapping.has(jsonrpc)) {
153
- const isSubscription = !!def.pubsub;
154
- if (!this[section]) {
155
- this[section] = {};
156
- }
157
- this.mapping.set(jsonrpc, objectSpread({}, def, {
158
- isSubscription,
159
- jsonrpc,
160
- method,
161
- section
162
- }));
163
- lazyMethod(this[section], method, () => isSubscription ? this._createMethodSubscribe(section, method, def) : this._createMethodSend(section, method, def));
53
+ #instanceId;
54
+ #registryDefault;
55
+ #getBlockRegistry;
56
+ #getBlockHash;
57
+ #storageCache = new Map();
58
+ mapping = new Map();
59
+ provider;
60
+ sections = [];
61
+ /**
62
+ * @constructor
63
+ * Default constructor for the Api Object
64
+ * @param {ProviderInterface} provider An API provider using HTTP or WebSocket
65
+ */
66
+ constructor(instanceId, registry, provider, userRpc = {}) {
67
+ // eslint-disable-next-line @typescript-eslint/unbound-method
68
+ if (!provider || !isFunction(provider.send)) {
69
+ throw new Error('Expected Provider to API create');
164
70
  }
165
- }
71
+ this.#instanceId = instanceId;
72
+ this.#registryDefault = registry;
73
+ this.provider = provider;
74
+ const sectionNames = Object.keys(rpcDefinitions);
75
+ // these are the base keys (i.e. part of jsonrpc)
76
+ this.sections.push(...sectionNames);
77
+ // decorate all interfaces, defined and user on this instance
78
+ this.addUserInterfaces(userRpc);
79
+ }
80
+ /**
81
+ * @description Returns the connected status of a provider
82
+ */
83
+ get isConnected() {
84
+ return this.provider.isConnected;
85
+ }
86
+ /**
87
+ * @description Manually connect from the attached provider
88
+ */
89
+ connect() {
90
+ return this.provider.connect();
166
91
  }
167
- }
168
- _memomize(creator, def) {
169
- const memoOpts = {
170
- getInstanceId: () => this.#instanceId
171
- };
172
- const memoized = memoize(creator(true), memoOpts);
173
- memoized.raw = memoize(creator(false), memoOpts);
174
- memoized.meta = def;
175
- return memoized;
176
- }
177
- _formatResult(isScale, registry, blockHash, method, def, params, result) {
178
- return isScale ? this._formatOutput(registry, blockHash, method, def, params, result) : result;
179
- }
180
- _createMethodSend(section, method, def) {
181
- const rpcName = def.endpoint || `${section}_${method}`;
182
- const hashIndex = def.params.findIndex(({
183
- isHistoric
184
- }) => isHistoric);
185
- let memoized = null;
186
-
187
- // execute the RPC call, doing a registry swap for historic as applicable
188
- const callWithRegistry = async (isScale, values) => {
189
- var _this$getBlockHash;
190
- const blockId = hashIndex === -1 ? null : values[hashIndex];
191
- const blockHash = blockId && def.params[hashIndex].type === 'BlockNumber' ? await ((_this$getBlockHash = this.#getBlockHash) == null ? void 0 : _this$getBlockHash.call(this, blockId)) : blockId;
192
- const {
193
- registry
194
- } = isScale && blockHash && this.#getBlockRegistry ? await this.#getBlockRegistry(u8aToU8a(blockHash)) : {
195
- registry: this.#registryDefault
196
- };
197
- const params = this._formatInputs(registry, null, def, values);
198
-
199
- // only cache .at(<blockHash>) queries, e.g. where valid blockHash was supplied
200
- const result = await this.provider.send(rpcName, params.map(p => p.toJSON()), !!blockHash);
201
- return this._formatResult(isScale, registry, blockHash, method, def, params, result);
202
- };
203
- const creator = isScale => (...values) => {
204
- const isDelayed = isScale && hashIndex !== -1 && !!values[hashIndex];
205
- return new Observable(observer => {
206
- callWithRegistry(isScale, values).then(value => {
207
- observer.next(value);
208
- observer.complete();
209
- }).catch(error => {
210
- logErrorMessage(method, def, error);
211
- observer.error(error);
212
- observer.complete();
92
+ /**
93
+ * @description Manually disconnect from the attached provider
94
+ */
95
+ disconnect() {
96
+ return this.provider.disconnect();
97
+ }
98
+ /**
99
+ * @description Sets a registry swap (typically from Api)
100
+ */
101
+ setRegistrySwap(registrySwap) {
102
+ this.#getBlockRegistry = memoize(registrySwap, {
103
+ getInstanceId: () => this.#instanceId
213
104
  });
214
- return () => {
215
- // delete old results from cache
216
- if (isScale) {
217
- var _memoized;
218
- (_memoized = memoized) == null ? void 0 : _memoized.unmemoize(...values);
219
- } else {
220
- var _memoized2;
221
- (_memoized2 = memoized) == null ? void 0 : _memoized2.raw.unmemoize(...values);
222
- }
223
- };
224
- }).pipe(
225
- // eslint-disable-next-line deprecation/deprecation
226
- publishReplay(1),
227
- // create a Replay(1)
228
- isDelayed ? refCountDelay() // Unsubscribe after delay
229
- // eslint-disable-next-line deprecation/deprecation
230
- : refCount());
231
- };
232
- memoized = this._memomize(creator, def);
233
- return memoized;
234
- }
235
-
236
- // create a subscriptor, it subscribes once and resolves with the id as subscribe
237
- _createSubscriber({
238
- paramsJson,
239
- subName,
240
- subType,
241
- update
242
- }, errorHandler) {
243
- return new Promise((resolve, reject) => {
244
- this.provider.subscribe(subType, subName, paramsJson, update).then(resolve).catch(error => {
245
- errorHandler(error);
246
- reject(error);
247
- });
248
- });
249
- }
250
- _createMethodSubscribe(section, method, def) {
251
- const [updateType, subMethod, unsubMethod] = def.pubsub;
252
- const subName = `${section}_${subMethod}`;
253
- const unsubName = `${section}_${unsubMethod}`;
254
- const subType = `${section}_${updateType}`;
255
- let memoized = null;
256
- const creator = isScale => (...values) => {
257
- return new Observable(observer => {
258
- // Have at least an empty promise, as used in the unsubscribe
259
- let subscriptionPromise = Promise.resolve(null);
260
- const registry = this.#registryDefault;
261
- const errorHandler = error => {
262
- logErrorMessage(method, def, error);
263
- observer.error(error);
264
- };
265
- try {
266
- const params = this._formatInputs(registry, null, def, values);
267
- const paramsJson = params.map(p => p.toJSON());
268
- const update = (error, result) => {
269
- if (error) {
270
- logErrorMessage(method, def, error);
271
- return;
272
- }
273
- try {
274
- observer.next(this._formatResult(isScale, registry, null, method, def, params, result));
275
- } catch (error) {
276
- observer.error(error);
105
+ }
106
+ /**
107
+ * @description Sets a function to resolve block hash from block number
108
+ */
109
+ setResolveBlockHash(resolveBlockHash) {
110
+ this.#getBlockHash = memoize(resolveBlockHash, {
111
+ getInstanceId: () => this.#instanceId
112
+ });
113
+ }
114
+ addUserInterfaces(userRpc) {
115
+ // add any extra user-defined sections
116
+ this.sections.push(...Object.keys(userRpc).filter((k) => !this.sections.includes(k)));
117
+ for (let s = 0; s < this.sections.length; s++) {
118
+ const section = this.sections[s];
119
+ const defs = objectSpread({}, rpcDefinitions[section], userRpc[section]);
120
+ const methods = Object.keys(defs);
121
+ for (let m = 0; m < methods.length; m++) {
122
+ const method = methods[m];
123
+ const def = defs[method];
124
+ const jsonrpc = def.endpoint || `${section}_${method}`;
125
+ if (!this.mapping.has(jsonrpc)) {
126
+ const isSubscription = !!def.pubsub;
127
+ if (!this[section]) {
128
+ this[section] = {};
129
+ }
130
+ this.mapping.set(jsonrpc, objectSpread({}, def, { isSubscription, jsonrpc, method, section }));
131
+ lazyMethod(this[section], method, () => isSubscription
132
+ ? this._createMethodSubscribe(section, method, def)
133
+ : this._createMethodSend(section, method, def));
134
+ }
277
135
  }
278
- };
279
- subscriptionPromise = this._createSubscriber({
280
- paramsJson,
281
- subName,
282
- subType,
283
- update
284
- }, errorHandler);
285
- } catch (error) {
286
- errorHandler(error);
287
136
  }
288
-
289
- // Teardown logic
290
- return () => {
291
- // Delete from cache, so old results don't hang around
292
- if (isScale) {
293
- var _memoized3;
294
- (_memoized3 = memoized) == null ? void 0 : _memoized3.unmemoize(...values);
295
- } else {
296
- var _memoized4;
297
- (_memoized4 = memoized) == null ? void 0 : _memoized4.raw.unmemoize(...values);
298
- }
299
-
300
- // Unsubscribe from provider
301
- subscriptionPromise.then(subscriptionId => isNull(subscriptionId) ? Promise.resolve(false) : this.provider.unsubscribe(subType, unsubName, subscriptionId)).catch(error => logErrorMessage(method, def, error));
137
+ }
138
+ _memomize(creator, def) {
139
+ const memoOpts = { getInstanceId: () => this.#instanceId };
140
+ const memoized = memoize(creator(true), memoOpts);
141
+ memoized.raw = memoize(creator(false), memoOpts);
142
+ memoized.meta = def;
143
+ return memoized;
144
+ }
145
+ _formatResult(isScale, registry, blockHash, method, def, params, result) {
146
+ return isScale
147
+ ? this._formatOutput(registry, blockHash, method, def, params, result)
148
+ : result;
149
+ }
150
+ _createMethodSend(section, method, def) {
151
+ const rpcName = def.endpoint || `${section}_${method}`;
152
+ const hashIndex = def.params.findIndex(({ isHistoric }) => isHistoric);
153
+ let memoized = null;
154
+ // execute the RPC call, doing a registry swap for historic as applicable
155
+ const callWithRegistry = async (isScale, values) => {
156
+ const blockId = hashIndex === -1
157
+ ? null
158
+ : values[hashIndex];
159
+ const blockHash = blockId && def.params[hashIndex].type === 'BlockNumber'
160
+ ? await this.#getBlockHash?.(blockId)
161
+ : blockId;
162
+ const { registry } = isScale && blockHash && this.#getBlockRegistry
163
+ ? await this.#getBlockRegistry(u8aToU8a(blockHash))
164
+ : { registry: this.#registryDefault };
165
+ const params = this._formatInputs(registry, null, def, values);
166
+ // only cache .at(<blockHash>) queries, e.g. where valid blockHash was supplied
167
+ const result = await this.provider.send(rpcName, params.map((p) => p.toJSON()), !!blockHash);
168
+ return this._formatResult(isScale, registry, blockHash, method, def, params, result);
169
+ };
170
+ const creator = (isScale) => (...values) => {
171
+ const isDelayed = isScale && hashIndex !== -1 && !!values[hashIndex];
172
+ return new Observable((observer) => {
173
+ callWithRegistry(isScale, values)
174
+ .then((value) => {
175
+ observer.next(value);
176
+ observer.complete();
177
+ })
178
+ .catch((error) => {
179
+ logErrorMessage(method, def, error);
180
+ observer.error(error);
181
+ observer.complete();
182
+ });
183
+ return () => {
184
+ // delete old results from cache
185
+ if (isScale) {
186
+ memoized?.unmemoize(...values);
187
+ }
188
+ else {
189
+ memoized?.raw.unmemoize(...values);
190
+ }
191
+ };
192
+ }).pipe(
193
+ // eslint-disable-next-line deprecation/deprecation
194
+ publishReplay(1), // create a Replay(1)
195
+ isDelayed
196
+ ? refCountDelay() // Unsubscribe after delay
197
+ // eslint-disable-next-line deprecation/deprecation
198
+ : refCount());
302
199
  };
303
- }).pipe(drr());
304
- };
305
- memoized = this._memomize(creator, def);
306
- return memoized;
307
- }
308
- _formatInputs(registry, blockHash, def, inputs) {
309
- const reqArgCount = def.params.filter(({
310
- isOptional
311
- }) => !isOptional).length;
312
- const optText = reqArgCount === def.params.length ? '' : ` (${def.params.length - reqArgCount} optional)`;
313
- if (inputs.length < reqArgCount || inputs.length > def.params.length) {
314
- throw new Error(`Expected ${def.params.length} parameters${optText}, ${inputs.length} found instead`);
200
+ memoized = this._memomize(creator, def);
201
+ return memoized;
315
202
  }
316
- return inputs.map((input, index) => registry.createTypeUnsafe(def.params[index].type, [input], {
317
- blockHash
318
- }));
319
- }
320
- _formatOutput(registry, blockHash, method, rpc, params, result) {
321
- if (rpc.type === 'StorageData') {
322
- const key = params[0];
323
- return this._formatStorageData(registry, blockHash, key, result);
324
- } else if (rpc.type === 'StorageChangeSet') {
325
- const keys = params[0];
326
- return keys ? this._formatStorageSet(registry, result.block, keys, result.changes) : registry.createType('StorageChangeSet', result);
327
- } else if (rpc.type === 'Vec<StorageChangeSet>') {
328
- const mapped = result.map(({
329
- block,
330
- changes
331
- }) => [registry.createType('Hash', block), this._formatStorageSet(registry, block, params[0], changes)]);
332
-
333
- // we only query at a specific block, not a range - flatten
334
- return method === 'queryStorageAt' ? mapped[0][1] : mapped;
203
+ // create a subscriptor, it subscribes once and resolves with the id as subscribe
204
+ _createSubscriber({ paramsJson, subName, subType, update }, errorHandler) {
205
+ return new Promise((resolve, reject) => {
206
+ this.provider
207
+ .subscribe(subType, subName, paramsJson, update)
208
+ .then(resolve)
209
+ .catch((error) => {
210
+ errorHandler(error);
211
+ reject(error);
212
+ });
213
+ });
214
+ }
215
+ _createMethodSubscribe(section, method, def) {
216
+ const [updateType, subMethod, unsubMethod] = def.pubsub;
217
+ const subName = `${section}_${subMethod}`;
218
+ const unsubName = `${section}_${unsubMethod}`;
219
+ const subType = `${section}_${updateType}`;
220
+ let memoized = null;
221
+ const creator = (isScale) => (...values) => {
222
+ return new Observable((observer) => {
223
+ // Have at least an empty promise, as used in the unsubscribe
224
+ let subscriptionPromise = Promise.resolve(null);
225
+ const registry = this.#registryDefault;
226
+ const errorHandler = (error) => {
227
+ logErrorMessage(method, def, error);
228
+ observer.error(error);
229
+ };
230
+ try {
231
+ const params = this._formatInputs(registry, null, def, values);
232
+ const paramsJson = params.map((p) => p.toJSON());
233
+ const update = (error, result) => {
234
+ if (error) {
235
+ logErrorMessage(method, def, error);
236
+ return;
237
+ }
238
+ try {
239
+ observer.next(this._formatResult(isScale, registry, null, method, def, params, result));
240
+ }
241
+ catch (error) {
242
+ observer.error(error);
243
+ }
244
+ };
245
+ subscriptionPromise = this._createSubscriber({ paramsJson, subName, subType, update }, errorHandler);
246
+ }
247
+ catch (error) {
248
+ errorHandler(error);
249
+ }
250
+ // Teardown logic
251
+ return () => {
252
+ // Delete from cache, so old results don't hang around
253
+ if (isScale) {
254
+ memoized?.unmemoize(...values);
255
+ }
256
+ else {
257
+ memoized?.raw.unmemoize(...values);
258
+ }
259
+ // Unsubscribe from provider
260
+ subscriptionPromise
261
+ .then((subscriptionId) => isNull(subscriptionId)
262
+ ? Promise.resolve(false)
263
+ : this.provider.unsubscribe(subType, unsubName, subscriptionId))
264
+ .catch((error) => logErrorMessage(method, def, error));
265
+ };
266
+ }).pipe(drr());
267
+ };
268
+ memoized = this._memomize(creator, def);
269
+ return memoized;
270
+ }
271
+ _formatInputs(registry, blockHash, def, inputs) {
272
+ const reqArgCount = def.params.filter(({ isOptional }) => !isOptional).length;
273
+ const optText = reqArgCount === def.params.length
274
+ ? ''
275
+ : ` (${def.params.length - reqArgCount} optional)`;
276
+ if (inputs.length < reqArgCount || inputs.length > def.params.length) {
277
+ throw new Error(`Expected ${def.params.length} parameters${optText}, ${inputs.length} found instead`);
278
+ }
279
+ return inputs.map((input, index) => registry.createTypeUnsafe(def.params[index].type, [input], { blockHash }));
280
+ }
281
+ _formatOutput(registry, blockHash, method, rpc, params, result) {
282
+ if (rpc.type === 'StorageData') {
283
+ const key = params[0];
284
+ return this._formatStorageData(registry, blockHash, key, result);
285
+ }
286
+ else if (rpc.type === 'StorageChangeSet') {
287
+ const keys = params[0];
288
+ return keys
289
+ ? this._formatStorageSet(registry, result.block, keys, result.changes)
290
+ : registry.createType('StorageChangeSet', result);
291
+ }
292
+ else if (rpc.type === 'Vec<StorageChangeSet>') {
293
+ const mapped = result.map(({ block, changes }) => [
294
+ registry.createType('Hash', block),
295
+ this._formatStorageSet(registry, block, params[0], changes)
296
+ ]);
297
+ // we only query at a specific block, not a range - flatten
298
+ return method === 'queryStorageAt'
299
+ ? mapped[0][1]
300
+ : mapped;
301
+ }
302
+ return registry.createTypeUnsafe(rpc.type, [result], { blockHash });
303
+ }
304
+ _formatStorageData(registry, blockHash, key, value) {
305
+ const isEmpty = isNull(value);
306
+ // we convert to Uint8Array since it maps to the raw encoding, all
307
+ // data will be correctly encoded (incl. numbers, excl. :code)
308
+ const input = isEmpty
309
+ ? null
310
+ : isTreatAsHex(key)
311
+ ? value
312
+ : u8aToU8a(value);
313
+ return this._newType(registry, blockHash, key, input, isEmpty);
335
314
  }
336
- return registry.createTypeUnsafe(rpc.type, [result], {
337
- blockHash
338
- });
339
- }
340
- _formatStorageData(registry, blockHash, key, value) {
341
- const isEmpty = isNull(value);
342
-
343
- // we convert to Uint8Array since it maps to the raw encoding, all
344
- // data will be correctly encoded (incl. numbers, excl. :code)
345
- const input = isEmpty ? null : isTreatAsHex(key) ? value : u8aToU8a(value);
346
- return this._newType(registry, blockHash, key, input, isEmpty);
347
- }
348
- _formatStorageSet(registry, blockHash, keys, changes) {
349
- // For StorageChangeSet, the changes has the [key, value] mappings
350
- const withCache = keys.length !== 1;
351
-
352
- // multiple return values (via state.storage subscription), decode the values
353
- // one at a time, all based on the query types. Three values can be returned -
354
- // - Codec - There is a valid value, non-empty
355
- // - null - The storage key is empty
356
- return keys.reduce((results, key, index) => {
357
- results.push(this._formatStorageSetEntry(registry, blockHash, key, changes, withCache, index));
358
- return results;
359
- }, []);
360
- }
361
- _formatStorageSetEntry(registry, blockHash, key, changes, withCache, entryIndex) {
362
- const hexKey = key.toHex();
363
- const found = changes.find(([key]) => key === hexKey);
364
- const isNotFound = isUndefined(found);
365
-
366
- // if we don't find the value, this is our fallback
367
- // - in the case of an array of values, fill the hole from the cache
368
- // - if a single result value, don't fill - it is not an update hole
369
- // - fallback to an empty option in all cases
370
- if (isNotFound && withCache) {
371
- const cached = this.#storageCache.get(hexKey);
372
- if (cached) {
373
- return cached;
374
- }
315
+ _formatStorageSet(registry, blockHash, keys, changes) {
316
+ // For StorageChangeSet, the changes has the [key, value] mappings
317
+ const withCache = keys.length !== 1;
318
+ // multiple return values (via state.storage subscription), decode the values
319
+ // one at a time, all based on the query types. Three values can be returned -
320
+ // - Codec - There is a valid value, non-empty
321
+ // - null - The storage key is empty
322
+ return keys.reduce((results, key, index) => {
323
+ results.push(this._formatStorageSetEntry(registry, blockHash, key, changes, withCache, index));
324
+ return results;
325
+ }, []);
326
+ }
327
+ _formatStorageSetEntry(registry, blockHash, key, changes, withCache, entryIndex) {
328
+ const hexKey = key.toHex();
329
+ const found = changes.find(([key]) => key === hexKey);
330
+ const isNotFound = isUndefined(found);
331
+ // if we don't find the value, this is our fallback
332
+ // - in the case of an array of values, fill the hole from the cache
333
+ // - if a single result value, don't fill - it is not an update hole
334
+ // - fallback to an empty option in all cases
335
+ if (isNotFound && withCache) {
336
+ const cached = this.#storageCache.get(hexKey);
337
+ if (cached) {
338
+ return cached;
339
+ }
340
+ }
341
+ const value = isNotFound
342
+ ? null
343
+ : found[1];
344
+ const isEmpty = isNull(value);
345
+ const input = isEmpty || isTreatAsHex(key)
346
+ ? value
347
+ : u8aToU8a(value);
348
+ const codec = this._newType(registry, blockHash, key, input, isEmpty, entryIndex);
349
+ // store the retrieved result - the only issue with this cache is that there is no
350
+ // clearing of it, so very long running processes (not just a couple of hours, longer)
351
+ // will increase memory beyond what is allowed.
352
+ this.#storageCache.set(hexKey, codec);
353
+ return codec;
375
354
  }
376
- const value = isNotFound ? null : found[1];
377
- const isEmpty = isNull(value);
378
- const input = isEmpty || isTreatAsHex(key) ? value : u8aToU8a(value);
379
- const codec = this._newType(registry, blockHash, key, input, isEmpty, entryIndex);
380
-
381
- // store the retrieved result - the only issue with this cache is that there is no
382
- // clearing of it, so very long running processes (not just a couple of hours, longer)
383
- // will increase memory beyond what is allowed.
384
- this.#storageCache.set(hexKey, codec);
385
- return codec;
386
- }
387
- _newType(registry, blockHash, key, input, isEmpty, entryIndex = -1) {
388
- // single return value (via state.getStorage), decode the value based on the
389
- // outputType that we have specified. Fallback to Raw on nothing
390
- const type = key.outputType || 'Raw';
391
- const meta = key.meta || EMPTY_META;
392
- const entryNum = entryIndex === -1 ? '' : ` entry ${entryIndex}:`;
393
- try {
394
- return registry.createTypeUnsafe(type, [isEmpty ? meta.fallback
395
- // For old-style Linkage, we add an empty linkage at the end
396
- ? type.includes('Linkage<') ? u8aConcat(hexToU8a(meta.fallback.toHex()), new Uint8Array(2)) : hexToU8a(meta.fallback.toHex()) : undefined : meta.modifier.isOptional ? registry.createTypeUnsafe(type, [input], {
397
- blockHash,
398
- isPedantic: true
399
- }) : input], {
400
- blockHash,
401
- isFallback: isEmpty && !!meta.fallback,
402
- isOptional: meta.modifier.isOptional,
403
- isPedantic: !meta.modifier.isOptional
404
- });
405
- } catch (error) {
406
- throw new Error(`Unable to decode storage ${key.section || 'unknown'}.${key.method || 'unknown'}:${entryNum}: ${error.message}`);
355
+ _newType(registry, blockHash, key, input, isEmpty, entryIndex = -1) {
356
+ // single return value (via state.getStorage), decode the value based on the
357
+ // outputType that we have specified. Fallback to Raw on nothing
358
+ const type = key.outputType || 'Raw';
359
+ const meta = key.meta || EMPTY_META;
360
+ const entryNum = entryIndex === -1
361
+ ? ''
362
+ : ` entry ${entryIndex}:`;
363
+ try {
364
+ return registry.createTypeUnsafe(type, [
365
+ isEmpty
366
+ ? meta.fallback
367
+ // For old-style Linkage, we add an empty linkage at the end
368
+ ? type.includes('Linkage<')
369
+ ? u8aConcat(hexToU8a(meta.fallback.toHex()), new Uint8Array(2))
370
+ : hexToU8a(meta.fallback.toHex())
371
+ : undefined
372
+ : meta.modifier.isOptional
373
+ ? registry.createTypeUnsafe(type, [input], { blockHash, isPedantic: true })
374
+ : input
375
+ ], { blockHash, isFallback: isEmpty && !!meta.fallback, isOptional: meta.modifier.isOptional, isPedantic: !meta.modifier.isOptional });
376
+ }
377
+ catch (error) {
378
+ throw new Error(`Unable to decode storage ${key.section || 'unknown'}.${key.method || 'unknown'}:${entryNum}: ${error.message}`);
379
+ }
407
380
  }
408
- }
409
381
  }