UNPKG

12.1 kBJavaScriptView Raw
1import { AbstractBase } from '@polkadot/types-codec';
2import { compactAddLength, compactFromU8a, compactToU8a, isHex, isU8a, objectProperty, objectSpread, u8aConcat, u8aToHex, u8aToU8a } from '@polkadot/util';
3import { BARE_EXTRINSIC, BIT_SIGNED, BIT_UNSIGNED, DEFAULT_PREAMBLE, GENERAL_EXTRINSIC, LATEST_EXTRINSIC_VERSION, LOWEST_SUPPORTED_EXTRINSIC_FORMAT_VERSION, SIGNED_EXTRINSIC, TYPE_MASK, VERSION_MASK } from './constants.js';
4const VERSIONS = [
5 'ExtrinsicUnknown', // v0 is unknown
6 'ExtrinsicUnknown',
7 'ExtrinsicUnknown',
8 'ExtrinsicUnknown',
9 'ExtrinsicV4',
10 'ExtrinsicV5'
11];
12const PREAMBLE = {
13 bare: 'ExtrinsicV5',
14 general: 'GeneralExtrinsic',
15 signed: 'ExtrinsicV5'
16};
17const PreambleMask = {
18 bare: BARE_EXTRINSIC,
19 general: GENERAL_EXTRINSIC,
20 signed: SIGNED_EXTRINSIC
21};
22const preambleUnMask = {
23 0: 'bare',
24 // eslint-disable-next-line sort-keys
25 64: 'general',
26 // eslint-disable-next-line sort-keys
27 128: 'signed'
28};
29export { LATEST_EXTRINSIC_VERSION };
30/** @internal */
31function newFromValue(registry, value, version, preamble) {
32 if (value instanceof GenericExtrinsic) {
33 return value.unwrap();
34 }
35 const isSigned = (version & BIT_SIGNED) === BIT_SIGNED;
36 const type = (version & VERSION_MASK) === 5 ? PREAMBLE[preamble] : VERSIONS[version & VERSION_MASK] || VERSIONS[0];
37 // we cast here since the VERSION definition is incredibly broad - we don't have a
38 // slice for "only add extrinsic types", and more string definitions become unwieldy
39 return registry.createTypeUnsafe(type, [value, { isSigned, version }]);
40}
41/** @internal */
42function decodeExtrinsic(registry, value, version = LOWEST_SUPPORTED_EXTRINSIC_FORMAT_VERSION, preamble = DEFAULT_PREAMBLE) {
43 if (isU8a(value) || Array.isArray(value) || isHex(value)) {
44 return decodeU8a(registry, u8aToU8a(value), version, preamble);
45 }
46 else if (value instanceof registry.createClassUnsafe('Call')) {
47 return newFromValue(registry, { method: value }, version, preamble);
48 }
49 return newFromValue(registry, value, version, preamble);
50}
51/** @internal */
52function decodeU8a(registry, value, version, preamble) {
53 if (!value.length) {
54 return newFromValue(registry, new Uint8Array(), version, preamble);
55 }
56 const [offset, length] = compactFromU8a(value);
57 const total = offset + length.toNumber();
58 if (total > value.length) {
59 throw new Error(`Extrinsic: length less than remainder, expected at least ${total}, found ${value.length}`);
60 }
61 const data = value.subarray(offset, total);
62 const unmaskedPreamble = data[0] & TYPE_MASK;
63 if (preambleUnMask[`${unmaskedPreamble}`] === 'general') {
64 // NOTE: GeneralExtrinsic needs to have the full data to validate the preamble and version
65 return newFromValue(registry, value, data[0], preambleUnMask[`${unmaskedPreamble}`] || preamble);
66 }
67 else {
68 return newFromValue(registry, data.subarray(1), data[0], preambleUnMask[`${unmaskedPreamble}`] || preamble);
69 }
70}
71class ExtrinsicBase extends AbstractBase {
72 __internal__preamble;
73 constructor(registry, value, initialU8aLength, preamble) {
74 super(registry, value, initialU8aLength);
75 const signKeys = Object.keys(registry.getSignedExtensionTypes());
76 if (this.version === 5 && preamble !== 'general') {
77 const getter = (key) => this.inner.signature[key];
78 // This is on the abstract class, ensuring that hasOwnProperty operates
79 // correctly, i.e. it needs to be on the base class exposing it
80 for (let i = 0, count = signKeys.length; i < count; i++) {
81 objectProperty(this, signKeys[i], getter);
82 }
83 }
84 const unmaskedPreamble = this.type & TYPE_MASK;
85 this.__internal__preamble = preamble || preambleUnMask[`${unmaskedPreamble}`];
86 }
87 isGeneral() {
88 return this.__internal__preamble === 'general';
89 }
90 /**
91 * @description The arguments passed to for the call, exposes args so it is compatible with [[Call]]
92 */
93 get args() {
94 return this.method.args;
95 }
96 /**
97 * @description The argument definitions, compatible with [[Call]]
98 */
99 get argsDef() {
100 return this.method.argsDef;
101 }
102 /**
103 * @description The actual `[sectionIndex, methodIndex]` as used in the Call
104 */
105 get callIndex() {
106 return this.method.callIndex;
107 }
108 /**
109 * @description The actual data for the Call
110 */
111 get data() {
112 return this.method.data;
113 }
114 /**
115 * @description The era for this extrinsic
116 */
117 get era() {
118 return this.isGeneral()
119 ? this.inner.era
120 : this.inner.signature.era;
121 }
122 /**
123 * @description The length of the value when encoded as a Uint8Array
124 */
125 get encodedLength() {
126 return this.toU8a().length;
127 }
128 /**
129 * @description `true` id the extrinsic is signed
130 */
131 get isSigned() {
132 return this.isGeneral()
133 ? false
134 : this.inner.signature.isSigned;
135 }
136 /**
137 * @description The length of the actual data, excluding prefix
138 */
139 get length() {
140 return this.toU8a(true).length;
141 }
142 /**
143 * @description The [[FunctionMetadataLatest]] that describes the extrinsic
144 */
145 get meta() {
146 return this.method.meta;
147 }
148 /**
149 * @description The [[Call]] this extrinsic wraps
150 */
151 get method() {
152 return this.inner.method;
153 }
154 /**
155 * @description The nonce for this extrinsic
156 */
157 get nonce() {
158 return this.isGeneral()
159 ? this.inner.nonce
160 : this.inner.signature.nonce;
161 }
162 /**
163 * @description The actual [[EcdsaSignature]], [[Ed25519Signature]] or [[Sr25519Signature]]
164 */
165 get signature() {
166 if (this.isGeneral()) {
167 throw new Error('Extrinsic: GeneralExtrinsic does not have signature implemented');
168 }
169 return this.inner.signature.signature;
170 }
171 /**
172 * @description The [[Address]] that signed
173 */
174 get signer() {
175 if (this.isGeneral()) {
176 throw new Error('Extrinsic: GeneralExtrinsic does not have signer implemented');
177 }
178 return this.inner.signature.signer;
179 }
180 /**
181 * @description Forwards compat
182 */
183 get tip() {
184 return this.isGeneral()
185 ? this.inner.tip
186 : this.inner.signature.tip;
187 }
188 /**
189 * @description Forward compat
190 */
191 get assetId() {
192 return this.isGeneral()
193 ? this.inner.assetId
194 : this.inner.signature.assetId;
195 }
196 /**
197 * @description Forward compat
198 */
199 get metadataHash() {
200 return this.isGeneral()
201 ? this.inner.metadataHash
202 : this.inner.signature.metadataHash;
203 }
204 /**
205 * @description Forward compat
206 */
207 get mode() {
208 return this.isGeneral()
209 ? this.inner.mode
210 : this.inner.signature.mode;
211 }
212 /**
213 * @description Returns the raw transaction version (not flagged with signing information)
214 */
215 get type() {
216 return this.inner.version;
217 }
218 get inner() {
219 return this.unwrap();
220 }
221 /**
222 * @description Returns the encoded version flag
223 */
224 get version() {
225 if (this.type <= LOWEST_SUPPORTED_EXTRINSIC_FORMAT_VERSION) {
226 return this.type | (this.isSigned ? BIT_SIGNED : BIT_UNSIGNED);
227 }
228 else {
229 return this.type | (this.isSigned ? PreambleMask.signed : this.isGeneral() ? PreambleMask.general : PreambleMask.bare);
230 }
231 }
232 /**
233 * @description Checks if the source matches this in type
234 */
235 is(other) {
236 return this.method.is(other);
237 }
238 unwrap() {
239 return super.unwrap();
240 }
241}
242/**
243 * @name GenericExtrinsic
244 * @description
245 * Representation of an Extrinsic in the system. It contains the actual call,
246 * (optional) signature and encodes with an actual length prefix
247 *
248 * {@link https://github.com/paritytech/wiki/blob/master/Extrinsic.md#the-extrinsic-format-for-node}.
249 *
250 * Can be:
251 * - signed, to create a transaction
252 * - left as is, to create an inherent
253 */
254export class GenericExtrinsic extends ExtrinsicBase {
255 __internal__hashCache;
256 static LATEST_EXTRINSIC_VERSION = LATEST_EXTRINSIC_VERSION;
257 constructor(registry, value, { preamble, version } = {}) {
258 super(registry, decodeExtrinsic(registry, value, version || registry.metadata.extrinsic.version?.toNumber(), preamble), undefined, preamble);
259 }
260 /**
261 * @description returns a hash of the contents
262 */
263 get hash() {
264 if (!this.__internal__hashCache) {
265 this.__internal__hashCache = super.hash;
266 }
267 return this.__internal__hashCache;
268 }
269 /**
270 * @description Injects an already-generated signature into the extrinsic
271 */
272 addSignature(signer, signature, payload) {
273 this.inner.addSignature(signer, signature, payload);
274 this.__internal__hashCache = undefined;
275 return this;
276 }
277 /**
278 * @description Returns a breakdown of the hex encoding for this Codec
279 */
280 inspect() {
281 const encoded = u8aConcat(...this.toU8aInner());
282 return {
283 inner: this.isSigned
284 ? this.inner.inspect().inner
285 : this.inner.method.inspect().inner,
286 outer: [compactToU8a(encoded.length), new Uint8Array([this.version])]
287 };
288 }
289 /**
290 * @description Sign the extrinsic with a specific keypair
291 */
292 sign(account, options) {
293 this.inner.sign(account, options);
294 this.__internal__hashCache = undefined;
295 return this;
296 }
297 /**
298 * @describe Adds a fake signature to the extrinsic
299 */
300 signFake(signer, options) {
301 this.inner.signFake(signer, options);
302 this.__internal__hashCache = undefined;
303 return this;
304 }
305 /**
306 * @description Returns a hex string representation of the value
307 */
308 toHex(isBare) {
309 return u8aToHex(this.toU8a(isBare));
310 }
311 /**
312 * @description Converts the Object to to a human-friendly JSON, with additional fields, expansion and formatting of information
313 */
314 toHuman(isExpanded, disableAscii) {
315 return objectSpread({}, {
316 isSigned: this.isSigned,
317 method: this.method.toHuman(isExpanded, disableAscii)
318 }, this.isSigned
319 ? {
320 assetId: this.assetId ? this.assetId.toHuman(isExpanded, disableAscii) : null,
321 era: this.era.toHuman(isExpanded, disableAscii),
322 metadataHash: this.metadataHash ? this.metadataHash.toHex() : null,
323 mode: this.mode ? this.mode.toHuman() : null,
324 nonce: this.nonce.toHuman(isExpanded, disableAscii),
325 signature: this.signature.toHex(),
326 signer: this.signer.toHuman(isExpanded, disableAscii),
327 tip: this.tip.toHuman(isExpanded, disableAscii)
328 }
329 : null);
330 }
331 /**
332 * @description Converts the Object to JSON, typically used for RPC transfers
333 */
334 toJSON() {
335 return this.toHex();
336 }
337 /**
338 * @description Returns the base runtime type name for this instance
339 */
340 toRawType() {
341 return 'Extrinsic';
342 }
343 /**
344 * @description Encodes the value as a Uint8Array as per the SCALE specifications
345 * @param isBare true when the value is not length-prefixed
346 */
347 toU8a(isBare) {
348 const encoded = u8aConcat(...this.toU8aInner());
349 return isBare
350 ? encoded
351 : compactAddLength(encoded);
352 }
353 toU8aInner() {
354 // we do not apply bare to the internal values, rather this only determines out length addition,
355 // where we strip all lengths this creates an extrinsic that cannot be decoded
356 return [
357 new Uint8Array([this.version]),
358 this.inner.toU8a()
359 ];
360 }
361}