UNPKG

37.1 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.Decorate = void 0;
4const rxjs_1 = require("rxjs");
5const api_derive_1 = require("@polkadot/api-derive");
6const rpc_core_1 = require("@polkadot/rpc-core");
7const rpc_provider_1 = require("@polkadot/rpc-provider");
8const types_1 = require("@polkadot/types");
9const types_known_1 = require("@polkadot/types-known");
10const util_1 = require("@polkadot/util");
11const util_crypto_1 = require("@polkadot/util-crypto");
12const index_js_1 = require("../submittable/index.js");
13const augmentObject_js_1 = require("../util/augmentObject.js");
14const decorate_js_1 = require("../util/decorate.js");
15const validate_js_1 = require("../util/validate.js");
16const Events_js_1 = require("./Events.js");
17const find_js_1 = require("./find.js");
18const PAGE_SIZE_K = 1000; // limit aligned with the 1k on the node (trie lookups are heavy)
19const PAGE_SIZE_V = 250; // limited since the data may be > 16MB (e.g. misfiring elections)
20const PAGE_SIZE_Q = 50; // queue of pending storage queries (mapped together, next tick)
21const l = (0, util_1.logger)('api/init');
22let instanceCounter = 0;
23function getAtQueryFn(api, { method, section }) {
24 return (0, util_1.assertReturn)(api.rx.query[section] && api.rx.query[section][method], () => `query.${section}.${method} is not available in this version of the metadata`);
25}
26class Decorate extends Events_js_1.Events {
27 __internal__instanceId;
28 __internal__runtimeLog = {};
29 __internal__registry;
30 __internal__storageGetQ = [];
31 __internal__storageSubQ = [];
32 // HACK Use BN import so decorateDerive works... yes, wtf.
33 __phantom = new util_1.BN(0);
34 _type;
35 _call = {};
36 _consts = {};
37 _derive;
38 _errors = {};
39 _events = {};
40 _extrinsics;
41 _extrinsicType = types_1.GenericExtrinsic.LATEST_EXTRINSIC_VERSION;
42 _genesisHash;
43 _isConnected;
44 _isReady = false;
45 _query = {};
46 _queryMulti;
47 _rpc;
48 _rpcCore;
49 _runtimeMap = {};
50 _runtimeChain;
51 _runtimeMetadata;
52 _runtimeVersion;
53 _rx = { call: {}, consts: {}, query: {}, tx: {} };
54 _options;
55 /**
56 * This is the one and only method concrete children classes need to implement.
57 * It's a higher-order function, which takes one argument
58 * `method: Method extends (...args: any[]) => Observable<any>`
59 * (and one optional `options`), and should return the user facing method.
60 * For example:
61 * - For ApiRx, `decorateMethod` should just be identity, because the input
62 * function is already an Observable
63 * - For ApiPromise, `decorateMethod` should return a function that takes all
64 * the parameters from `method`, adds an optional `callback` argument, and
65 * returns a Promise.
66 *
67 * We could easily imagine other user-facing interfaces, which are simply
68 * implemented by transforming the Observable to Stream/Iterator/Kefir/Bacon
69 * via `decorateMethod`.
70 */
71 _decorateMethod;
72 /**
73 * @description Create an instance of the class
74 *
75 * @param options Options object to create API instance or a Provider instance
76 *
77 * @example
78 * <BR>
79 *
80 * ```javascript
81 * import Api from '@polkadot/api/promise';
82 *
83 * const api = new Api().isReady();
84 *
85 * api.rpc.subscribeNewHeads((header) => {
86 * console.log(`new block #${header.number.toNumber()}`);
87 * });
88 * ```
89 */
90 constructor(options, type, decorateMethod) {
91 super();
92 this.__internal__instanceId = `${++instanceCounter}`;
93 this.__internal__registry = options.source?.registry || options.registry || new types_1.TypeRegistry();
94 this._rx.callAt = (blockHash, knownVersion) => (0, rxjs_1.from)(this.at(blockHash, knownVersion)).pipe((0, rxjs_1.map)((a) => a.rx.call));
95 this._rx.queryAt = (blockHash, knownVersion) => (0, rxjs_1.from)(this.at(blockHash, knownVersion)).pipe((0, rxjs_1.map)((a) => a.rx.query));
96 this._rx.registry = this.__internal__registry;
97 this._decorateMethod = decorateMethod;
98 this._options = options;
99 this._type = type;
100 const provider = options.source
101 ? options.source._rpcCore.provider.isClonable
102 ? options.source._rpcCore.provider.clone()
103 : options.source._rpcCore.provider
104 : (options.provider || new rpc_provider_1.WsProvider());
105 // The RPC interface decorates the known interfaces on init
106 this._rpcCore = new rpc_core_1.RpcCore(this.__internal__instanceId, this.__internal__registry, {
107 isPedantic: this._options.isPedantic,
108 provider,
109 userRpc: this._options.rpc
110 });
111 this._isConnected = new rxjs_1.BehaviorSubject(this._rpcCore.provider.isConnected);
112 this._rx.hasSubscriptions = this._rpcCore.provider.hasSubscriptions;
113 }
114 /**
115 * @description Return the current used registry
116 */
117 get registry() {
118 return this.__internal__registry;
119 }
120 /**
121 * @description Creates an instance of a type as registered
122 */
123 createType(type, ...params) {
124 return this.__internal__registry.createType(type, ...params);
125 }
126 /**
127 * @description Register additional user-defined of chain-specific types in the type registry
128 */
129 registerTypes(types) {
130 types && this.__internal__registry.register(types);
131 }
132 /**
133 * @returns `true` if the API operates with subscriptions
134 */
135 get hasSubscriptions() {
136 return this._rpcCore.provider.hasSubscriptions;
137 }
138 /**
139 * @returns `true` if the API decorate multi-key queries
140 */
141 get supportMulti() {
142 return this._rpcCore.provider.hasSubscriptions || !!this._rpcCore.state.queryStorageAt;
143 }
144 _emptyDecorated(registry, blockHash) {
145 return {
146 call: {},
147 consts: {},
148 errors: {},
149 events: {},
150 query: {},
151 registry,
152 rx: {
153 call: {},
154 query: {}
155 },
156 tx: (0, index_js_1.createSubmittable)(this._type, this._rx, this._decorateMethod, registry, blockHash)
157 };
158 }
159 _createDecorated(registry, fromEmpty, decoratedApi, blockHash) {
160 if (!decoratedApi) {
161 decoratedApi = this._emptyDecorated(registry.registry, blockHash);
162 }
163 if (fromEmpty || !registry.decoratedMeta) {
164 registry.decoratedMeta = (0, types_1.expandMetadata)(registry.registry, registry.metadata);
165 }
166 const runtime = this._decorateCalls(registry, this._decorateMethod, blockHash);
167 const runtimeRx = this._decorateCalls(registry, this._rxDecorateMethod, blockHash);
168 const storage = this._decorateStorage(registry.decoratedMeta, this._decorateMethod, blockHash);
169 const storageRx = this._decorateStorage(registry.decoratedMeta, this._rxDecorateMethod, blockHash);
170 (0, augmentObject_js_1.augmentObject)('consts', registry.decoratedMeta.consts, decoratedApi.consts, fromEmpty);
171 (0, augmentObject_js_1.augmentObject)('errors', registry.decoratedMeta.errors, decoratedApi.errors, fromEmpty);
172 (0, augmentObject_js_1.augmentObject)('events', registry.decoratedMeta.events, decoratedApi.events, fromEmpty);
173 (0, augmentObject_js_1.augmentObject)('query', storage, decoratedApi.query, fromEmpty);
174 (0, augmentObject_js_1.augmentObject)('query', storageRx, decoratedApi.rx.query, fromEmpty);
175 (0, augmentObject_js_1.augmentObject)('call', runtime, decoratedApi.call, fromEmpty);
176 (0, augmentObject_js_1.augmentObject)('call', runtimeRx, decoratedApi.rx.call, fromEmpty);
177 decoratedApi.findCall = (callIndex) => (0, find_js_1.findCall)(registry.registry, callIndex);
178 decoratedApi.findError = (errorIndex) => (0, find_js_1.findError)(registry.registry, errorIndex);
179 decoratedApi.queryMulti = blockHash
180 ? this._decorateMultiAt(decoratedApi, this._decorateMethod, blockHash)
181 : this._decorateMulti(this._decorateMethod);
182 decoratedApi.runtimeVersion = registry.runtimeVersion;
183 return {
184 createdAt: blockHash,
185 decoratedApi,
186 decoratedMeta: registry.decoratedMeta
187 };
188 }
189 _injectMetadata(registry, fromEmpty = false) {
190 // clear the decoration, we are redoing it here
191 if (fromEmpty || !registry.decoratedApi) {
192 registry.decoratedApi = this._emptyDecorated(registry.registry);
193 }
194 const { decoratedApi, decoratedMeta } = this._createDecorated(registry, fromEmpty, registry.decoratedApi);
195 this._call = decoratedApi.call;
196 this._consts = decoratedApi.consts;
197 this._errors = decoratedApi.errors;
198 this._events = decoratedApi.events;
199 this._query = decoratedApi.query;
200 this._rx.call = decoratedApi.rx.call;
201 this._rx.query = decoratedApi.rx.query;
202 const tx = this._decorateExtrinsics(decoratedMeta, this._decorateMethod);
203 const rxtx = this._decorateExtrinsics(decoratedMeta, this._rxDecorateMethod);
204 if (fromEmpty || !this._extrinsics) {
205 this._extrinsics = tx;
206 this._rx.tx = rxtx;
207 }
208 else {
209 (0, augmentObject_js_1.augmentObject)('tx', tx, this._extrinsics, false);
210 (0, augmentObject_js_1.augmentObject)(null, rxtx, this._rx.tx, false);
211 }
212 (0, augmentObject_js_1.augmentObject)(null, decoratedMeta.consts, this._rx.consts, fromEmpty);
213 this.emit('decorated');
214 }
215 /**
216 * @deprecated
217 * backwards compatible endpoint for metadata injection, may be removed in the future (However, it is still useful for testing injection)
218 */
219 injectMetadata(metadata, fromEmpty, registry) {
220 this._injectMetadata({ counter: 0, metadata, registry: registry || this.__internal__registry, runtimeVersion: this.__internal__registry.createType('RuntimeVersionPartial') }, fromEmpty);
221 }
222 _decorateFunctionMeta(input, output) {
223 output.meta = input.meta;
224 output.method = input.method;
225 output.section = input.section;
226 output.toJSON = input.toJSON;
227 if (input.callIndex) {
228 output.callIndex = input.callIndex;
229 }
230 return output;
231 }
232 // Filter all RPC methods based on the results of the rpc_methods call. We do this in the following
233 // manner to cater for both old and new:
234 // - when the number of entries are 0, only remove the ones with isOptional (account & contracts)
235 // - when non-zero, remove anything that is not in the array (we don't do this)
236 _filterRpc(methods, additional) {
237 // add any specific user-base RPCs
238 if (Object.keys(additional).length !== 0) {
239 this._rpcCore.addUserInterfaces(additional);
240 // re-decorate, only adding any new additional interfaces
241 this._decorateRpc(this._rpcCore, this._decorateMethod, this._rpc);
242 this._decorateRpc(this._rpcCore, this._rxDecorateMethod, this._rx.rpc);
243 }
244 // extract the actual sections from the methods (this is useful when
245 // we try and create mappings to runtime names via a hash mapping)
246 const sectionMap = {};
247 for (let i = 0, count = methods.length; i < count; i++) {
248 const [section] = methods[i].split('_');
249 sectionMap[section] = true;
250 }
251 // convert the actual section names into an easy name lookup
252 const sections = Object.keys(sectionMap);
253 for (let i = 0, count = sections.length; i < count; i++) {
254 const nameA = (0, util_1.stringUpperFirst)(sections[i]);
255 const nameB = `${nameA}Api`;
256 this._runtimeMap[(0, util_crypto_1.blake2AsHex)(nameA, 64)] = nameA;
257 this._runtimeMap[(0, util_crypto_1.blake2AsHex)(nameB, 64)] = nameB;
258 }
259 // finally we filter the actual methods to expose
260 this._filterRpcMethods(methods);
261 }
262 _filterRpcMethods(exposed) {
263 const hasResults = exposed.length !== 0;
264 const allKnown = [...this._rpcCore.mapping.entries()];
265 const allKeys = [];
266 const count = allKnown.length;
267 for (let i = 0; i < count; i++) {
268 const [, { alias, endpoint, method, pubsub, section }] = allKnown[i];
269 allKeys.push(`${section}_${method}`);
270 if (pubsub) {
271 allKeys.push(`${section}_${pubsub[1]}`);
272 allKeys.push(`${section}_${pubsub[2]}`);
273 }
274 if (alias) {
275 allKeys.push(...alias);
276 }
277 if (endpoint) {
278 allKeys.push(endpoint);
279 }
280 }
281 const unknown = exposed.filter((k) => !allKeys.includes(k) &&
282 !k.includes('_unstable_'));
283 if (unknown.length && !this._options.noInitWarn) {
284 l.warn(`RPC methods not decorated: ${unknown.join(', ')}`);
285 }
286 // loop through all entries we have (populated in decorate) and filter as required
287 // only remove when we have results and method missing, or with no results if optional
288 for (let i = 0; i < count; i++) {
289 const [k, { method, section }] = allKnown[i];
290 if (hasResults && !exposed.includes(k) && k !== 'rpc_methods') {
291 if (this._rpc[section]) {
292 delete this._rpc[section][method];
293 delete this._rx.rpc[section][method];
294 }
295 }
296 }
297 }
298 _rpcSubmitter(decorateMethod) {
299 const method = (method, ...params) => {
300 return (0, rxjs_1.from)(this._rpcCore.provider.send(method, params));
301 };
302 return decorateMethod(method);
303 }
304 _decorateRpc(rpc, decorateMethod, input = this._rpcSubmitter(decorateMethod)) {
305 const out = input;
306 const decorateFn = (section, method) => {
307 const source = rpc[section][method];
308 const fn = decorateMethod(source, { methodName: method });
309 fn.meta = source.meta;
310 fn.raw = decorateMethod(source.raw, { methodName: method });
311 return fn;
312 };
313 for (let s = 0, scount = rpc.sections.length; s < scount; s++) {
314 const section = rpc.sections[s];
315 if (!Object.prototype.hasOwnProperty.call(out, section)) {
316 const methods = Object.keys(rpc[section]);
317 const decorateInternal = (method) => decorateFn(section, method);
318 for (let m = 0, mcount = methods.length; m < mcount; m++) {
319 const method = methods[m];
320 // skip subscriptions where we have a non-subscribe interface
321 if (this.hasSubscriptions || !(method.startsWith('subscribe') || method.startsWith('unsubscribe'))) {
322 if (!Object.prototype.hasOwnProperty.call(out, section)) {
323 out[section] = {};
324 }
325 (0, util_1.lazyMethod)(out[section], method, decorateInternal);
326 }
327 }
328 }
329 }
330 return out;
331 }
332 // add all definition entries
333 _addRuntimeDef(result, additional) {
334 if (!additional) {
335 return;
336 }
337 const entries = Object.entries(additional);
338 for (let j = 0, ecount = entries.length; j < ecount; j++) {
339 const [key, defs] = entries[j];
340 if (result[key]) {
341 // we have this one already, step through for new versions or
342 // new methods and add those as applicable
343 for (let k = 0, dcount = defs.length; k < dcount; k++) {
344 const def = defs[k];
345 const prev = result[key].find(({ version }) => def.version === version);
346 if (prev) {
347 // interleave the new methods with the old - last definition wins
348 (0, util_1.objectSpread)(prev.methods, def.methods);
349 }
350 else {
351 // we don't have this specific version, add it
352 result[key].push(def);
353 }
354 }
355 }
356 else {
357 // we don't have this runtime definition, add it as-is
358 result[key] = defs;
359 }
360 }
361 }
362 // extract all runtime definitions
363 _getRuntimeDefs(registry, specName, chain = '') {
364 const result = {};
365 const defValues = Object.values(types_1.typeDefinitions);
366 // options > chain/spec > built-in, apply in reverse order with
367 // methods overriding previous definitions (or interleave missing)
368 for (let i = 0, count = defValues.length; i < count; i++) {
369 this._addRuntimeDef(result, defValues[i].runtime);
370 }
371 this._addRuntimeDef(result, (0, types_known_1.getSpecRuntime)(registry, chain, specName));
372 this._addRuntimeDef(result, this._options.runtime);
373 return Object.entries(result);
374 }
375 // pre-metadata decoration
376 _decorateCalls({ registry, runtimeVersion: { apis, specName, specVersion } }, decorateMethod, blockHash) {
377 const result = {};
378 const named = {};
379 const hashes = {};
380 const sections = this._getRuntimeDefs(registry, specName, this._runtimeChain);
381 const older = [];
382 const implName = `${specName.toString()}/${specVersion.toString()}`;
383 const hasLogged = this.__internal__runtimeLog[implName] || false;
384 this.__internal__runtimeLog[implName] = true;
385 for (let i = 0, scount = sections.length; i < scount; i++) {
386 const [_section, secs] = sections[i];
387 const sectionHash = (0, util_crypto_1.blake2AsHex)(_section, 64);
388 const rtApi = apis.find(([a]) => a.eq(sectionHash));
389 hashes[sectionHash] = true;
390 if (rtApi) {
391 const all = secs.map(({ version }) => version).sort();
392 const sec = secs.find(({ version }) => rtApi[1].eq(version));
393 if (sec) {
394 const section = (0, util_1.stringCamelCase)(_section);
395 const methods = Object.entries(sec.methods);
396 if (methods.length) {
397 if (!named[section]) {
398 named[section] = {};
399 }
400 for (let m = 0, mcount = methods.length; m < mcount; m++) {
401 const [_method, def] = methods[m];
402 const method = (0, util_1.stringCamelCase)(_method);
403 named[section][method] = (0, util_1.objectSpread)({ method, name: `${_section}_${_method}`, section, sectionHash }, def);
404 }
405 }
406 }
407 else {
408 older.push(`${_section}/${rtApi[1].toString()} (${all.join('/')} known)`);
409 }
410 }
411 }
412 // find the runtimes that we don't have hashes for
413 const notFound = apis
414 .map(([a, v]) => [a.toHex(), v.toString()])
415 .filter(([a]) => !hashes[a])
416 .map(([a, v]) => `${this._runtimeMap[a] || a}/${v}`);
417 if (!this._options.noInitWarn && !hasLogged) {
418 if (older.length) {
419 l.warn(`${implName}: Not decorating runtime apis without matching versions: ${older.join(', ')}`);
420 }
421 if (notFound.length) {
422 l.warn(`${implName}: Not decorating unknown runtime apis: ${notFound.join(', ')}`);
423 }
424 }
425 const stateCall = blockHash
426 ? (name, bytes) => this._rpcCore.state.call(name, bytes, blockHash)
427 : (name, bytes) => this._rpcCore.state.call(name, bytes);
428 const lazySection = (section) => (0, util_1.lazyMethods)({}, Object.keys(named[section]), (method) => this._decorateCall(registry, named[section][method], stateCall, decorateMethod));
429 const modules = Object.keys(named);
430 for (let i = 0, count = modules.length; i < count; i++) {
431 (0, util_1.lazyMethod)(result, modules[i], lazySection);
432 }
433 return result;
434 }
435 _decorateCall(registry, def, stateCall, decorateMethod) {
436 // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
437 const decorated = decorateMethod((...args) => {
438 if (args.length !== def.params.length) {
439 throw new Error(`${def.name}:: Expected ${def.params.length} arguments, found ${args.length}`);
440 }
441 const bytes = registry.createType('Raw', (0, util_1.u8aConcatStrict)(args.map((a, i) => registry.createTypeUnsafe(def.params[i].type, [a]).toU8a())));
442 return stateCall(def.name, bytes).pipe((0, rxjs_1.map)((r) => registry.createTypeUnsafe(def.type, [r])));
443 });
444 decorated.meta = def;
445 // eslint-disable-next-line @typescript-eslint/no-unsafe-return
446 return decorated;
447 }
448 // only be called if supportMulti is true
449 _decorateMulti(decorateMethod) {
450 // eslint-disable-next-line @typescript-eslint/no-unsafe-return
451 return decorateMethod((keys) => keys.length
452 ? (this.hasSubscriptions
453 ? this._rpcCore.state.subscribeStorage
454 : this._rpcCore.state.queryStorageAt)(keys.map((args) => Array.isArray(args)
455 ? args[0].creator.meta.type.isPlain
456 ? [args[0].creator]
457 : args[0].creator.meta.type.asMap.hashers.length === 1
458 ? [args[0].creator, args.slice(1)]
459 : [args[0].creator, ...args.slice(1)]
460 : [args.creator]))
461 : (0, rxjs_1.of)([]));
462 }
463 _decorateMultiAt(atApi, decorateMethod, blockHash) {
464 // eslint-disable-next-line @typescript-eslint/no-unsafe-return
465 return decorateMethod((calls) => calls.length
466 ? this._rpcCore.state.queryStorageAt(calls.map((args) => {
467 if (Array.isArray(args)) {
468 const { creator } = getAtQueryFn(atApi, args[0].creator);
469 return creator.meta.type.isPlain
470 ? [creator]
471 : creator.meta.type.asMap.hashers.length === 1
472 ? [creator, args.slice(1)]
473 : [creator, ...args.slice(1)];
474 }
475 return [getAtQueryFn(atApi, args.creator).creator];
476 }), blockHash)
477 : (0, rxjs_1.of)([]));
478 }
479 _decorateExtrinsics({ tx }, decorateMethod) {
480 const result = (0, index_js_1.createSubmittable)(this._type, this._rx, decorateMethod);
481 const lazySection = (section) => (0, util_1.lazyMethods)({}, Object.keys(tx[section]), (method) => method.startsWith('$')
482 ? tx[section][method]
483 : this._decorateExtrinsicEntry(tx[section][method], result));
484 const sections = Object.keys(tx);
485 for (let i = 0, count = sections.length; i < count; i++) {
486 (0, util_1.lazyMethod)(result, sections[i], lazySection);
487 }
488 return result;
489 }
490 _decorateExtrinsicEntry(method, creator) {
491 const decorated = (...params) => creator(method(...params));
492 // pass through the `.is`
493 decorated.is = (other) => method.is(other);
494 // eslint-disable-next-line @typescript-eslint/no-unsafe-return
495 return this._decorateFunctionMeta(method, decorated);
496 }
497 _decorateStorage({ query, registry }, decorateMethod, blockHash) {
498 const result = {};
499 const lazySection = (section) => (0, util_1.lazyMethods)({}, Object.keys(query[section]), (method) => blockHash
500 ? this._decorateStorageEntryAt(registry, query[section][method], decorateMethod, blockHash)
501 : this._decorateStorageEntry(query[section][method], decorateMethod));
502 const sections = Object.keys(query);
503 for (let i = 0, count = sections.length; i < count; i++) {
504 (0, util_1.lazyMethod)(result, sections[i], lazySection);
505 }
506 return result;
507 }
508 _decorateStorageEntry(creator, decorateMethod) {
509 const getArgs = (args, registry) => (0, validate_js_1.extractStorageArgs)(registry || this.__internal__registry, creator, args);
510 const getQueryAt = (blockHash) => (0, rxjs_1.from)(this.at(blockHash)).pipe((0, rxjs_1.map)((api) => getAtQueryFn(api, creator)));
511 // Disable this where it occurs for each field we are decorating
512 /* eslint-disable @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-assignment */
513 const decorated = this._decorateStorageCall(creator, decorateMethod);
514 decorated.creator = creator;
515 // eslint-disable-next-line deprecation/deprecation
516 decorated.at = decorateMethod((blockHash, ...args) => getQueryAt(blockHash).pipe((0, rxjs_1.switchMap)((q) => q(...args))));
517 decorated.hash = decorateMethod((...args) => this._rpcCore.state.getStorageHash(getArgs(args)));
518 decorated.is = (key) => key.section === creator.section &&
519 key.method === creator.method;
520 decorated.key = (...args) => (0, util_1.u8aToHex)((0, util_1.compactStripLength)(creator(...args))[1]);
521 decorated.keyPrefix = (...args) => (0, util_1.u8aToHex)(creator.keyPrefix(...args));
522 decorated.size = decorateMethod((...args) => this._rpcCore.state.getStorageSize(getArgs(args)));
523 // eslint-disable-next-line deprecation/deprecation
524 decorated.sizeAt = decorateMethod((blockHash, ...args) => getQueryAt(blockHash).pipe((0, rxjs_1.switchMap)((q) => this._rpcCore.state.getStorageSize(getArgs(args, q.creator.meta.registry), blockHash))));
525 // .keys() & .entries() only available on map types
526 if (creator.iterKey && creator.meta.type.isMap) {
527 decorated.entries = decorateMethod((0, rpc_core_1.memo)(this.__internal__instanceId, (...args) => this._retrieveMapEntries(creator, null, args)));
528 // eslint-disable-next-line deprecation/deprecation
529 decorated.entriesAt = decorateMethod((0, rpc_core_1.memo)(this.__internal__instanceId, (blockHash, ...args) => getQueryAt(blockHash).pipe((0, rxjs_1.switchMap)((q) => this._retrieveMapEntries(q.creator, blockHash, args)))));
530 decorated.entriesPaged = decorateMethod((0, rpc_core_1.memo)(this.__internal__instanceId, (opts) => this._retrieveMapEntriesPaged(creator, undefined, opts)));
531 decorated.keys = decorateMethod((0, rpc_core_1.memo)(this.__internal__instanceId, (...args) => this._retrieveMapKeys(creator, null, args)));
532 // eslint-disable-next-line deprecation/deprecation
533 decorated.keysAt = decorateMethod((0, rpc_core_1.memo)(this.__internal__instanceId, (blockHash, ...args) => getQueryAt(blockHash).pipe((0, rxjs_1.switchMap)((q) => this._retrieveMapKeys(q.creator, blockHash, args)))));
534 decorated.keysPaged = decorateMethod((0, rpc_core_1.memo)(this.__internal__instanceId, (opts) => this._retrieveMapKeysPaged(creator, undefined, opts)));
535 }
536 if (this.supportMulti && creator.meta.type.isMap) {
537 // When using double map storage function, user need to pass double map key as an array
538 decorated.multi = decorateMethod((args) => creator.meta.type.asMap.hashers.length === 1
539 ? this._retrieveMulti(args.map((a) => [creator, [a]]))
540 : this._retrieveMulti(args.map((a) => [creator, a])));
541 }
542 /* eslint-enable @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-assignment */
543 return this._decorateFunctionMeta(creator, decorated);
544 }
545 _decorateStorageEntryAt(registry, creator, decorateMethod, blockHash) {
546 const getArgs = (args) => (0, validate_js_1.extractStorageArgs)(registry, creator, args);
547 // Disable this where it occurs for each field we are decorating
548 /* eslint-disable @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-assignment */
549 const decorated = decorateMethod((...args) => this._rpcCore.state.getStorage(getArgs(args), blockHash));
550 decorated.creator = creator;
551 decorated.hash = decorateMethod((...args) => this._rpcCore.state.getStorageHash(getArgs(args), blockHash));
552 decorated.is = (key) => key.section === creator.section &&
553 key.method === creator.method;
554 decorated.key = (...args) => (0, util_1.u8aToHex)((0, util_1.compactStripLength)(creator(...args))[1]);
555 decorated.keyPrefix = (...keys) => (0, util_1.u8aToHex)(creator.keyPrefix(...keys));
556 decorated.size = decorateMethod((...args) => this._rpcCore.state.getStorageSize(getArgs(args), blockHash));
557 // .keys() & .entries() only available on map types
558 if (creator.iterKey && creator.meta.type.isMap) {
559 decorated.entries = decorateMethod((0, rpc_core_1.memo)(this.__internal__instanceId, (...args) => this._retrieveMapEntries(creator, blockHash, args)));
560 decorated.entriesPaged = decorateMethod((0, rpc_core_1.memo)(this.__internal__instanceId, (opts) => this._retrieveMapEntriesPaged(creator, blockHash, opts)));
561 decorated.keys = decorateMethod((0, rpc_core_1.memo)(this.__internal__instanceId, (...args) => this._retrieveMapKeys(creator, blockHash, args)));
562 decorated.keysPaged = decorateMethod((0, rpc_core_1.memo)(this.__internal__instanceId, (opts) => this._retrieveMapKeysPaged(creator, blockHash, opts)));
563 }
564 if (this.supportMulti && creator.meta.type.isMap) {
565 // When using double map storage function, user need to pass double map key as an array
566 decorated.multi = decorateMethod((args) => creator.meta.type.asMap.hashers.length === 1
567 ? this._retrieveMulti(args.map((a) => [creator, [a]]), blockHash)
568 : this._retrieveMulti(args.map((a) => [creator, a]), blockHash));
569 }
570 /* eslint-enable @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-assignment */
571 return this._decorateFunctionMeta(creator, decorated);
572 }
573 _queueStorage(call, queue) {
574 const query = queue === this.__internal__storageSubQ
575 ? this._rpcCore.state.subscribeStorage
576 : this._rpcCore.state.queryStorageAt;
577 let queueIdx = queue.length - 1;
578 let valueIdx = 0;
579 let valueObs;
580 // if we don't have queue entries yet,
581 // or the current queue has fired (see from below),
582 // or the current queue has the max entries,
583 // then we create a new queue
584 if (queueIdx === -1 || !queue[queueIdx] || queue[queueIdx][1].length === PAGE_SIZE_Q) {
585 queueIdx++;
586 valueObs = (0, rxjs_1.from)(
587 // we delay the execution until the next tick, this allows
588 // any queries made in this timeframe to be added to the same
589 // queue for a single query
590 new Promise((resolve) => {
591 (0, util_1.nextTick)(() => {
592 // get all the calls in this instance, resolve with it
593 // and then clear the queue so we don't add more
594 // (anything after this will be added to a new queue)
595 const calls = queue[queueIdx][1];
596 delete queue[queueIdx];
597 resolve(calls);
598 });
599 })).pipe((0, rxjs_1.switchMap)((calls) => query(calls)));
600 queue.push([valueObs, [call]]);
601 }
602 else {
603 valueObs = queue[queueIdx][0];
604 valueIdx = queue[queueIdx][1].length;
605 queue[queueIdx][1].push(call);
606 }
607 return valueObs.pipe(
608 // return the single value at this index
609 (0, rxjs_1.map)((values) => values[valueIdx]));
610 }
611 // Decorate the base storage call. In the case or rxjs or promise-without-callback (await)
612 // we make a subscription, alternatively we push this through a single-shot query
613 _decorateStorageCall(creator, decorateMethod) {
614 const memoed = (0, rpc_core_1.memo)(this.__internal__instanceId, (...args) => {
615 const call = (0, validate_js_1.extractStorageArgs)(this.__internal__registry, creator, args);
616 if (!this.hasSubscriptions) {
617 return this._rpcCore.state.getStorage(call);
618 }
619 return this._queueStorage(call, this.__internal__storageSubQ);
620 });
621 return decorateMethod(memoed, {
622 methodName: creator.method,
623 overrideNoSub: (...args) => this._queueStorage((0, validate_js_1.extractStorageArgs)(this.__internal__registry, creator, args), this.__internal__storageGetQ)
624 });
625 }
626 // retrieve a set of values for a specific set of keys - here we chunk the keys into PAGE_SIZE sizes
627 _retrieveMulti(keys, blockHash) {
628 if (!keys.length) {
629 return (0, rxjs_1.of)([]);
630 }
631 const query = this.hasSubscriptions && !blockHash
632 ? this._rpcCore.state.subscribeStorage
633 : this._rpcCore.state.queryStorageAt;
634 if (keys.length <= PAGE_SIZE_V) {
635 return blockHash
636 ? query(keys, blockHash)
637 : query(keys);
638 }
639 return (0, rxjs_1.combineLatest)((0, util_1.arrayChunk)(keys, PAGE_SIZE_V).map((k) => blockHash
640 ? query(k, blockHash)
641 : query(k))).pipe((0, rxjs_1.map)(util_1.arrayFlatten));
642 }
643 _retrieveMapKeys({ iterKey, meta, method, section }, at, args) {
644 if (!iterKey || !meta.type.isMap) {
645 throw new Error('keys can only be retrieved on maps');
646 }
647 const headKey = iterKey(...args).toHex();
648 const startSubject = new rxjs_1.BehaviorSubject(headKey);
649 const query = at
650 ? (startKey) => this._rpcCore.state.getKeysPaged(headKey, PAGE_SIZE_K, startKey, at)
651 : (startKey) => this._rpcCore.state.getKeysPaged(headKey, PAGE_SIZE_K, startKey);
652 const setMeta = (key) => key.setMeta(meta, section, method);
653 return startSubject.pipe((0, rxjs_1.switchMap)(query), (0, rxjs_1.map)((keys) => keys.map(setMeta)), (0, rxjs_1.tap)((keys) => (0, util_1.nextTick)(() => {
654 keys.length === PAGE_SIZE_K
655 ? startSubject.next(keys[PAGE_SIZE_K - 1].toHex())
656 : startSubject.complete();
657 })), (0, rxjs_1.toArray)(), // toArray since we want to startSubject to be completed
658 (0, rxjs_1.map)(util_1.arrayFlatten));
659 }
660 _retrieveMapKeysPaged({ iterKey, meta, method, section }, at, opts) {
661 if (!iterKey || !meta.type.isMap) {
662 throw new Error('keys can only be retrieved on maps');
663 }
664 const setMeta = (key) => key.setMeta(meta, section, method);
665 const query = at
666 ? (headKey) => this._rpcCore.state.getKeysPaged(headKey, opts.pageSize, opts.startKey || headKey, at)
667 : (headKey) => this._rpcCore.state.getKeysPaged(headKey, opts.pageSize, opts.startKey || headKey);
668 return query(iterKey(...opts.args).toHex()).pipe((0, rxjs_1.map)((keys) => keys.map(setMeta)));
669 }
670 _retrieveMapEntries(entry, at, args) {
671 const query = at
672 ? (keys) => this._rpcCore.state.queryStorageAt(keys, at)
673 : (keys) => this._rpcCore.state.queryStorageAt(keys);
674 return this._retrieveMapKeys(entry, at, args).pipe((0, rxjs_1.switchMap)((keys) => keys.length
675 ? (0, rxjs_1.combineLatest)((0, util_1.arrayChunk)(keys, PAGE_SIZE_V).map(query)).pipe((0, rxjs_1.map)((valsArr) => (0, util_1.arrayFlatten)(valsArr).map((value, index) => [keys[index], value])))
676 : (0, rxjs_1.of)([])));
677 }
678 _retrieveMapEntriesPaged(entry, at, opts) {
679 const query = at
680 ? (keys) => this._rpcCore.state.queryStorageAt(keys, at)
681 : (keys) => this._rpcCore.state.queryStorageAt(keys);
682 return this._retrieveMapKeysPaged(entry, at, opts).pipe((0, rxjs_1.switchMap)((keys) => keys.length
683 ? query(keys).pipe((0, rxjs_1.map)((valsArr) => valsArr.map((value, index) => [keys[index], value])))
684 : (0, rxjs_1.of)([])));
685 }
686 _decorateDeriveRx(decorateMethod) {
687 const specName = this._runtimeVersion?.specName.toString();
688 // Pull in derive from api-derive
689 const available = (0, api_derive_1.getAvailableDerives)(this.__internal__instanceId, this._rx, (0, util_1.objectSpread)({}, this._options.derives, this._options.typesBundle?.spec?.[specName || '']?.derives));
690 return (0, decorate_js_1.decorateDeriveSections)(decorateMethod, available);
691 }
692 _decorateDerive(decorateMethod) {
693 return (0, decorate_js_1.decorateDeriveSections)(decorateMethod, this._rx.derive);
694 }
695 /**
696 * Put the `this.onCall` function of ApiRx here, because it is needed by
697 * `api._rx`.
698 */
699 _rxDecorateMethod = (method) => {
700 return method;
701 };
702}
703exports.Decorate = Decorate;