1 | import { sanitize, Struct } from '@polkadot/types-codec';
|
2 | import { getTypeDef, TypeDefInfo, withTypeString } from '@polkadot/types-create';
|
3 | import { assertUnreachable, isNumber, isString, logger, objectSpread, stringCamelCase, stringify, stringPascalCase } from '@polkadot/util';
|
4 | const l = logger('PortableRegistry');
|
5 | const TYPE_UNWRAP = { toNumber: () => -1 };
|
6 | const PRIMITIVE_ALIAS = {
|
7 | Char: 'u32',
|
8 | Str: 'Text'
|
9 | };
|
10 | const PATHS_ALIAS = splitNamespace([
|
11 |
|
12 |
|
13 | 'sp_core::crypto::AccountId32',
|
14 | 'sp_runtime::generic::era::Era',
|
15 | 'sp_runtime::multiaddress::MultiAddress',
|
16 |
|
17 | 'fp_account::AccountId20',
|
18 | 'account::AccountId20',
|
19 | 'polkadot_runtime_common::claims::EthereumAddress',
|
20 |
|
21 |
|
22 | 'frame_support::weights::weight_v2::Weight',
|
23 | 'sp_weights::weight_v2::Weight',
|
24 |
|
25 |
|
26 | '*_democracy::vote::Vote',
|
27 | '*_conviction_voting::vote::Vote',
|
28 | '*_identity::types::Data',
|
29 |
|
30 | 'sp_core::OpaqueMetadata',
|
31 | 'sp_core::OpaquePeerId',
|
32 | 'sp_core::offchain::OpaqueMultiaddr',
|
33 |
|
34 | 'primitive_types::*',
|
35 | 'sp_arithmetic::per_things::*',
|
36 |
|
37 | '*_runtime::RuntimeCall',
|
38 | '*_runtime::RuntimeEvent',
|
39 |
|
40 | 'ink::env::types::*',
|
41 | 'ink::primitives::types::*',
|
42 | 'ink_env::types::*',
|
43 | 'ink_primitives::types::*'
|
44 | ]);
|
45 | const PATHS_SET = splitNamespace([
|
46 | 'pallet_identity::types::BitFlags'
|
47 | ]);
|
48 | const BITVEC_NS_LSB = ['bitvec::order::Lsb0', 'BitOrderLsb0'];
|
49 | const BITVEC_NS_MSB = ['bitvec::order::Msb0', 'BitOrderMsb0'];
|
50 | const BITVEC_NS = [...BITVEC_NS_LSB, ...BITVEC_NS_MSB];
|
51 | const WRAPPERS = ['BoundedBTreeMap', 'BoundedBTreeSet', 'BoundedVec', 'Box', 'BTreeMap', 'BTreeSet', 'Cow', 'Option', 'Range', 'RangeInclusive', 'Result', 'WeakBoundedVec', 'WrapperKeepOpaque', 'WrapperOpaque'];
|
52 | const RESERVED = [
|
53 |
|
54 | 'entries', 'keys', 'new', 'size',
|
55 |
|
56 | 'hash', 'registry'
|
57 | ];
|
58 | const PATH_RM_INDEX_1 = ['generic', 'misc', 'pallet', 'traits', 'types'];
|
59 |
|
60 | function sanitizeDocs(docs) {
|
61 | const count = docs.length;
|
62 | const result = new Array(count);
|
63 | for (let i = 0; i < count; i++) {
|
64 | result[i] = docs[i].toString();
|
65 | }
|
66 | return result;
|
67 | }
|
68 |
|
69 | function splitNamespace(values) {
|
70 | const count = values.length;
|
71 | const result = new Array(count);
|
72 | for (let i = 0; i < count; i++) {
|
73 | result[i] = values[i].split('::');
|
74 | }
|
75 | return result;
|
76 | }
|
77 |
|
78 | function matchParts(first, second) {
|
79 | return first.length === second.length && first.every((a, index) => {
|
80 | const b = second[index].toString();
|
81 | if ((a === '*') || (a === b)) {
|
82 | return true;
|
83 | }
|
84 | if (a.includes('*') && a.includes('_') && b.includes('_')) {
|
85 | let suba = a.split('_');
|
86 | let subb = b.split('_');
|
87 |
|
88 | if (suba[0] === '*') {
|
89 | const indexOf = subb.indexOf(suba[1]);
|
90 | if (indexOf !== -1) {
|
91 | suba = suba.slice(1);
|
92 | subb = subb.slice(indexOf);
|
93 | }
|
94 | }
|
95 |
|
96 | if ((suba.length === 2) && (suba[1] === '*') && (suba[0] === subb[0])) {
|
97 | return true;
|
98 | }
|
99 | return matchParts(suba, subb);
|
100 | }
|
101 | return false;
|
102 | });
|
103 | }
|
104 |
|
105 | function getAliasPath({ def, path }) {
|
106 |
|
107 |
|
108 | if (['frame_support::weights::weight_v2::Weight', 'sp_weights::weight_v2::Weight'].includes(path.join('::'))) {
|
109 | return !def.isComposite || def.asComposite.fields.length === 1
|
110 | ? 'WeightV1'
|
111 | : null;
|
112 | }
|
113 |
|
114 | return path.length && PATHS_ALIAS.some((a) => matchParts(a, path))
|
115 | ? path[path.length - 1].toString()
|
116 | : null;
|
117 | }
|
118 |
|
119 | function extractNameFlat(portable, lookupIndex, params, path, isInternal = false) {
|
120 | const count = path.length;
|
121 |
|
122 | if (count === 0 || WRAPPERS.includes(path[count - 1].toString())) {
|
123 | return null;
|
124 | }
|
125 | const camels = new Array(count);
|
126 | const lowers = new Array(count);
|
127 |
|
128 |
|
129 |
|
130 | for (let i = 0; i < count; i++) {
|
131 | const c = stringPascalCase(isInternal
|
132 | ? path[i].replace('pallet_', '')
|
133 | : path[i]);
|
134 | const l = c.toLowerCase();
|
135 | camels[i] = c;
|
136 | lowers[i] = l;
|
137 | }
|
138 | let name = '';
|
139 | for (let i = 0; i < count; i++) {
|
140 | const l = lowers[i];
|
141 |
|
142 | if (i !== 1 || !PATH_RM_INDEX_1.includes(l)) {
|
143 |
|
144 |
|
145 | if (l !== lowers[i + 1]) {
|
146 | name += camels[i];
|
147 | }
|
148 | }
|
149 | }
|
150 |
|
151 | if (camels[1] === 'RawOrigin' && count === 2 && params.length === 2 && params[1].type.isSome) {
|
152 | const instanceType = portable[params[1].type.unwrap().toNumber()];
|
153 | if (instanceType.type.path.length === 2) {
|
154 | name = `${name}${instanceType.type.path[1].toString()}`;
|
155 | }
|
156 | }
|
157 | return { lookupIndex, name, params };
|
158 | }
|
159 |
|
160 | function extractName(portable, lookupIndex, { type: { params, path } }) {
|
161 | return extractNameFlat(portable, lookupIndex, params, path);
|
162 | }
|
163 |
|
164 | function nextDupeMatches(name, startAt, names) {
|
165 | const result = [names[startAt]];
|
166 | for (let i = startAt + 1, count = names.length; i < count; i++) {
|
167 | const v = names[i];
|
168 | if (v.name === name) {
|
169 | result.push(v);
|
170 | }
|
171 | }
|
172 | return result;
|
173 | }
|
174 |
|
175 | function rewriteDupes(input, rewrite) {
|
176 | const count = input.length;
|
177 | for (let i = 0; i < count; i++) {
|
178 | const a = input[i];
|
179 | for (let j = i + 1; j < count; j++) {
|
180 | const b = input[j];
|
181 |
|
182 | if (a.lookupIndex !== b.lookupIndex && a.name === b.name) {
|
183 | return false;
|
184 | }
|
185 | }
|
186 | }
|
187 |
|
188 | for (let i = 0; i < count; i++) {
|
189 | const p = input[i];
|
190 | rewrite[p.lookupIndex] = p.name;
|
191 | }
|
192 | return true;
|
193 | }
|
194 |
|
195 | function removeDupeNames(lookup, portable, names) {
|
196 | const rewrite = {};
|
197 | return names
|
198 | .map((original, startAt) => {
|
199 | const { lookupIndex, name, params } = original;
|
200 | if (!name) {
|
201 |
|
202 | return null;
|
203 | }
|
204 | else if (rewrite[lookupIndex]) {
|
205 |
|
206 | return original;
|
207 | }
|
208 |
|
209 | const allSame = nextDupeMatches(name, startAt, names);
|
210 |
|
211 | if (allSame.length === 1) {
|
212 | return original;
|
213 | }
|
214 |
|
215 | const anyDiff = allSame.some((o) => params.length !== o.params.length ||
|
216 | params.some((p, index) => !p.name.eq(o.params[index].name) ||
|
217 | p.type.unwrapOr(TYPE_UNWRAP).toNumber() !== o.params[index].type.unwrapOr(TYPE_UNWRAP).toNumber()));
|
218 |
|
219 | if (!anyDiff) {
|
220 | return original;
|
221 | }
|
222 |
|
223 |
|
224 |
|
225 | const paramIdx = params.findIndex(({ type }, index) => allSame.every(({ params }, aIndex) => params[index].type.isSome && (aIndex === 0 ||
|
226 | !params[index].type.eq(type))));
|
227 |
|
228 | if (paramIdx === -1) {
|
229 | return original;
|
230 | }
|
231 |
|
232 | const sameCount = allSame.length;
|
233 | const adjusted = new Array(sameCount);
|
234 |
|
235 |
|
236 | for (let i = 0; i < sameCount; i++) {
|
237 | const { lookupIndex, name, params } = allSame[i];
|
238 | const { def, path } = lookup.getSiType(params[paramIdx].type.unwrap());
|
239 |
|
240 |
|
241 | if (!def.isPrimitive && !path.length) {
|
242 | return null;
|
243 | }
|
244 | adjusted[i] = {
|
245 | lookupIndex,
|
246 | name: def.isPrimitive
|
247 | ? `${name}${def.asPrimitive.toString()}`
|
248 | : `${name}${path[path.length - 1].toString()}`
|
249 | };
|
250 | }
|
251 |
|
252 | if (rewriteDupes(adjusted, rewrite)) {
|
253 | return original;
|
254 | }
|
255 |
|
256 |
|
257 |
|
258 |
|
259 |
|
260 |
|
261 | for (let i = 0; i < sameCount; i++) {
|
262 | const { lookupIndex, name, params } = allSame[i];
|
263 | const { def, path } = lookup.getSiType(params[paramIdx].type.unwrap());
|
264 | const flat = extractNameFlat(portable, lookupIndex, params, path, true);
|
265 | if (def.isPrimitive || !flat) {
|
266 | return null;
|
267 | }
|
268 | adjusted[i] = {
|
269 | lookupIndex,
|
270 | name: `${name}${flat.name}`
|
271 | };
|
272 | }
|
273 |
|
274 | if (rewriteDupes(adjusted, rewrite)) {
|
275 | return original;
|
276 | }
|
277 | return null;
|
278 | })
|
279 | .filter((n) => !!n)
|
280 | .map(({ lookupIndex, name, params }) => ({
|
281 | lookupIndex,
|
282 | name: rewrite[lookupIndex] || name,
|
283 | params
|
284 | }));
|
285 | }
|
286 |
|
287 | function registerTypes(lookup, lookups, names, params) {
|
288 |
|
289 | lookup.registry.register(lookups);
|
290 |
|
291 | if (params.SpRuntimeUncheckedExtrinsic) {
|
292 |
|
293 | const [addrParam, , sigParam] = params.SpRuntimeUncheckedExtrinsic;
|
294 | const siAddress = lookup.getSiType(addrParam.type.unwrap());
|
295 | const siSignature = lookup.getSiType(sigParam.type.unwrap());
|
296 | const nsSignature = siSignature.path.join('::');
|
297 | let nsAccountId = siAddress.path.join('::');
|
298 | const isMultiAddress = nsAccountId === 'sp_runtime::multiaddress::MultiAddress';
|
299 |
|
300 | if (isMultiAddress) {
|
301 |
|
302 | const [idParam] = siAddress.params;
|
303 | nsAccountId = lookup.getSiType(idParam.type.unwrap()).path.join('::');
|
304 | }
|
305 | lookup.registry.register({
|
306 |
|
307 | AccountId: nsAccountId.endsWith('::AccountId20') || nsAccountId.endsWith('::H160')
|
308 | ? 'AccountId20'
|
309 | : 'AccountId32',
|
310 | Address: isMultiAddress
|
311 | ? 'MultiAddress'
|
312 | : 'AccountId',
|
313 | ExtrinsicSignature: ['sp_runtime::MultiSignature'].includes(nsSignature)
|
314 | ? 'MultiSignature'
|
315 | : names[sigParam.type.unwrap().toNumber()] || 'MultiSignature'
|
316 | });
|
317 | }
|
318 | }
|
319 |
|
320 |
|
321 |
|
322 |
|
323 | function extractAliases(params, isContract) {
|
324 | const hasParams = Object.keys(params).some((k) => !k.startsWith('Pallet'));
|
325 | const alias = {};
|
326 | if (params.SpRuntimeUncheckedExtrinsic) {
|
327 |
|
328 | const [, { type }] = params.SpRuntimeUncheckedExtrinsic;
|
329 | alias[type.unwrap().toNumber()] = 'Call';
|
330 | }
|
331 | else if (hasParams && !isContract) {
|
332 | l.warn('Unable to determine runtime Call type, cannot inspect sp_runtime::generic::unchecked_extrinsic::UncheckedExtrinsic');
|
333 | }
|
334 | if (params.FrameSystemEventRecord) {
|
335 |
|
336 | const [{ type }] = params.FrameSystemEventRecord;
|
337 | alias[type.unwrap().toNumber()] = 'Event';
|
338 | }
|
339 | else if (hasParams && !isContract) {
|
340 | l.warn('Unable to determine runtime Event type, cannot inspect frame_system::EventRecord');
|
341 | }
|
342 | return alias;
|
343 | }
|
344 |
|
345 | function extractTypeInfo(lookup, portable) {
|
346 | const nameInfo = [];
|
347 | const types = {};
|
348 | for (let i = 0, count = portable.length; i < count; i++) {
|
349 | const type = portable[i];
|
350 | const lookupIndex = type.id.toNumber();
|
351 | const extracted = extractName(portable, lookupIndex, portable[i]);
|
352 | if (extracted) {
|
353 | nameInfo.push(extracted);
|
354 | }
|
355 | types[lookupIndex] = type;
|
356 | }
|
357 | const lookups = {};
|
358 | const names = {};
|
359 | const params = {};
|
360 | const dedup = removeDupeNames(lookup, portable, nameInfo);
|
361 | for (let i = 0, count = dedup.length; i < count; i++) {
|
362 | const { lookupIndex, name, params: p } = dedup[i];
|
363 | names[lookupIndex] = name;
|
364 | lookups[name] = lookup.registry.createLookupType(lookupIndex);
|
365 | params[name] = p;
|
366 | }
|
367 | return { lookups, names, params, types };
|
368 | }
|
369 | export class PortableRegistry extends Struct {
|
370 | constructor(registry, value, isContract) {
|
371 |
|
372 | super(registry, {
|
373 | types: 'Vec<PortableType>'
|
374 | }, value);
|
375 | this.__internal__typeDefs = {};
|
376 | const { lookups, names, params, types } = extractTypeInfo(this, this.types);
|
377 | this.__internal__alias = extractAliases(params, isContract);
|
378 | this.__internal__lookups = lookups;
|
379 | this.__internal__names = names;
|
380 | this.__internal__params = params;
|
381 | this.__internal__types = types;
|
382 |
|
383 | }
|
384 | |
385 |
|
386 |
|
387 | get names() {
|
388 | return Object.values(this.__internal__names).sort();
|
389 | }
|
390 | |
391 |
|
392 |
|
393 | get types() {
|
394 | return this.getT('types');
|
395 | }
|
396 | |
397 |
|
398 |
|
399 | register() {
|
400 | registerTypes(this, this.__internal__lookups, this.__internal__names, this.__internal__params);
|
401 | }
|
402 | |
403 |
|
404 |
|
405 | getName(lookupId) {
|
406 | return this.__internal__names[this.__internal__getLookupId(lookupId)];
|
407 | }
|
408 | |
409 |
|
410 |
|
411 | getSiType(lookupId) {
|
412 |
|
413 |
|
414 | const found = (this.__internal__types || this.types)[this.__internal__getLookupId(lookupId)];
|
415 | if (!found) {
|
416 | throw new Error(`PortableRegistry: Unable to find type with lookupId ${lookupId.toString()}`);
|
417 | }
|
418 | return found.type;
|
419 | }
|
420 | |
421 |
|
422 |
|
423 | getTypeDef(lookupId) {
|
424 | const lookupIndex = this.__internal__getLookupId(lookupId);
|
425 | if (!this.__internal__typeDefs[lookupIndex]) {
|
426 | const lookupName = this.__internal__names[lookupIndex];
|
427 | const empty = {
|
428 | info: TypeDefInfo.DoNotConstruct,
|
429 | lookupIndex,
|
430 | lookupName,
|
431 | type: this.registry.createLookupType(lookupIndex)
|
432 | };
|
433 |
|
434 | if (lookupName) {
|
435 | this.__internal__typeDefs[lookupIndex] = empty;
|
436 | }
|
437 | const extracted = this.__internal__extract(this.getSiType(lookupId), lookupIndex);
|
438 |
|
439 | if (!lookupName) {
|
440 | this.__internal__typeDefs[lookupIndex] = empty;
|
441 | }
|
442 | Object.keys(extracted).forEach((k) => {
|
443 | if (k !== 'lookupName' || extracted[k]) {
|
444 |
|
445 | this.__internal__typeDefs[lookupIndex][k] = extracted[k];
|
446 | }
|
447 | });
|
448 |
|
449 | if (extracted.info === TypeDefInfo.Plain) {
|
450 | this.__internal__typeDefs[lookupIndex].lookupNameRoot = this.__internal__typeDefs[lookupIndex].lookupName;
|
451 | delete this.__internal__typeDefs[lookupIndex].lookupName;
|
452 | }
|
453 | }
|
454 | return this.__internal__typeDefs[lookupIndex];
|
455 | }
|
456 | |
457 |
|
458 |
|
459 | sanitizeField(name) {
|
460 | let nameField = null;
|
461 | let nameOrig = null;
|
462 | if (name.isSome) {
|
463 | nameField = stringCamelCase(name.unwrap());
|
464 | if (nameField.includes('#')) {
|
465 | nameOrig = nameField;
|
466 | nameField = nameOrig.replace(/#/g, '_');
|
467 | }
|
468 | else if (RESERVED.includes(nameField)) {
|
469 | nameOrig = nameField;
|
470 | nameField = `${nameField}_`;
|
471 | }
|
472 | }
|
473 | return [nameField, nameOrig];
|
474 | }
|
475 |
|
476 | __internal__createSiDef(lookupId) {
|
477 | const typeDef = this.getTypeDef(lookupId);
|
478 | const lookupIndex = lookupId.toNumber();
|
479 |
|
480 | return [TypeDefInfo.DoNotConstruct, TypeDefInfo.Enum, TypeDefInfo.Struct].includes(typeDef.info) && typeDef.lookupName
|
481 | ? {
|
482 | docs: typeDef.docs,
|
483 | info: TypeDefInfo.Si,
|
484 | lookupIndex,
|
485 | lookupName: this.__internal__names[lookupIndex],
|
486 | type: this.registry.createLookupType(lookupId)
|
487 | }
|
488 | : typeDef;
|
489 | }
|
490 |
|
491 | __internal__getLookupId(lookupId) {
|
492 | if (isString(lookupId)) {
|
493 | if (!this.registry.isLookupType(lookupId)) {
|
494 | throw new Error(`PortableRegistry: Expected a lookup string type, found ${lookupId}`);
|
495 | }
|
496 | return parseInt(lookupId.replace('Lookup', ''), 10);
|
497 | }
|
498 | else if (isNumber(lookupId)) {
|
499 | return lookupId;
|
500 | }
|
501 | return lookupId.toNumber();
|
502 | }
|
503 |
|
504 | __internal__extract(type, lookupIndex) {
|
505 | const namespace = type.path.join('::');
|
506 | let typeDef;
|
507 | const aliasType = this.__internal__alias[lookupIndex] || getAliasPath(type);
|
508 | try {
|
509 | if (aliasType) {
|
510 | typeDef = this.__internal__extractAliasPath(lookupIndex, aliasType);
|
511 | }
|
512 | else {
|
513 | switch (type.def.type) {
|
514 | case 'Array':
|
515 | typeDef = this.__internal__extractArray(lookupIndex, type.def.asArray);
|
516 | break;
|
517 | case 'BitSequence':
|
518 | typeDef = this.__internal__extractBitSequence(lookupIndex, type.def.asBitSequence);
|
519 | break;
|
520 | case 'Compact':
|
521 | typeDef = this.__internal__extractCompact(lookupIndex, type.def.asCompact);
|
522 | break;
|
523 | case 'Composite':
|
524 | typeDef = this.__internal__extractComposite(lookupIndex, type, type.def.asComposite);
|
525 | break;
|
526 | case 'HistoricMetaCompat':
|
527 | typeDef = this.__internal__extractHistoric(lookupIndex, type.def.asHistoricMetaCompat);
|
528 | break;
|
529 | case 'Primitive':
|
530 | typeDef = this.__internal__extractPrimitive(lookupIndex, type);
|
531 | break;
|
532 | case 'Sequence':
|
533 | typeDef = this.__internal__extractSequence(lookupIndex, type.def.asSequence);
|
534 | break;
|
535 | case 'Tuple':
|
536 | typeDef = this.__internal__extractTuple(lookupIndex, type.def.asTuple);
|
537 | break;
|
538 | case 'Variant':
|
539 | typeDef = this.__internal__extractVariant(lookupIndex, type, type.def.asVariant);
|
540 | break;
|
541 | default: assertUnreachable(type.def.type);
|
542 | }
|
543 | }
|
544 | }
|
545 | catch (error) {
|
546 | throw new Error(`PortableRegistry: ${lookupIndex}${namespace ? ` (${namespace})` : ''}: Error extracting ${stringify(type)}: ${error.message}`);
|
547 | }
|
548 | return objectSpread({
|
549 | docs: sanitizeDocs(type.docs),
|
550 | namespace
|
551 | }, typeDef);
|
552 | }
|
553 |
|
554 | __internal__extractArray(_, { len, type }) {
|
555 | const length = len.toNumber();
|
556 | if (length > 2048) {
|
557 | throw new Error('Only support for [Type; <length>], where length <= 2048');
|
558 | }
|
559 | return withTypeString(this.registry, {
|
560 | info: TypeDefInfo.VecFixed,
|
561 | length,
|
562 | sub: this.__internal__createSiDef(type)
|
563 | });
|
564 | }
|
565 |
|
566 | __internal__extractBitSequence(_, { bitOrderType, bitStoreType }) {
|
567 |
|
568 |
|
569 | const a = this.__internal__createSiDef(bitOrderType);
|
570 | const b = this.__internal__createSiDef(bitStoreType);
|
571 | const [bitOrder, bitStore] = BITVEC_NS.includes(a.namespace || '')
|
572 | ? [a, b]
|
573 | : [b, a];
|
574 | if (!bitOrder.namespace || !BITVEC_NS.includes(bitOrder.namespace)) {
|
575 | throw new Error(`Unexpected bitOrder found as ${bitOrder.namespace || '<unknown>'}`);
|
576 | }
|
577 | else if (bitStore.info !== TypeDefInfo.Plain || bitStore.type !== 'u8') {
|
578 | throw new Error(`Only u8 bitStore is currently supported, found ${bitStore.type}`);
|
579 | }
|
580 | const isLsb = BITVEC_NS_LSB.includes(bitOrder.namespace);
|
581 | if (!isLsb) {
|
582 |
|
583 |
|
584 |
|
585 |
|
586 |
|
587 |
|
588 |
|
589 | }
|
590 | return {
|
591 | info: TypeDefInfo.Plain,
|
592 | type: 'BitVec'
|
593 | };
|
594 | }
|
595 |
|
596 | __internal__extractCompact(_, { type }) {
|
597 | return withTypeString(this.registry, {
|
598 | info: TypeDefInfo.Compact,
|
599 | sub: this.__internal__createSiDef(type)
|
600 | });
|
601 | }
|
602 |
|
603 | __internal__extractComposite(lookupIndex, { params, path }, { fields }) {
|
604 | if (path.length) {
|
605 | const pathFirst = path[0].toString();
|
606 | const pathLast = path[path.length - 1].toString();
|
607 | if (path.length === 1 && pathFirst === 'BTreeMap') {
|
608 | if (params.length !== 2) {
|
609 | throw new Error(`BTreeMap requires 2 parameters, found ${params.length}`);
|
610 | }
|
611 | return withTypeString(this.registry, {
|
612 | info: TypeDefInfo.BTreeMap,
|
613 | sub: params.map(({ type }) => this.__internal__createSiDef(type.unwrap()))
|
614 | });
|
615 | }
|
616 | else if (path.length === 1 && pathFirst === 'BTreeSet') {
|
617 | if (params.length !== 1) {
|
618 | throw new Error(`BTreeSet requires 1 parameter, found ${params.length}`);
|
619 | }
|
620 | return withTypeString(this.registry, {
|
621 | info: TypeDefInfo.BTreeSet,
|
622 | sub: this.__internal__createSiDef(params[0].type.unwrap())
|
623 | });
|
624 | }
|
625 | else if (['Range', 'RangeInclusive'].includes(pathFirst)) {
|
626 | if (params.length !== 1) {
|
627 | throw new Error(`Range requires 1 parameter, found ${params.length}`);
|
628 | }
|
629 | return withTypeString(this.registry, {
|
630 | info: pathFirst === 'Range'
|
631 | ? TypeDefInfo.Range
|
632 | : TypeDefInfo.RangeInclusive,
|
633 | sub: this.__internal__createSiDef(params[0].type.unwrap()),
|
634 | type: pathFirst
|
635 | });
|
636 | }
|
637 | else if (['WrapperKeepOpaque', 'WrapperOpaque'].includes(pathLast)) {
|
638 | if (params.length !== 1) {
|
639 | throw new Error(`WrapperOpaque requires 1 parameter, found ${params.length}`);
|
640 | }
|
641 | return withTypeString(this.registry, {
|
642 | info: pathLast === 'WrapperKeepOpaque'
|
643 | ? TypeDefInfo.WrapperKeepOpaque
|
644 | : TypeDefInfo.WrapperOpaque,
|
645 | sub: this.__internal__createSiDef(params[0].type.unwrap()),
|
646 | type: pathLast
|
647 | });
|
648 | }
|
649 | }
|
650 | return PATHS_SET.some((p) => matchParts(p, path))
|
651 | ? this.__internal__extractCompositeSet(lookupIndex, params, fields)
|
652 | : this.__internal__extractFields(lookupIndex, fields);
|
653 | }
|
654 |
|
655 | __internal__extractCompositeSet(_, params, fields) {
|
656 | if (params.length !== 1 || fields.length !== 1) {
|
657 | throw new Error('Set handling expects param/field as single entries');
|
658 | }
|
659 | return withTypeString(this.registry, {
|
660 | info: TypeDefInfo.Set,
|
661 | length: this.registry.createTypeUnsafe(this.registry.createLookupType(fields[0].type), []).bitLength(),
|
662 | sub: this.getSiType(params[0].type.unwrap()).def.asVariant.variants.map(({ index, name }) => ({
|
663 |
|
664 | index: index.toNumber(),
|
665 | info: TypeDefInfo.Plain,
|
666 | name: name.toString(),
|
667 | type: 'Null'
|
668 | }))
|
669 | });
|
670 | }
|
671 |
|
672 | __internal__extractFields(lookupIndex, fields) {
|
673 | let isStruct = true;
|
674 | let isTuple = true;
|
675 | const count = fields.length;
|
676 | for (let f = 0; f < count; f++) {
|
677 | const { name } = fields[f];
|
678 | isStruct = isStruct && name.isSome;
|
679 | isTuple = isTuple && name.isNone;
|
680 | }
|
681 | if (!isTuple && !isStruct) {
|
682 | throw new Error('Invalid fields type detected, expected either Tuple (all unnamed) or Struct (all named)');
|
683 | }
|
684 | if (count === 0) {
|
685 | return {
|
686 | info: TypeDefInfo.Null,
|
687 | type: 'Null'
|
688 | };
|
689 | }
|
690 | else if (isTuple && count === 1) {
|
691 | const typeDef = this.__internal__createSiDef(fields[0].type);
|
692 | return objectSpread({}, typeDef, lookupIndex === -1
|
693 | ? null
|
694 | : {
|
695 | lookupIndex,
|
696 | lookupName: this.__internal__names[lookupIndex],
|
697 | lookupNameRoot: typeDef.lookupName
|
698 | }, fields[0].typeName.isSome
|
699 | ? { typeName: sanitize(fields[0].typeName.unwrap()) }
|
700 | : null);
|
701 | }
|
702 | const [sub, alias] = this.__internal__extractFieldsAlias(fields);
|
703 | return withTypeString(this.registry, objectSpread({
|
704 | info: isTuple
|
705 | ? TypeDefInfo.Tuple
|
706 | : TypeDefInfo.Struct,
|
707 | sub
|
708 | }, alias.size
|
709 | ? { alias }
|
710 | : null, lookupIndex === -1
|
711 | ? null
|
712 | : {
|
713 | lookupIndex,
|
714 | lookupName: this.__internal__names[lookupIndex]
|
715 | }));
|
716 | }
|
717 |
|
718 | __internal__extractFieldsAlias(fields) {
|
719 | const alias = new Map();
|
720 | const count = fields.length;
|
721 | const sub = new Array(count);
|
722 | for (let i = 0; i < count; i++) {
|
723 | const { docs, name, type, typeName } = fields[i];
|
724 | const typeDef = this.__internal__createSiDef(type);
|
725 | if (name.isNone) {
|
726 | sub[i] = typeDef;
|
727 | }
|
728 | else {
|
729 | const [nameField, nameOrig] = this.sanitizeField(name);
|
730 | if (nameField && nameOrig) {
|
731 | alias.set(nameField, nameOrig);
|
732 | }
|
733 | sub[i] = objectSpread({
|
734 | docs: sanitizeDocs(docs),
|
735 | name: nameField
|
736 | }, typeDef, typeName.isSome
|
737 | ? { typeName: sanitize(typeName.unwrap()) }
|
738 | : null);
|
739 | }
|
740 | }
|
741 | return [sub, alias];
|
742 | }
|
743 |
|
744 | __internal__extractHistoric(_, type) {
|
745 | return objectSpread({
|
746 | displayName: type.toString(),
|
747 | isFromSi: true
|
748 | }, getTypeDef(type));
|
749 | }
|
750 |
|
751 | __internal__extractPrimitive(_, type) {
|
752 | const typeStr = type.def.asPrimitive.type.toString();
|
753 | return {
|
754 | info: TypeDefInfo.Plain,
|
755 | type: PRIMITIVE_ALIAS[typeStr] || typeStr.toLowerCase()
|
756 | };
|
757 | }
|
758 |
|
759 | __internal__extractAliasPath(_, type) {
|
760 | return {
|
761 | info: TypeDefInfo.Plain,
|
762 | type
|
763 | };
|
764 | }
|
765 |
|
766 | __internal__extractSequence(lookupIndex, { type }) {
|
767 | const sub = this.__internal__createSiDef(type);
|
768 | if (sub.type === 'u8') {
|
769 | return {
|
770 | info: TypeDefInfo.Plain,
|
771 | type: 'Bytes'
|
772 | };
|
773 | }
|
774 | return withTypeString(this.registry, {
|
775 | info: TypeDefInfo.Vec,
|
776 | lookupIndex,
|
777 | lookupName: this.__internal__names[lookupIndex],
|
778 | sub
|
779 | });
|
780 | }
|
781 |
|
782 | __internal__extractTuple(lookupIndex, ids) {
|
783 | if (ids.length === 0) {
|
784 | return {
|
785 | info: TypeDefInfo.Null,
|
786 | type: 'Null'
|
787 | };
|
788 | }
|
789 | else if (ids.length === 1) {
|
790 | return this.getTypeDef(ids[0]);
|
791 | }
|
792 | const sub = ids.map((t) => this.__internal__createSiDef(t));
|
793 | return withTypeString(this.registry, {
|
794 | info: TypeDefInfo.Tuple,
|
795 | lookupIndex,
|
796 | lookupName: this.__internal__names[lookupIndex],
|
797 | sub
|
798 | });
|
799 | }
|
800 |
|
801 | __internal__extractVariant(lookupIndex, { params, path }, { variants }) {
|
802 | if (path.length) {
|
803 | const specialVariant = path[0].toString();
|
804 | if (specialVariant === 'Option') {
|
805 | if (params.length !== 1) {
|
806 | throw new Error(`Option requires 1 parameter, found ${params.length}`);
|
807 | }
|
808 |
|
809 |
|
810 |
|
811 |
|
812 |
|
813 |
|
814 |
|
815 | return withTypeString(this.registry, {
|
816 | info: TypeDefInfo.Option,
|
817 | sub: this.__internal__createSiDef(params[0].type.unwrap())
|
818 | });
|
819 | }
|
820 | else if (specialVariant === 'Result') {
|
821 | if (params.length !== 2) {
|
822 | throw new Error(`Result requires 2 parameters, found ${params.length}`);
|
823 | }
|
824 | return withTypeString(this.registry, {
|
825 | info: TypeDefInfo.Result,
|
826 | sub: params.map(({ type }, index) => objectSpread({
|
827 | name: ['Ok', 'Error'][index]
|
828 | }, this.__internal__createSiDef(type.unwrap())))
|
829 | });
|
830 | }
|
831 | }
|
832 | if (variants.length === 0) {
|
833 | return {
|
834 | info: TypeDefInfo.Null,
|
835 | type: 'Null'
|
836 | };
|
837 | }
|
838 | return this.__internal__extractVariantEnum(lookupIndex, variants);
|
839 | }
|
840 |
|
841 | __internal__extractVariantEnum(lookupIndex, variants) {
|
842 | const sub = [];
|
843 |
|
844 |
|
845 | variants
|
846 | .slice()
|
847 | .sort((a, b) => a.index.cmp(b.index))
|
848 | .forEach(({ fields, index: bnIndex, name }) => {
|
849 | const index = bnIndex.toNumber();
|
850 | while (sub.length !== index) {
|
851 | sub.push({
|
852 | index: sub.length,
|
853 | info: TypeDefInfo.Null,
|
854 | name: `__Unused${sub.length}`,
|
855 | type: 'Null'
|
856 | });
|
857 | }
|
858 | sub.push(objectSpread(this.__internal__extractFields(-1, fields), {
|
859 | index,
|
860 | name: name.toString()
|
861 | }));
|
862 | });
|
863 | return withTypeString(this.registry, {
|
864 | info: TypeDefInfo.Enum,
|
865 | lookupIndex,
|
866 | lookupName: this.__internal__names[lookupIndex],
|
867 | sub
|
868 | });
|
869 | }
|
870 | }
|