UNPKG

5.69 kBJavaScriptView Raw
1import { Struct, U8aFixed } from '@polkadot/types-codec';
2import { isHex, isObject, isU8a, objectSpread, u8aToU8a } from '@polkadot/util';
3/**
4 * Get a mapping of `argument name -> argument type` for the function, from
5 * its metadata.
6 *
7 * @param meta - The function metadata used to get the definition.
8 * @internal
9 */
10function getArgsDef(registry, meta) {
11 return meta.fields.reduce((result, { name, type }, index) => {
12 result[name.unwrapOr(`param${index}`).toString()] = registry.createLookupType(type);
13 return result;
14 }, {});
15}
16/** @internal */
17function decodeCallViaObject(registry, value, _meta) {
18 // we only pass args/methodsIndex out
19 const { args, callIndex } = value;
20 // Get the correct lookupIndex
21 // eslint-disable-next-line @typescript-eslint/no-use-before-define
22 const lookupIndex = callIndex instanceof GenericCallIndex
23 ? callIndex.toU8a()
24 : callIndex;
25 // Find metadata with callIndex
26 const meta = _meta || registry.findMetaCall(lookupIndex).meta;
27 return {
28 args,
29 argsDef: getArgsDef(registry, meta),
30 callIndex,
31 meta
32 };
33}
34/** @internal */
35function decodeCallViaU8a(registry, value, _meta) {
36 // We need 2 bytes for the callIndex
37 const callIndex = registry.firstCallIndex.slice();
38 callIndex.set(value.subarray(0, 2), 0);
39 // Find metadata with callIndex
40 const meta = _meta || registry.findMetaCall(callIndex).meta;
41 return {
42 args: value.subarray(2),
43 argsDef: getArgsDef(registry, meta),
44 callIndex,
45 meta
46 };
47}
48/**
49 * Decode input to pass into constructor.
50 *
51 * @param value - Value to decode, one of:
52 * - hex
53 * - Uint8Array
54 * - {@see DecodeMethodInput}
55 * @param _meta - Metadata to use, so that `injectMethods` lookup is not
56 * necessary.
57 * @internal
58 */
59function decodeCall(registry, value = new Uint8Array(), _meta) {
60 if (isU8a(value) || isHex(value)) {
61 return decodeCallViaU8a(registry, u8aToU8a(value), _meta);
62 }
63 else if (isObject(value) && value.callIndex && value.args) {
64 return decodeCallViaObject(registry, value, _meta);
65 }
66 throw new Error(`Call: Cannot decode value '${value}' of type ${typeof value}`);
67}
68/**
69 * @name GenericCallIndex
70 * @description
71 * A wrapper around the `[sectionIndex, methodIndex]` value that uniquely identifies a method
72 */
73export class GenericCallIndex extends U8aFixed {
74 constructor(registry, value) {
75 super(registry, value, 16);
76 }
77 /**
78 * @description Converts the value in a best-fit primitive form
79 */
80 toPrimitive() {
81 return this.toHex();
82 }
83}
84/**
85 * @name GenericCall
86 * @description
87 * Extrinsic function descriptor
88 */
89export class GenericCall extends Struct {
90 _meta;
91 constructor(registry, value, meta) {
92 const decoded = decodeCall(registry, value, meta);
93 try {
94 super(registry, {
95 callIndex: GenericCallIndex,
96 // eslint-disable-next-line sort-keys
97 args: Struct.with(decoded.argsDef)
98 }, decoded);
99 }
100 catch (error) {
101 let method = 'unknown.unknown';
102 try {
103 const c = registry.findMetaCall(decoded.callIndex);
104 method = `${c.section}.${c.method}`;
105 }
106 catch {
107 // ignore
108 }
109 throw new Error(`Call: failed decoding ${method}:: ${error.message}`);
110 }
111 this._meta = decoded.meta;
112 }
113 /**
114 * @description The arguments for the function call
115 */
116 get args() {
117 return [...this.getT('args').values()];
118 }
119 /**
120 * @description The argument definitions
121 */
122 get argsDef() {
123 return getArgsDef(this.registry, this.meta);
124 }
125 /**
126 * @description The argument entries
127 */
128 get argsEntries() {
129 return [...this.getT('args').entries()];
130 }
131 /**
132 * @description The encoded `[sectionIndex, methodIndex]` identifier
133 */
134 get callIndex() {
135 return this.getT('callIndex').toU8a();
136 }
137 /**
138 * @description The encoded data
139 */
140 get data() {
141 return this.getT('args').toU8a();
142 }
143 /**
144 * @description The [[FunctionMetadata]]
145 */
146 get meta() {
147 return this._meta;
148 }
149 /**
150 * @description Returns the name of the method
151 */
152 get method() {
153 return this.registry.findMetaCall(this.callIndex).method;
154 }
155 /**
156 * @description Returns the module containing the method
157 */
158 get section() {
159 return this.registry.findMetaCall(this.callIndex).section;
160 }
161 /**
162 * @description Checks if the source matches this in type
163 */
164 is(other) {
165 return other.callIndex[0] === this.callIndex[0] && other.callIndex[1] === this.callIndex[1];
166 }
167 /**
168 * @description Converts the Object to to a human-friendly JSON, with additional fields, expansion and formatting of information
169 */
170 toHuman(isExpanded, disableAscii) {
171 let call;
172 try {
173 call = this.registry.findMetaCall(this.callIndex);
174 }
175 catch {
176 // swallow
177 }
178 return objectSpread({
179 args: this.argsEntries.reduce((args, [n, a]) => objectSpread(args, { [n]: a.toHuman(isExpanded, disableAscii) }), {}),
180 method: call?.method,
181 section: call?.section
182 }, isExpanded && call
183 ? { docs: call.meta.docs.map((d) => d.toString()) }
184 : null);
185 }
186 /**
187 * @description Returns the base runtime type name for this instance
188 */
189 toRawType() {
190 return 'Call';
191 }
192}