1 | import { BehaviorSubject, combineLatest, from, map, of, switchMap, tap, toArray } from 'rxjs';
|
2 | import { getAvailableDerives } from '@polkadot/api-derive';
|
3 | import { memo, RpcCore } from '@polkadot/rpc-core';
|
4 | import { WsProvider } from '@polkadot/rpc-provider';
|
5 | import { expandMetadata, GenericExtrinsic, typeDefinitions, TypeRegistry } from '@polkadot/types';
|
6 | import { getSpecRuntime } from '@polkadot/types-known';
|
7 | import { arrayChunk, arrayFlatten, assertReturn, BN, compactStripLength, lazyMethod, lazyMethods, logger, nextTick, objectSpread, stringCamelCase, stringUpperFirst, u8aConcatStrict, u8aToHex } from '@polkadot/util';
|
8 | import { blake2AsHex } from '@polkadot/util-crypto';
|
9 | import { createSubmittable } from '../submittable/index.js';
|
10 | import { augmentObject } from '../util/augmentObject.js';
|
11 | import { decorateDeriveSections } from '../util/decorate.js';
|
12 | import { extractStorageArgs } from '../util/validate.js';
|
13 | import { Events } from './Events.js';
|
14 | import { findCall, findError } from './find.js';
|
15 | const PAGE_SIZE_K = 1000;
|
16 | const PAGE_SIZE_V = 250;
|
17 | const PAGE_SIZE_Q = 50;
|
18 | const l = logger('api/init');
|
19 | let instanceCounter = 0;
|
20 | function getAtQueryFn(api, { method, section }) {
|
21 | return assertReturn(api.rx.query[section] && api.rx.query[section][method], () => `query.${section}.${method} is not available in this version of the metadata`);
|
22 | }
|
23 | export class Decorate extends Events {
|
24 | __internal__instanceId;
|
25 | __internal__runtimeLog = {};
|
26 | __internal__registry;
|
27 | __internal__storageGetQ = [];
|
28 | __internal__storageSubQ = [];
|
29 |
|
30 | __phantom = new BN(0);
|
31 | _type;
|
32 | _call = {};
|
33 | _consts = {};
|
34 | _derive;
|
35 | _errors = {};
|
36 | _events = {};
|
37 | _extrinsics;
|
38 | _extrinsicType = GenericExtrinsic.LATEST_EXTRINSIC_VERSION;
|
39 | _genesisHash;
|
40 | _isConnected;
|
41 | _isReady = false;
|
42 | _query = {};
|
43 | _queryMulti;
|
44 | _rpc;
|
45 | _rpcCore;
|
46 | _runtimeMap = {};
|
47 | _runtimeChain;
|
48 | _runtimeMetadata;
|
49 | _runtimeVersion;
|
50 | _rx = { call: {}, consts: {}, query: {}, tx: {} };
|
51 | _options;
|
52 | |
53 |
|
54 |
|
55 |
|
56 |
|
57 |
|
58 |
|
59 |
|
60 |
|
61 |
|
62 |
|
63 |
|
64 |
|
65 |
|
66 |
|
67 |
|
68 | _decorateMethod;
|
69 | |
70 |
|
71 |
|
72 |
|
73 |
|
74 |
|
75 |
|
76 |
|
77 |
|
78 |
|
79 |
|
80 |
|
81 |
|
82 |
|
83 |
|
84 |
|
85 |
|
86 |
|
87 | constructor(options, type, decorateMethod) {
|
88 | super();
|
89 | this.__internal__instanceId = `${++instanceCounter}`;
|
90 | this.__internal__registry = options.source?.registry || options.registry || new TypeRegistry();
|
91 | this._rx.callAt = (blockHash, knownVersion) => from(this.at(blockHash, knownVersion)).pipe(map((a) => a.rx.call));
|
92 | this._rx.queryAt = (blockHash, knownVersion) => from(this.at(blockHash, knownVersion)).pipe(map((a) => a.rx.query));
|
93 | this._rx.registry = this.__internal__registry;
|
94 | this._decorateMethod = decorateMethod;
|
95 | this._options = options;
|
96 | this._type = type;
|
97 | const provider = options.source
|
98 | ? options.source._rpcCore.provider.isClonable
|
99 | ? options.source._rpcCore.provider.clone()
|
100 | : options.source._rpcCore.provider
|
101 | : (options.provider || new WsProvider());
|
102 |
|
103 | this._rpcCore = new RpcCore(this.__internal__instanceId, this.__internal__registry, {
|
104 | isPedantic: this._options.isPedantic,
|
105 | provider,
|
106 | userRpc: this._options.rpc
|
107 | });
|
108 | this._isConnected = new BehaviorSubject(this._rpcCore.provider.isConnected);
|
109 | this._rx.hasSubscriptions = this._rpcCore.provider.hasSubscriptions;
|
110 | }
|
111 | |
112 |
|
113 |
|
114 | get registry() {
|
115 | return this.__internal__registry;
|
116 | }
|
117 | |
118 |
|
119 |
|
120 | createType(type, ...params) {
|
121 | return this.__internal__registry.createType(type, ...params);
|
122 | }
|
123 | |
124 |
|
125 |
|
126 | registerTypes(types) {
|
127 | types && this.__internal__registry.register(types);
|
128 | }
|
129 | |
130 |
|
131 |
|
132 | get hasSubscriptions() {
|
133 | return this._rpcCore.provider.hasSubscriptions;
|
134 | }
|
135 | |
136 |
|
137 |
|
138 | get supportMulti() {
|
139 | return this._rpcCore.provider.hasSubscriptions || !!this._rpcCore.state.queryStorageAt;
|
140 | }
|
141 | _emptyDecorated(registry, blockHash) {
|
142 | return {
|
143 | call: {},
|
144 | consts: {},
|
145 | errors: {},
|
146 | events: {},
|
147 | query: {},
|
148 | registry,
|
149 | rx: {
|
150 | call: {},
|
151 | query: {}
|
152 | },
|
153 | tx: createSubmittable(this._type, this._rx, this._decorateMethod, registry, blockHash)
|
154 | };
|
155 | }
|
156 | _createDecorated(registry, fromEmpty, decoratedApi, blockHash) {
|
157 | if (!decoratedApi) {
|
158 | decoratedApi = this._emptyDecorated(registry.registry, blockHash);
|
159 | }
|
160 | if (fromEmpty || !registry.decoratedMeta) {
|
161 | registry.decoratedMeta = expandMetadata(registry.registry, registry.metadata);
|
162 | }
|
163 | const runtime = this._decorateCalls(registry, this._decorateMethod, blockHash);
|
164 | const runtimeRx = this._decorateCalls(registry, this._rxDecorateMethod, blockHash);
|
165 | const storage = this._decorateStorage(registry.decoratedMeta, this._decorateMethod, blockHash);
|
166 | const storageRx = this._decorateStorage(registry.decoratedMeta, this._rxDecorateMethod, blockHash);
|
167 | augmentObject('consts', registry.decoratedMeta.consts, decoratedApi.consts, fromEmpty);
|
168 | augmentObject('errors', registry.decoratedMeta.errors, decoratedApi.errors, fromEmpty);
|
169 | augmentObject('events', registry.decoratedMeta.events, decoratedApi.events, fromEmpty);
|
170 | augmentObject('query', storage, decoratedApi.query, fromEmpty);
|
171 | augmentObject('query', storageRx, decoratedApi.rx.query, fromEmpty);
|
172 | augmentObject('call', runtime, decoratedApi.call, fromEmpty);
|
173 | augmentObject('call', runtimeRx, decoratedApi.rx.call, fromEmpty);
|
174 | decoratedApi.findCall = (callIndex) => findCall(registry.registry, callIndex);
|
175 | decoratedApi.findError = (errorIndex) => findError(registry.registry, errorIndex);
|
176 | decoratedApi.queryMulti = blockHash
|
177 | ? this._decorateMultiAt(decoratedApi, this._decorateMethod, blockHash)
|
178 | : this._decorateMulti(this._decorateMethod);
|
179 | decoratedApi.runtimeVersion = registry.runtimeVersion;
|
180 | return {
|
181 | createdAt: blockHash,
|
182 | decoratedApi,
|
183 | decoratedMeta: registry.decoratedMeta
|
184 | };
|
185 | }
|
186 | _injectMetadata(registry, fromEmpty = false) {
|
187 |
|
188 | if (fromEmpty || !registry.decoratedApi) {
|
189 | registry.decoratedApi = this._emptyDecorated(registry.registry);
|
190 | }
|
191 | const { decoratedApi, decoratedMeta } = this._createDecorated(registry, fromEmpty, registry.decoratedApi);
|
192 | this._call = decoratedApi.call;
|
193 | this._consts = decoratedApi.consts;
|
194 | this._errors = decoratedApi.errors;
|
195 | this._events = decoratedApi.events;
|
196 | this._query = decoratedApi.query;
|
197 | this._rx.call = decoratedApi.rx.call;
|
198 | this._rx.query = decoratedApi.rx.query;
|
199 | const tx = this._decorateExtrinsics(decoratedMeta, this._decorateMethod);
|
200 | const rxtx = this._decorateExtrinsics(decoratedMeta, this._rxDecorateMethod);
|
201 | if (fromEmpty || !this._extrinsics) {
|
202 | this._extrinsics = tx;
|
203 | this._rx.tx = rxtx;
|
204 | }
|
205 | else {
|
206 | augmentObject('tx', tx, this._extrinsics, false);
|
207 | augmentObject(null, rxtx, this._rx.tx, false);
|
208 | }
|
209 | augmentObject(null, decoratedMeta.consts, this._rx.consts, fromEmpty);
|
210 | this.emit('decorated');
|
211 | }
|
212 | |
213 |
|
214 |
|
215 |
|
216 | injectMetadata(metadata, fromEmpty, registry) {
|
217 | this._injectMetadata({ counter: 0, metadata, registry: registry || this.__internal__registry, runtimeVersion: this.__internal__registry.createType('RuntimeVersionPartial') }, fromEmpty);
|
218 | }
|
219 | _decorateFunctionMeta(input, output) {
|
220 | output.meta = input.meta;
|
221 | output.method = input.method;
|
222 | output.section = input.section;
|
223 | output.toJSON = input.toJSON;
|
224 | if (input.callIndex) {
|
225 | output.callIndex = input.callIndex;
|
226 | }
|
227 | return output;
|
228 | }
|
229 |
|
230 |
|
231 |
|
232 |
|
233 | _filterRpc(methods, additional) {
|
234 |
|
235 | if (Object.keys(additional).length !== 0) {
|
236 | this._rpcCore.addUserInterfaces(additional);
|
237 |
|
238 | this._decorateRpc(this._rpcCore, this._decorateMethod, this._rpc);
|
239 | this._decorateRpc(this._rpcCore, this._rxDecorateMethod, this._rx.rpc);
|
240 | }
|
241 |
|
242 |
|
243 | const sectionMap = {};
|
244 | for (let i = 0, count = methods.length; i < count; i++) {
|
245 | const [section] = methods[i].split('_');
|
246 | sectionMap[section] = true;
|
247 | }
|
248 |
|
249 | const sections = Object.keys(sectionMap);
|
250 | for (let i = 0, count = sections.length; i < count; i++) {
|
251 | const nameA = stringUpperFirst(sections[i]);
|
252 | const nameB = `${nameA}Api`;
|
253 | this._runtimeMap[blake2AsHex(nameA, 64)] = nameA;
|
254 | this._runtimeMap[blake2AsHex(nameB, 64)] = nameB;
|
255 | }
|
256 |
|
257 | this._filterRpcMethods(methods);
|
258 | }
|
259 | _filterRpcMethods(exposed) {
|
260 | const hasResults = exposed.length !== 0;
|
261 | const allKnown = [...this._rpcCore.mapping.entries()];
|
262 | const allKeys = [];
|
263 | const count = allKnown.length;
|
264 | for (let i = 0; i < count; i++) {
|
265 | const [, { alias, endpoint, method, pubsub, section }] = allKnown[i];
|
266 | allKeys.push(`${section}_${method}`);
|
267 | if (pubsub) {
|
268 | allKeys.push(`${section}_${pubsub[1]}`);
|
269 | allKeys.push(`${section}_${pubsub[2]}`);
|
270 | }
|
271 | if (alias) {
|
272 | allKeys.push(...alias);
|
273 | }
|
274 | if (endpoint) {
|
275 | allKeys.push(endpoint);
|
276 | }
|
277 | }
|
278 | const unknown = exposed.filter((k) => !allKeys.includes(k) &&
|
279 | !k.includes('_unstable_'));
|
280 | if (unknown.length && !this._options.noInitWarn) {
|
281 | l.warn(`RPC methods not decorated: ${unknown.join(', ')}`);
|
282 | }
|
283 |
|
284 |
|
285 | for (let i = 0; i < count; i++) {
|
286 | const [k, { method, section }] = allKnown[i];
|
287 | if (hasResults && !exposed.includes(k) && k !== 'rpc_methods') {
|
288 | if (this._rpc[section]) {
|
289 | delete this._rpc[section][method];
|
290 | delete this._rx.rpc[section][method];
|
291 | }
|
292 | }
|
293 | }
|
294 | }
|
295 | _rpcSubmitter(decorateMethod) {
|
296 | const method = (method, ...params) => {
|
297 | return from(this._rpcCore.provider.send(method, params));
|
298 | };
|
299 | return decorateMethod(method);
|
300 | }
|
301 | _decorateRpc(rpc, decorateMethod, input = this._rpcSubmitter(decorateMethod)) {
|
302 | const out = input;
|
303 | const decorateFn = (section, method) => {
|
304 | const source = rpc[section][method];
|
305 | const fn = decorateMethod(source, { methodName: method });
|
306 | fn.meta = source.meta;
|
307 | fn.raw = decorateMethod(source.raw, { methodName: method });
|
308 | return fn;
|
309 | };
|
310 | for (let s = 0, scount = rpc.sections.length; s < scount; s++) {
|
311 | const section = rpc.sections[s];
|
312 | if (!Object.prototype.hasOwnProperty.call(out, section)) {
|
313 | const methods = Object.keys(rpc[section]);
|
314 | const decorateInternal = (method) => decorateFn(section, method);
|
315 | for (let m = 0, mcount = methods.length; m < mcount; m++) {
|
316 | const method = methods[m];
|
317 |
|
318 | if (this.hasSubscriptions || !(method.startsWith('subscribe') || method.startsWith('unsubscribe'))) {
|
319 | if (!Object.prototype.hasOwnProperty.call(out, section)) {
|
320 | out[section] = {};
|
321 | }
|
322 | lazyMethod(out[section], method, decorateInternal);
|
323 | }
|
324 | }
|
325 | }
|
326 | }
|
327 | return out;
|
328 | }
|
329 |
|
330 | _addRuntimeDef(result, additional) {
|
331 | if (!additional) {
|
332 | return;
|
333 | }
|
334 | const entries = Object.entries(additional);
|
335 | for (let j = 0, ecount = entries.length; j < ecount; j++) {
|
336 | const [key, defs] = entries[j];
|
337 | if (result[key]) {
|
338 |
|
339 |
|
340 | for (let k = 0, dcount = defs.length; k < dcount; k++) {
|
341 | const def = defs[k];
|
342 | const prev = result[key].find(({ version }) => def.version === version);
|
343 | if (prev) {
|
344 |
|
345 | objectSpread(prev.methods, def.methods);
|
346 | }
|
347 | else {
|
348 |
|
349 | result[key].push(def);
|
350 | }
|
351 | }
|
352 | }
|
353 | else {
|
354 |
|
355 | result[key] = defs;
|
356 | }
|
357 | }
|
358 | }
|
359 |
|
360 | _getRuntimeDefs(registry, specName, chain = '') {
|
361 | const result = {};
|
362 | const defValues = Object.values(typeDefinitions);
|
363 |
|
364 |
|
365 | for (let i = 0, count = defValues.length; i < count; i++) {
|
366 | this._addRuntimeDef(result, defValues[i].runtime);
|
367 | }
|
368 | this._addRuntimeDef(result, getSpecRuntime(registry, chain, specName));
|
369 | this._addRuntimeDef(result, this._options.runtime);
|
370 | return Object.entries(result);
|
371 | }
|
372 |
|
373 | _decorateCalls({ registry, runtimeVersion: { apis, specName, specVersion } }, decorateMethod, blockHash) {
|
374 | const result = {};
|
375 | const named = {};
|
376 | const hashes = {};
|
377 | const sections = this._getRuntimeDefs(registry, specName, this._runtimeChain);
|
378 | const older = [];
|
379 | const implName = `${specName.toString()}/${specVersion.toString()}`;
|
380 | const hasLogged = this.__internal__runtimeLog[implName] || false;
|
381 | this.__internal__runtimeLog[implName] = true;
|
382 | for (let i = 0, scount = sections.length; i < scount; i++) {
|
383 | const [_section, secs] = sections[i];
|
384 | const sectionHash = blake2AsHex(_section, 64);
|
385 | const rtApi = apis.find(([a]) => a.eq(sectionHash));
|
386 | hashes[sectionHash] = true;
|
387 | if (rtApi) {
|
388 | const all = secs.map(({ version }) => version).sort();
|
389 | const sec = secs.find(({ version }) => rtApi[1].eq(version));
|
390 | if (sec) {
|
391 | const section = stringCamelCase(_section);
|
392 | const methods = Object.entries(sec.methods);
|
393 | if (methods.length) {
|
394 | if (!named[section]) {
|
395 | named[section] = {};
|
396 | }
|
397 | for (let m = 0, mcount = methods.length; m < mcount; m++) {
|
398 | const [_method, def] = methods[m];
|
399 | const method = stringCamelCase(_method);
|
400 | named[section][method] = objectSpread({ method, name: `${_section}_${_method}`, section, sectionHash }, def);
|
401 | }
|
402 | }
|
403 | }
|
404 | else {
|
405 | older.push(`${_section}/${rtApi[1].toString()} (${all.join('/')} known)`);
|
406 | }
|
407 | }
|
408 | }
|
409 |
|
410 | const notFound = apis
|
411 | .map(([a, v]) => [a.toHex(), v.toString()])
|
412 | .filter(([a]) => !hashes[a])
|
413 | .map(([a, v]) => `${this._runtimeMap[a] || a}/${v}`);
|
414 | if (!this._options.noInitWarn && !hasLogged) {
|
415 | if (older.length) {
|
416 | l.warn(`${implName}: Not decorating runtime apis without matching versions: ${older.join(', ')}`);
|
417 | }
|
418 | if (notFound.length) {
|
419 | l.warn(`${implName}: Not decorating unknown runtime apis: ${notFound.join(', ')}`);
|
420 | }
|
421 | }
|
422 | const stateCall = blockHash
|
423 | ? (name, bytes) => this._rpcCore.state.call(name, bytes, blockHash)
|
424 | : (name, bytes) => this._rpcCore.state.call(name, bytes);
|
425 | const lazySection = (section) => lazyMethods({}, Object.keys(named[section]), (method) => this._decorateCall(registry, named[section][method], stateCall, decorateMethod));
|
426 | const modules = Object.keys(named);
|
427 | for (let i = 0, count = modules.length; i < count; i++) {
|
428 | lazyMethod(result, modules[i], lazySection);
|
429 | }
|
430 | return result;
|
431 | }
|
432 | _decorateCall(registry, def, stateCall, decorateMethod) {
|
433 |
|
434 | const decorated = decorateMethod((...args) => {
|
435 | if (args.length !== def.params.length) {
|
436 | throw new Error(`${def.name}:: Expected ${def.params.length} arguments, found ${args.length}`);
|
437 | }
|
438 | const bytes = registry.createType('Raw', u8aConcatStrict(args.map((a, i) => registry.createTypeUnsafe(def.params[i].type, [a]).toU8a())));
|
439 | return stateCall(def.name, bytes).pipe(map((r) => registry.createTypeUnsafe(def.type, [r])));
|
440 | });
|
441 | decorated.meta = def;
|
442 |
|
443 | return decorated;
|
444 | }
|
445 |
|
446 | _decorateMulti(decorateMethod) {
|
447 |
|
448 | return decorateMethod((keys) => keys.length
|
449 | ? (this.hasSubscriptions
|
450 | ? this._rpcCore.state.subscribeStorage
|
451 | : this._rpcCore.state.queryStorageAt)(keys.map((args) => Array.isArray(args)
|
452 | ? args[0].creator.meta.type.isPlain
|
453 | ? [args[0].creator]
|
454 | : args[0].creator.meta.type.asMap.hashers.length === 1
|
455 | ? [args[0].creator, args.slice(1)]
|
456 | : [args[0].creator, ...args.slice(1)]
|
457 | : [args.creator]))
|
458 | : of([]));
|
459 | }
|
460 | _decorateMultiAt(atApi, decorateMethod, blockHash) {
|
461 |
|
462 | return decorateMethod((calls) => calls.length
|
463 | ? this._rpcCore.state.queryStorageAt(calls.map((args) => {
|
464 | if (Array.isArray(args)) {
|
465 | const { creator } = getAtQueryFn(atApi, args[0].creator);
|
466 | return creator.meta.type.isPlain
|
467 | ? [creator]
|
468 | : creator.meta.type.asMap.hashers.length === 1
|
469 | ? [creator, args.slice(1)]
|
470 | : [creator, ...args.slice(1)];
|
471 | }
|
472 | return [getAtQueryFn(atApi, args.creator).creator];
|
473 | }), blockHash)
|
474 | : of([]));
|
475 | }
|
476 | _decorateExtrinsics({ tx }, decorateMethod) {
|
477 | const result = createSubmittable(this._type, this._rx, decorateMethod);
|
478 | const lazySection = (section) => lazyMethods({}, Object.keys(tx[section]), (method) => method.startsWith('$')
|
479 | ? tx[section][method]
|
480 | : this._decorateExtrinsicEntry(tx[section][method], result));
|
481 | const sections = Object.keys(tx);
|
482 | for (let i = 0, count = sections.length; i < count; i++) {
|
483 | lazyMethod(result, sections[i], lazySection);
|
484 | }
|
485 | return result;
|
486 | }
|
487 | _decorateExtrinsicEntry(method, creator) {
|
488 | const decorated = (...params) => creator(method(...params));
|
489 |
|
490 | decorated.is = (other) => method.is(other);
|
491 |
|
492 | return this._decorateFunctionMeta(method, decorated);
|
493 | }
|
494 | _decorateStorage({ query, registry }, decorateMethod, blockHash) {
|
495 | const result = {};
|
496 | const lazySection = (section) => lazyMethods({}, Object.keys(query[section]), (method) => blockHash
|
497 | ? this._decorateStorageEntryAt(registry, query[section][method], decorateMethod, blockHash)
|
498 | : this._decorateStorageEntry(query[section][method], decorateMethod));
|
499 | const sections = Object.keys(query);
|
500 | for (let i = 0, count = sections.length; i < count; i++) {
|
501 | lazyMethod(result, sections[i], lazySection);
|
502 | }
|
503 | return result;
|
504 | }
|
505 | _decorateStorageEntry(creator, decorateMethod) {
|
506 | const getArgs = (args, registry) => extractStorageArgs(registry || this.__internal__registry, creator, args);
|
507 | const getQueryAt = (blockHash) => from(this.at(blockHash)).pipe(map((api) => getAtQueryFn(api, creator)));
|
508 |
|
509 |
|
510 | const decorated = this._decorateStorageCall(creator, decorateMethod);
|
511 | decorated.creator = creator;
|
512 |
|
513 | decorated.at = decorateMethod((blockHash, ...args) => getQueryAt(blockHash).pipe(switchMap((q) => q(...args))));
|
514 | decorated.hash = decorateMethod((...args) => this._rpcCore.state.getStorageHash(getArgs(args)));
|
515 | decorated.is = (key) => key.section === creator.section &&
|
516 | key.method === creator.method;
|
517 | decorated.key = (...args) => u8aToHex(compactStripLength(creator(...args))[1]);
|
518 | decorated.keyPrefix = (...args) => u8aToHex(creator.keyPrefix(...args));
|
519 | decorated.size = decorateMethod((...args) => this._rpcCore.state.getStorageSize(getArgs(args)));
|
520 |
|
521 | decorated.sizeAt = decorateMethod((blockHash, ...args) => getQueryAt(blockHash).pipe(switchMap((q) => this._rpcCore.state.getStorageSize(getArgs(args, q.creator.meta.registry), blockHash))));
|
522 |
|
523 | if (creator.iterKey && creator.meta.type.isMap) {
|
524 | decorated.entries = decorateMethod(memo(this.__internal__instanceId, (...args) => this._retrieveMapEntries(creator, null, args)));
|
525 |
|
526 | decorated.entriesAt = decorateMethod(memo(this.__internal__instanceId, (blockHash, ...args) => getQueryAt(blockHash).pipe(switchMap((q) => this._retrieveMapEntries(q.creator, blockHash, args)))));
|
527 | decorated.entriesPaged = decorateMethod(memo(this.__internal__instanceId, (opts) => this._retrieveMapEntriesPaged(creator, undefined, opts)));
|
528 | decorated.keys = decorateMethod(memo(this.__internal__instanceId, (...args) => this._retrieveMapKeys(creator, null, args)));
|
529 |
|
530 | decorated.keysAt = decorateMethod(memo(this.__internal__instanceId, (blockHash, ...args) => getQueryAt(blockHash).pipe(switchMap((q) => this._retrieveMapKeys(q.creator, blockHash, args)))));
|
531 | decorated.keysPaged = decorateMethod(memo(this.__internal__instanceId, (opts) => this._retrieveMapKeysPaged(creator, undefined, opts)));
|
532 | }
|
533 | if (this.supportMulti && creator.meta.type.isMap) {
|
534 |
|
535 | decorated.multi = decorateMethod((args) => creator.meta.type.asMap.hashers.length === 1
|
536 | ? this._retrieveMulti(args.map((a) => [creator, [a]]))
|
537 | : this._retrieveMulti(args.map((a) => [creator, a])));
|
538 | }
|
539 |
|
540 | return this._decorateFunctionMeta(creator, decorated);
|
541 | }
|
542 | _decorateStorageEntryAt(registry, creator, decorateMethod, blockHash) {
|
543 | const getArgs = (args) => extractStorageArgs(registry, creator, args);
|
544 |
|
545 |
|
546 | const decorated = decorateMethod((...args) => this._rpcCore.state.getStorage(getArgs(args), blockHash));
|
547 | decorated.creator = creator;
|
548 | decorated.hash = decorateMethod((...args) => this._rpcCore.state.getStorageHash(getArgs(args), blockHash));
|
549 | decorated.is = (key) => key.section === creator.section &&
|
550 | key.method === creator.method;
|
551 | decorated.key = (...args) => u8aToHex(compactStripLength(creator(...args))[1]);
|
552 | decorated.keyPrefix = (...keys) => u8aToHex(creator.keyPrefix(...keys));
|
553 | decorated.size = decorateMethod((...args) => this._rpcCore.state.getStorageSize(getArgs(args), blockHash));
|
554 |
|
555 | if (creator.iterKey && creator.meta.type.isMap) {
|
556 | decorated.entries = decorateMethod(memo(this.__internal__instanceId, (...args) => this._retrieveMapEntries(creator, blockHash, args)));
|
557 | decorated.entriesPaged = decorateMethod(memo(this.__internal__instanceId, (opts) => this._retrieveMapEntriesPaged(creator, blockHash, opts)));
|
558 | decorated.keys = decorateMethod(memo(this.__internal__instanceId, (...args) => this._retrieveMapKeys(creator, blockHash, args)));
|
559 | decorated.keysPaged = decorateMethod(memo(this.__internal__instanceId, (opts) => this._retrieveMapKeysPaged(creator, blockHash, opts)));
|
560 | }
|
561 | if (this.supportMulti && creator.meta.type.isMap) {
|
562 |
|
563 | decorated.multi = decorateMethod((args) => creator.meta.type.asMap.hashers.length === 1
|
564 | ? this._retrieveMulti(args.map((a) => [creator, [a]]), blockHash)
|
565 | : this._retrieveMulti(args.map((a) => [creator, a]), blockHash));
|
566 | }
|
567 |
|
568 | return this._decorateFunctionMeta(creator, decorated);
|
569 | }
|
570 | _queueStorage(call, queue) {
|
571 | const query = queue === this.__internal__storageSubQ
|
572 | ? this._rpcCore.state.subscribeStorage
|
573 | : this._rpcCore.state.queryStorageAt;
|
574 | let queueIdx = queue.length - 1;
|
575 | let valueIdx = 0;
|
576 | let valueObs;
|
577 |
|
578 |
|
579 |
|
580 |
|
581 | if (queueIdx === -1 || !queue[queueIdx] || queue[queueIdx][1].length === PAGE_SIZE_Q) {
|
582 | queueIdx++;
|
583 | valueObs = from(
|
584 |
|
585 |
|
586 |
|
587 | new Promise((resolve) => {
|
588 | nextTick(() => {
|
589 |
|
590 |
|
591 |
|
592 | const calls = queue[queueIdx][1];
|
593 | delete queue[queueIdx];
|
594 | resolve(calls);
|
595 | });
|
596 | })).pipe(switchMap((calls) => query(calls)));
|
597 | queue.push([valueObs, [call]]);
|
598 | }
|
599 | else {
|
600 | valueObs = queue[queueIdx][0];
|
601 | valueIdx = queue[queueIdx][1].length;
|
602 | queue[queueIdx][1].push(call);
|
603 | }
|
604 | return valueObs.pipe(
|
605 |
|
606 | map((values) => values[valueIdx]));
|
607 | }
|
608 |
|
609 |
|
610 | _decorateStorageCall(creator, decorateMethod) {
|
611 | const memoed = memo(this.__internal__instanceId, (...args) => {
|
612 | const call = extractStorageArgs(this.__internal__registry, creator, args);
|
613 | if (!this.hasSubscriptions) {
|
614 | return this._rpcCore.state.getStorage(call);
|
615 | }
|
616 | return this._queueStorage(call, this.__internal__storageSubQ);
|
617 | });
|
618 | return decorateMethod(memoed, {
|
619 | methodName: creator.method,
|
620 | overrideNoSub: (...args) => this._queueStorage(extractStorageArgs(this.__internal__registry, creator, args), this.__internal__storageGetQ)
|
621 | });
|
622 | }
|
623 |
|
624 | _retrieveMulti(keys, blockHash) {
|
625 | if (!keys.length) {
|
626 | return of([]);
|
627 | }
|
628 | const query = this.hasSubscriptions && !blockHash
|
629 | ? this._rpcCore.state.subscribeStorage
|
630 | : this._rpcCore.state.queryStorageAt;
|
631 | if (keys.length <= PAGE_SIZE_V) {
|
632 | return blockHash
|
633 | ? query(keys, blockHash)
|
634 | : query(keys);
|
635 | }
|
636 | return combineLatest(arrayChunk(keys, PAGE_SIZE_V).map((k) => blockHash
|
637 | ? query(k, blockHash)
|
638 | : query(k))).pipe(map(arrayFlatten));
|
639 | }
|
640 | _retrieveMapKeys({ iterKey, meta, method, section }, at, args) {
|
641 | if (!iterKey || !meta.type.isMap) {
|
642 | throw new Error('keys can only be retrieved on maps');
|
643 | }
|
644 | const headKey = iterKey(...args).toHex();
|
645 | const startSubject = new BehaviorSubject(headKey);
|
646 | const query = at
|
647 | ? (startKey) => this._rpcCore.state.getKeysPaged(headKey, PAGE_SIZE_K, startKey, at)
|
648 | : (startKey) => this._rpcCore.state.getKeysPaged(headKey, PAGE_SIZE_K, startKey);
|
649 | const setMeta = (key) => key.setMeta(meta, section, method);
|
650 | return startSubject.pipe(switchMap(query), map((keys) => keys.map(setMeta)), tap((keys) => nextTick(() => {
|
651 | keys.length === PAGE_SIZE_K
|
652 | ? startSubject.next(keys[PAGE_SIZE_K - 1].toHex())
|
653 | : startSubject.complete();
|
654 | })), toArray(),
|
655 | map(arrayFlatten));
|
656 | }
|
657 | _retrieveMapKeysPaged({ iterKey, meta, method, section }, at, opts) {
|
658 | if (!iterKey || !meta.type.isMap) {
|
659 | throw new Error('keys can only be retrieved on maps');
|
660 | }
|
661 | const setMeta = (key) => key.setMeta(meta, section, method);
|
662 | const query = at
|
663 | ? (headKey) => this._rpcCore.state.getKeysPaged(headKey, opts.pageSize, opts.startKey || headKey, at)
|
664 | : (headKey) => this._rpcCore.state.getKeysPaged(headKey, opts.pageSize, opts.startKey || headKey);
|
665 | return query(iterKey(...opts.args).toHex()).pipe(map((keys) => keys.map(setMeta)));
|
666 | }
|
667 | _retrieveMapEntries(entry, at, args) {
|
668 | const query = at
|
669 | ? (keys) => this._rpcCore.state.queryStorageAt(keys, at)
|
670 | : (keys) => this._rpcCore.state.queryStorageAt(keys);
|
671 | return this._retrieveMapKeys(entry, at, args).pipe(switchMap((keys) => keys.length
|
672 | ? combineLatest(arrayChunk(keys, PAGE_SIZE_V).map(query)).pipe(map((valsArr) => arrayFlatten(valsArr).map((value, index) => [keys[index], value])))
|
673 | : of([])));
|
674 | }
|
675 | _retrieveMapEntriesPaged(entry, at, opts) {
|
676 | const query = at
|
677 | ? (keys) => this._rpcCore.state.queryStorageAt(keys, at)
|
678 | : (keys) => this._rpcCore.state.queryStorageAt(keys);
|
679 | return this._retrieveMapKeysPaged(entry, at, opts).pipe(switchMap((keys) => keys.length
|
680 | ? query(keys).pipe(map((valsArr) => valsArr.map((value, index) => [keys[index], value])))
|
681 | : of([])));
|
682 | }
|
683 | _decorateDeriveRx(decorateMethod) {
|
684 | const specName = this._runtimeVersion?.specName.toString();
|
685 |
|
686 | const available = getAvailableDerives(this.__internal__instanceId, this._rx, objectSpread({}, this._options.derives, this._options.typesBundle?.spec?.[specName || '']?.derives));
|
687 | return decorateDeriveSections(decorateMethod, available);
|
688 | }
|
689 | _decorateDerive(decorateMethod) {
|
690 | return decorateDeriveSections(decorateMethod, this._rx.derive);
|
691 | }
|
692 | |
693 |
|
694 |
|
695 |
|
696 | _rxDecorateMethod = (method) => {
|
697 | return method;
|
698 | };
|
699 | }
|