1 | import { DoNotConstruct, Json, Raw } from '@polkadot/types-codec';
|
2 | import { constructTypeClass, createClassUnsafe, createTypeUnsafe } from '@polkadot/types-create';
|
3 | import { assertReturn, BN_ZERO, formatBalance, isBn, isFunction, isNumber, isString, isU8a, lazyMethod, logger, objectSpread, stringCamelCase, stringify } from '@polkadot/util';
|
4 | import { blake2AsU8a } from '@polkadot/util-crypto';
|
5 | import { expandExtensionTypes, fallbackExtensions, findUnknownExtensions } from '../extrinsic/signedExtensions/index.js';
|
6 | import { GenericEventData } from '../generic/Event.js';
|
7 | import * as baseTypes from '../index.types.js';
|
8 | import * as definitions from '../interfaces/definitions.js';
|
9 | import { createCallFunction } from '../metadata/decorate/extrinsics/index.js';
|
10 | import { decorateConstants, filterCallsSome, filterEventsSome } from '../metadata/decorate/index.js';
|
11 | import { Metadata } from '../metadata/Metadata.js';
|
12 | import { PortableRegistry } from '../metadata/PortableRegistry/index.js';
|
13 | import { lazyVariants } from './lazy.js';
|
14 | const DEFAULT_FIRST_CALL_IDX = new Uint8Array(2);
|
15 | const l = logger('registry');
|
16 | function sortDecimalStrings(a, b) {
|
17 | return parseInt(a, 10) - parseInt(b, 10);
|
18 | }
|
19 | function valueToString(v) {
|
20 | return v.toString();
|
21 | }
|
22 | function getFieldArgs(lookup, fields) {
|
23 | const count = fields.length;
|
24 | const args = new Array(count);
|
25 | for (let i = 0; i < count; i++) {
|
26 | args[i] = lookup.getTypeDef(fields[i].type).type;
|
27 | }
|
28 | return args;
|
29 | }
|
30 | function clearRecord(record) {
|
31 | const keys = Object.keys(record);
|
32 | for (let i = 0, count = keys.length; i < count; i++) {
|
33 | delete record[keys[i]];
|
34 | }
|
35 | }
|
36 | function getVariantStringIdx({ index }) {
|
37 | return index.toString();
|
38 | }
|
39 | function injectErrors(_, { lookup, pallets }, version, result) {
|
40 | clearRecord(result);
|
41 | for (let i = 0, count = pallets.length; i < count; i++) {
|
42 | const { errors, index, name } = pallets[i];
|
43 | if (errors.isSome) {
|
44 | const sectionName = stringCamelCase(name);
|
45 | lazyMethod(result, version >= 12 ? index.toNumber() : i, () => lazyVariants(lookup, errors.unwrap(), getVariantStringIdx, ({ docs, fields, index, name }) => ({
|
46 | args: getFieldArgs(lookup, fields),
|
47 | docs: docs.map(valueToString),
|
48 | fields,
|
49 | index: index.toNumber(),
|
50 | method: name.toString(),
|
51 | name: name.toString(),
|
52 | section: sectionName
|
53 | })));
|
54 | }
|
55 | }
|
56 | }
|
57 | function injectEvents(registry, { lookup, pallets }, version, result) {
|
58 | const filtered = pallets.filter(filterEventsSome);
|
59 | clearRecord(result);
|
60 | for (let i = 0, count = filtered.length; i < count; i++) {
|
61 | const { events, index, name } = filtered[i];
|
62 | lazyMethod(result, version >= 12 ? index.toNumber() : i, () => lazyVariants(lookup, events.unwrap(), getVariantStringIdx, (variant) => {
|
63 | const meta = registry.createType('EventMetadataLatest', objectSpread({}, variant, { args: getFieldArgs(lookup, variant.fields) }));
|
64 | return class extends GenericEventData {
|
65 | constructor(registry, value) {
|
66 | super(registry, value, meta, stringCamelCase(name), variant.name.toString());
|
67 | }
|
68 | };
|
69 | }));
|
70 | }
|
71 | }
|
72 | function injectExtrinsics(registry, { lookup, pallets }, version, result, mapping) {
|
73 | const filtered = pallets.filter(filterCallsSome);
|
74 | clearRecord(result);
|
75 | clearRecord(mapping);
|
76 | for (let i = 0, count = filtered.length; i < count; i++) {
|
77 | const { calls, index, name } = filtered[i];
|
78 | const sectionIndex = version >= 12 ? index.toNumber() : i;
|
79 | const sectionName = stringCamelCase(name);
|
80 | const allCalls = calls.unwrap();
|
81 | lazyMethod(result, sectionIndex, () => lazyVariants(lookup, allCalls, getVariantStringIdx, (variant) => createCallFunction(registry, lookup, variant, sectionName, sectionIndex)));
|
82 | const { path } = registry.lookup.getSiType(allCalls.type);
|
83 |
|
84 | const palletIdx = path.findIndex((v) => v.eq('pallet'));
|
85 | if (palletIdx !== -1) {
|
86 | const name = stringCamelCase(path
|
87 | .slice(0, palletIdx)
|
88 | .map((p, i) => i === 0
|
89 |
|
90 | ? p.replace(/^(frame|pallet)_/, '')
|
91 | : p)
|
92 | .join(' '));
|
93 | if (!mapping[name]) {
|
94 | mapping[name] = [sectionName];
|
95 | }
|
96 | else {
|
97 | mapping[name].push(sectionName);
|
98 | }
|
99 | }
|
100 | }
|
101 | }
|
102 | function extractProperties(registry, metadata) {
|
103 | const original = registry.getChainProperties();
|
104 | const constants = decorateConstants(registry, metadata.asLatest, metadata.version);
|
105 | const ss58Format = constants['system'] && (constants['system']['sS58Prefix'] || constants['system']['ss58Prefix']);
|
106 | if (!ss58Format) {
|
107 | return original;
|
108 | }
|
109 | const { isEthereum, tokenDecimals, tokenSymbol } = original || {};
|
110 | return registry.createTypeUnsafe('ChainProperties', [{ isEthereum, ss58Format, tokenDecimals, tokenSymbol }]);
|
111 | }
|
112 | export class TypeRegistry {
|
113 | __internal__chainProperties;
|
114 | __internal__classes = new Map();
|
115 | __internal__definitions = new Map();
|
116 | __internal__firstCallIndex = null;
|
117 | __internal__hasher = blake2AsU8a;
|
118 | __internal__knownTypes = {};
|
119 | __internal__lookup;
|
120 | __internal__metadata;
|
121 | __internal__metadataVersion = 0;
|
122 | __internal__signedExtensions = fallbackExtensions;
|
123 | __internal__unknownTypes = new Map();
|
124 | __internal__userExtensions;
|
125 | __internal__knownDefaults;
|
126 | __internal__knownDefaultsEntries;
|
127 | __internal__knownDefinitions;
|
128 | __internal__metadataCalls = {};
|
129 | __internal__metadataErrors = {};
|
130 | __internal__metadataEvents = {};
|
131 | __internal__moduleMap = {};
|
132 | createdAtHash;
|
133 | constructor(createdAtHash) {
|
134 | this.__internal__knownDefaults = objectSpread({ Json, Metadata, PortableRegistry, Raw }, baseTypes);
|
135 | this.__internal__knownDefaultsEntries = Object.entries(this.__internal__knownDefaults);
|
136 | this.__internal__knownDefinitions = definitions;
|
137 | const allKnown = Object.values(this.__internal__knownDefinitions);
|
138 | for (let i = 0, count = allKnown.length; i < count; i++) {
|
139 | this.register(allKnown[i].types);
|
140 | }
|
141 | if (createdAtHash) {
|
142 | this.createdAtHash = this.createType('BlockHash', createdAtHash);
|
143 | }
|
144 | }
|
145 | get chainDecimals() {
|
146 | if (this.__internal__chainProperties?.tokenDecimals.isSome) {
|
147 | const allDecimals = this.__internal__chainProperties.tokenDecimals.unwrap();
|
148 | if (allDecimals.length) {
|
149 | return allDecimals.map((b) => b.toNumber());
|
150 | }
|
151 | }
|
152 | return [12];
|
153 | }
|
154 | get chainIsEthereum() {
|
155 | return this.__internal__chainProperties?.isEthereum.isTrue || false;
|
156 | }
|
157 | get chainSS58() {
|
158 | return this.__internal__chainProperties?.ss58Format.isSome
|
159 | ? this.__internal__chainProperties.ss58Format.unwrap().toNumber()
|
160 | : undefined;
|
161 | }
|
162 | get chainTokens() {
|
163 | if (this.__internal__chainProperties?.tokenSymbol.isSome) {
|
164 | const allTokens = this.__internal__chainProperties.tokenSymbol.unwrap();
|
165 | if (allTokens.length) {
|
166 | return allTokens.map(valueToString);
|
167 | }
|
168 | }
|
169 | return [formatBalance.getDefaults().unit];
|
170 | }
|
171 | get firstCallIndex() {
|
172 | return this.__internal__firstCallIndex || DEFAULT_FIRST_CALL_IDX;
|
173 | }
|
174 | |
175 |
|
176 |
|
177 | isLookupType(value) {
|
178 | return /Lookup\d+$/.test(value);
|
179 | }
|
180 | |
181 |
|
182 |
|
183 | createLookupType(lookupId) {
|
184 | return `Lookup${typeof lookupId === 'number' ? lookupId : lookupId.toNumber()}`;
|
185 | }
|
186 | get knownTypes() {
|
187 | return this.__internal__knownTypes;
|
188 | }
|
189 | get lookup() {
|
190 | return assertReturn(this.__internal__lookup, 'PortableRegistry has not been set on this registry');
|
191 | }
|
192 | get metadata() {
|
193 | return assertReturn(this.__internal__metadata, 'Metadata has not been set on this registry');
|
194 | }
|
195 | get unknownTypes() {
|
196 | return [...this.__internal__unknownTypes.keys()];
|
197 | }
|
198 | get signedExtensions() {
|
199 | return this.__internal__signedExtensions;
|
200 | }
|
201 | clearCache() {
|
202 | this.__internal__classes = new Map();
|
203 | }
|
204 | |
205 |
|
206 |
|
207 | createClass(type) {
|
208 | return createClassUnsafe(this, type);
|
209 | }
|
210 | |
211 |
|
212 |
|
213 | createClassUnsafe(type) {
|
214 | return createClassUnsafe(this, type);
|
215 | }
|
216 | |
217 |
|
218 |
|
219 | createType(type, ...params) {
|
220 | return createTypeUnsafe(this, type, params);
|
221 | }
|
222 | |
223 |
|
224 |
|
225 | createTypeUnsafe(type, params, options) {
|
226 | return createTypeUnsafe(this, type, params, options);
|
227 | }
|
228 |
|
229 | findMetaCall(callIndex) {
|
230 | const [section, method] = [callIndex[0], callIndex[1]];
|
231 | return assertReturn(this.__internal__metadataCalls[`${section}`] && this.__internal__metadataCalls[`${section}`][`${method}`], () => `findMetaCall: Unable to find Call with index [${section}, ${method}]/[${callIndex.toString()}]`);
|
232 | }
|
233 |
|
234 | findMetaError(errorIndex) {
|
235 | const [section, method] = isU8a(errorIndex)
|
236 | ? [errorIndex[0], errorIndex[1]]
|
237 | : [
|
238 | errorIndex.index.toNumber(),
|
239 | isU8a(errorIndex.error)
|
240 | ? errorIndex.error[0]
|
241 | : errorIndex.error.toNumber()
|
242 | ];
|
243 | return assertReturn(this.__internal__metadataErrors[`${section}`] && this.__internal__metadataErrors[`${section}`][`${method}`], () => `findMetaError: Unable to find Error with index [${section}, ${method}]/[${errorIndex.toString()}]`);
|
244 | }
|
245 | findMetaEvent(eventIndex) {
|
246 | const [section, method] = [eventIndex[0], eventIndex[1]];
|
247 | return assertReturn(this.__internal__metadataEvents[`${section}`] && this.__internal__metadataEvents[`${section}`][`${method}`], () => `findMetaEvent: Unable to find Event with index [${section}, ${method}]/[${eventIndex.toString()}]`);
|
248 | }
|
249 | get(name, withUnknown, knownTypeDef) {
|
250 | return this.getUnsafe(name, withUnknown, knownTypeDef);
|
251 | }
|
252 | getUnsafe(name, withUnknown, knownTypeDef) {
|
253 | let Type = this.__internal__classes.get(name) || this.__internal__knownDefaults[name];
|
254 |
|
255 | if (!Type) {
|
256 | const definition = this.__internal__definitions.get(name);
|
257 | let BaseType;
|
258 |
|
259 | if (definition) {
|
260 | BaseType = createClassUnsafe(this, definition);
|
261 | }
|
262 | else if (knownTypeDef) {
|
263 | BaseType = constructTypeClass(this, knownTypeDef);
|
264 | }
|
265 | else if (withUnknown) {
|
266 | l.warn(`Unable to resolve type ${name}, it will fail on construction`);
|
267 | this.__internal__unknownTypes.set(name, true);
|
268 | BaseType = DoNotConstruct.with(name);
|
269 | }
|
270 | if (BaseType) {
|
271 |
|
272 |
|
273 |
|
274 | Type = class extends BaseType {
|
275 | };
|
276 | this.__internal__classes.set(name, Type);
|
277 |
|
278 |
|
279 | if (knownTypeDef && isNumber(knownTypeDef.lookupIndex)) {
|
280 | this.__internal__classes.set(this.createLookupType(knownTypeDef.lookupIndex), Type);
|
281 | }
|
282 | }
|
283 | }
|
284 | return Type;
|
285 | }
|
286 | getChainProperties() {
|
287 | return this.__internal__chainProperties;
|
288 | }
|
289 | getClassName(Type) {
|
290 |
|
291 |
|
292 |
|
293 | const names = [];
|
294 | for (const [name, Clazz] of this.__internal__knownDefaultsEntries) {
|
295 | if (Type === Clazz) {
|
296 | names.push(name);
|
297 | }
|
298 | }
|
299 | for (const [name, Clazz] of this.__internal__classes.entries()) {
|
300 | if (Type === Clazz) {
|
301 | names.push(name);
|
302 | }
|
303 | }
|
304 | return names.length
|
305 |
|
306 |
|
307 | ? names.sort().reverse()[0]
|
308 | : undefined;
|
309 | }
|
310 | getDefinition(typeName) {
|
311 | return this.__internal__definitions.get(typeName);
|
312 | }
|
313 | getModuleInstances(specName, moduleName) {
|
314 | return this.__internal__knownTypes?.typesBundle?.spec?.[specName.toString()]?.instances?.[moduleName] || this.__internal__moduleMap[moduleName];
|
315 | }
|
316 | getOrThrow(name) {
|
317 | const Clazz = this.get(name);
|
318 | if (!Clazz) {
|
319 | throw new Error(`type ${name} not found`);
|
320 | }
|
321 | return Clazz;
|
322 | }
|
323 | getOrUnknown(name) {
|
324 | return this.get(name, true);
|
325 | }
|
326 |
|
327 | getTransactionExtensionVersion() {
|
328 | return 0;
|
329 | }
|
330 | getSignedExtensionExtra() {
|
331 | return expandExtensionTypes(this.__internal__signedExtensions, 'payload', this.__internal__userExtensions);
|
332 | }
|
333 | getSignedExtensionTypes() {
|
334 | return expandExtensionTypes(this.__internal__signedExtensions, 'extrinsic', this.__internal__userExtensions);
|
335 | }
|
336 | hasClass(name) {
|
337 | return this.__internal__classes.has(name) || !!this.__internal__knownDefaults[name];
|
338 | }
|
339 | hasDef(name) {
|
340 | return this.__internal__definitions.has(name);
|
341 | }
|
342 | hasType(name) {
|
343 | return !this.__internal__unknownTypes.get(name) && (this.hasClass(name) || this.hasDef(name));
|
344 | }
|
345 | hash(data) {
|
346 | return this.createType('CodecHash', this.__internal__hasher(data));
|
347 | }
|
348 |
|
349 | register(arg1, arg2) {
|
350 |
|
351 | if (isFunction(arg1)) {
|
352 | this.__internal__classes.set(arg1.name, arg1);
|
353 | }
|
354 | else if (isString(arg1)) {
|
355 | if (!isFunction(arg2)) {
|
356 | throw new Error(`Expected class definition passed to '${arg1}' registration`);
|
357 | }
|
358 | else if (arg1 === arg2.toString()) {
|
359 | throw new Error(`Unable to register circular ${arg1} === ${arg1}`);
|
360 | }
|
361 | this.__internal__classes.set(arg1, arg2);
|
362 | }
|
363 | else {
|
364 | this.__internal__registerObject(arg1);
|
365 | }
|
366 | }
|
367 | __internal__registerObject = (obj) => {
|
368 | const entries = Object.entries(obj);
|
369 | for (let e = 0, count = entries.length; e < count; e++) {
|
370 | const [name, type] = entries[e];
|
371 | if (isFunction(type)) {
|
372 |
|
373 | this.__internal__classes.set(name, type);
|
374 | }
|
375 | else {
|
376 | const def = isString(type)
|
377 | ? type
|
378 | : stringify(type);
|
379 | if (name === def) {
|
380 | throw new Error(`Unable to register circular ${name} === ${def}`);
|
381 | }
|
382 |
|
383 | if (this.__internal__classes.has(name)) {
|
384 | this.__internal__classes.delete(name);
|
385 | }
|
386 | this.__internal__definitions.set(name, def);
|
387 | }
|
388 | }
|
389 | };
|
390 |
|
391 | setChainProperties(properties) {
|
392 | if (properties) {
|
393 | this.__internal__chainProperties = properties;
|
394 | }
|
395 | }
|
396 | setHasher(hasher) {
|
397 | this.__internal__hasher = hasher || blake2AsU8a;
|
398 | }
|
399 | setKnownTypes(knownTypes) {
|
400 | this.__internal__knownTypes = knownTypes;
|
401 | }
|
402 | setLookup(lookup) {
|
403 | this.__internal__lookup = lookup;
|
404 |
|
405 | lookup.register();
|
406 | }
|
407 |
|
408 |
|
409 |
|
410 |
|
411 | __internal__registerLookup = (lookup) => {
|
412 |
|
413 | this.setLookup(lookup);
|
414 |
|
415 | let Weight = null;
|
416 | if (this.hasType('SpWeightsWeightV2Weight')) {
|
417 |
|
418 | const weightv2 = this.createType('SpWeightsWeightV2Weight');
|
419 | Weight = weightv2.refTime && weightv2.proofSize
|
420 |
|
421 | ? 'SpWeightsWeightV2Weight'
|
422 |
|
423 | : 'WeightV1';
|
424 | }
|
425 | else if (!isBn(this.createType('Weight'))) {
|
426 |
|
427 |
|
428 |
|
429 | Weight = 'WeightV1';
|
430 | }
|
431 | if (Weight) {
|
432 |
|
433 | this.register({ Weight });
|
434 | }
|
435 | };
|
436 |
|
437 | setMetadata(metadata, signedExtensions, userExtensions, noInitWarn) {
|
438 | this.__internal__metadata = metadata.asLatest;
|
439 | this.__internal__metadataVersion = metadata.version;
|
440 | this.__internal__firstCallIndex = null;
|
441 |
|
442 | this.__internal__registerLookup(this.__internal__metadata.lookup);
|
443 | injectExtrinsics(this, this.__internal__metadata, this.__internal__metadataVersion, this.__internal__metadataCalls, this.__internal__moduleMap);
|
444 | injectErrors(this, this.__internal__metadata, this.__internal__metadataVersion, this.__internal__metadataErrors);
|
445 | injectEvents(this, this.__internal__metadata, this.__internal__metadataVersion, this.__internal__metadataEvents);
|
446 |
|
447 |
|
448 | const [defSection] = Object
|
449 | .keys(this.__internal__metadataCalls)
|
450 | .sort(sortDecimalStrings);
|
451 | if (defSection) {
|
452 | const [defMethod] = Object
|
453 | .keys(this.__internal__metadataCalls[defSection])
|
454 | .sort(sortDecimalStrings);
|
455 | if (defMethod) {
|
456 | this.__internal__firstCallIndex = new Uint8Array([parseInt(defSection, 10), parseInt(defMethod, 10)]);
|
457 | }
|
458 | }
|
459 |
|
460 | this.setSignedExtensions(signedExtensions || (this.__internal__metadata.extrinsic.version.gt(BN_ZERO)
|
461 |
|
462 | ? this.__internal__metadata.extrinsic.signedExtensions.map(({ identifier }) => identifier.toString())
|
463 | : fallbackExtensions), userExtensions, noInitWarn);
|
464 |
|
465 | this.setChainProperties(extractProperties(this, metadata));
|
466 | }
|
467 |
|
468 | setSignedExtensions(signedExtensions = fallbackExtensions, userExtensions, noInitWarn) {
|
469 | this.__internal__signedExtensions = signedExtensions;
|
470 | this.__internal__userExtensions = userExtensions;
|
471 | if (!noInitWarn) {
|
472 | const unknown = findUnknownExtensions(this.__internal__signedExtensions, this.__internal__userExtensions);
|
473 | if (unknown.length) {
|
474 | l.warn(`Unknown signed extensions ${unknown.join(', ')} found, treating them as no-effect`);
|
475 | }
|
476 | }
|
477 | }
|
478 | }
|