@polkadot/types
Version:
Implementation of the Parity codec
365 lines (364 loc) • 12.5 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GenericExtrinsic = exports.LATEST_EXTRINSIC_VERSION = void 0;
const types_codec_1 = require("@polkadot/types-codec");
const util_1 = require("@polkadot/util");
const constants_js_1 = require("./constants.js");
Object.defineProperty(exports, "LATEST_EXTRINSIC_VERSION", { enumerable: true, get: function () { return constants_js_1.LATEST_EXTRINSIC_VERSION; } });
const VERSIONS = [
'ExtrinsicUnknown', // v0 is unknown
'ExtrinsicUnknown',
'ExtrinsicUnknown',
'ExtrinsicUnknown',
'ExtrinsicV4',
'ExtrinsicV5'
];
const PREAMBLE = {
bare: 'ExtrinsicV5',
general: 'GeneralExtrinsic'
};
const PreambleMask = {
bare: constants_js_1.BARE_EXTRINSIC,
general: constants_js_1.GENERAL_EXTRINSIC
};
const preambleUnMask = {
0: 'bare',
// eslint-disable-next-line sort-keys
64: 'general'
};
/** @internal */
function newFromValue(registry, value, version, preamble) {
if (value instanceof GenericExtrinsic) {
return value.unwrap();
}
const isSigned = (version & constants_js_1.BIT_SIGNED) === constants_js_1.BIT_SIGNED;
const type = (version & constants_js_1.VERSION_MASK) === 5 ? PREAMBLE[preamble] : VERSIONS[version & constants_js_1.VERSION_MASK] || VERSIONS[0];
// we cast here since the VERSION definition is incredibly broad - we don't have a
// slice for "only add extrinsic types", and more string definitions become unwieldy
return registry.createTypeUnsafe(type, [value, { isSigned, version }]);
}
/** @internal */
function decodeExtrinsic(registry, value, version = constants_js_1.LOWEST_SUPPORTED_EXTRINSIC_FORMAT_VERSION, preamble = constants_js_1.DEFAULT_PREAMBLE) {
if ((0, util_1.isU8a)(value) || Array.isArray(value) || (0, util_1.isHex)(value)) {
return decodeU8a(registry, (0, util_1.u8aToU8a)(value), version, preamble);
}
else if (value instanceof registry.createClassUnsafe('Call')) {
return newFromValue(registry, { method: value }, version, preamble);
}
return newFromValue(registry, value, version, preamble);
}
/** @internal */
function decodeU8a(registry, value, version, preamble) {
if (!value.length) {
return newFromValue(registry, new Uint8Array(), version, preamble);
}
const [offset, length] = (0, util_1.compactFromU8a)(value);
const total = offset + length.toNumber();
if (total > value.length) {
throw new Error(`Extrinsic: length less than remainder, expected at least ${total}, found ${value.length}`);
}
const data = value.subarray(offset, total);
const unmaskedPreamble = data[0] & constants_js_1.TYPE_MASK;
if (preambleUnMask[`${unmaskedPreamble}`] === 'general') {
// NOTE: GeneralExtrinsic needs to have the full data to validate the preamble and version
return newFromValue(registry, value, data[0], preambleUnMask[`${unmaskedPreamble}`] || preamble);
}
else {
return newFromValue(registry, data.subarray(1), data[0], preambleUnMask[`${unmaskedPreamble}`] || preamble);
}
}
class ExtrinsicBase extends types_codec_1.AbstractBase {
__internal__preamble;
constructor(registry, value, initialU8aLength, preamble) {
super(registry, value, initialU8aLength);
const signKeys = Object.keys(registry.getSignedExtensionTypes());
if (this.version === 5 && preamble !== 'general') {
const getter = (key) => this.inner.signature[key];
// This is on the abstract class, ensuring that hasOwnProperty operates
// correctly, i.e. it needs to be on the base class exposing it
for (let i = 0, count = signKeys.length; i < count; i++) {
(0, util_1.objectProperty)(this, signKeys[i], getter);
}
}
const unmaskedPreamble = this.type & constants_js_1.TYPE_MASK;
this.__internal__preamble = preamble || preambleUnMask[`${unmaskedPreamble}`];
}
isGeneral() {
return this.__internal__preamble === 'general';
}
/**
* @description The arguments passed to for the call, exposes args so it is compatible with [[Call]]
*/
get args() {
return this.method.args;
}
/**
* @description The argument definitions, compatible with [[Call]]
*/
get argsDef() {
return this.method.argsDef;
}
/**
* @description The actual `[sectionIndex, methodIndex]` as used in the Call
*/
get callIndex() {
return this.method.callIndex;
}
/**
* @description The actual data for the Call
*/
get data() {
return this.method.data;
}
/**
* @description The era for this extrinsic
*/
get era() {
return this.isGeneral()
? this.inner.era
: this.inner.signature.era;
}
/**
* @description The length of the value when encoded as a Uint8Array
*/
get encodedLength() {
return this.toU8a().length;
}
/**
* @description `true` id the extrinsic is signed
*/
get isSigned() {
return this.isGeneral()
? false
: this.inner.signature.isSigned;
}
/**
* @description The length of the actual data, excluding prefix
*/
get length() {
return this.toU8a(true).length;
}
/**
* @description The [[FunctionMetadataLatest]] that describes the extrinsic
*/
get meta() {
return this.method.meta;
}
/**
* @description The [[Call]] this extrinsic wraps
*/
get method() {
return this.inner.method;
}
/**
* @description The nonce for this extrinsic
*/
get nonce() {
return this.isGeneral()
? this.inner.nonce
: this.inner.signature.nonce;
}
/**
* @description The actual [[EcdsaSignature]], [[Ed25519Signature]] or [[Sr25519Signature]]
*/
get signature() {
if (this.isGeneral()) {
throw new Error('Extrinsic: GeneralExtrinsic does not have signature implemented');
}
return this.inner.signature.signature;
}
/**
* @description The [[Address]] that signed
*/
get signer() {
if (this.isGeneral()) {
throw new Error('Extrinsic: GeneralExtrinsic does not have signer implemented');
}
return this.inner.signature.signer;
}
/**
* @description Forwards compat
*/
get tip() {
return this.isGeneral()
? this.inner.tip
: this.inner.signature.tip;
}
/**
* @description Forward compat
*/
get assetId() {
return this.isGeneral()
? this.inner.assetId
: this.inner.signature.assetId;
}
/**
* @description Forward compat
*/
get metadataHash() {
return this.isGeneral()
? this.inner.metadataHash
: this.inner.signature.metadataHash;
}
/**
* @description Forward compat
*/
get mode() {
return this.isGeneral()
? this.inner.mode
: this.inner.signature.mode;
}
/**
* @description Returns the raw transaction version (not flagged with signing information)
*/
get type() {
return this.inner.version;
}
get inner() {
return this.unwrap();
}
/**
* @description Returns the encoded version flag
*/
get version() {
if (this.type <= constants_js_1.LOWEST_SUPPORTED_EXTRINSIC_FORMAT_VERSION) {
return this.type | (this.isSigned ? constants_js_1.BIT_SIGNED : constants_js_1.BIT_UNSIGNED);
}
else {
if (this.isSigned) {
throw new Error('Signed Extrinsics are currently only available for ExtrinsicV4');
}
return this.type | (this.isGeneral() ? PreambleMask.general : PreambleMask.bare);
}
}
/**
* @description Checks if the source matches this in type
*/
is(other) {
return this.method.is(other);
}
unwrap() {
return super.unwrap();
}
}
/**
* @name GenericExtrinsic
* @description
* Representation of an Extrinsic in the system. It contains the actual call,
* (optional) signature and encodes with an actual length prefix
*
* {@link https://github.com/paritytech/wiki/blob/master/Extrinsic.md#the-extrinsic-format-for-node}.
*
* Can be:
* - signed, to create a transaction
* - left as is, to create an inherent
*/
class GenericExtrinsic extends ExtrinsicBase {
__internal__hashCache;
static LATEST_EXTRINSIC_VERSION = constants_js_1.LATEST_EXTRINSIC_VERSION;
constructor(registry, value, { preamble, version } = {}) {
super(registry, decodeExtrinsic(registry, value, version || registry.metadata.extrinsic.version?.toNumber(), preamble), undefined, preamble);
}
/**
* @description returns a hash of the contents
*/
get hash() {
if (!this.__internal__hashCache) {
this.__internal__hashCache = super.hash;
}
return this.__internal__hashCache;
}
/**
* @description Injects an already-generated signature into the extrinsic
*/
addSignature(signer, signature, payload) {
this.inner.addSignature(signer, signature, payload);
this.__internal__hashCache = undefined;
return this;
}
/**
* @description Returns a breakdown of the hex encoding for this Codec
*/
inspect() {
const encoded = (0, util_1.u8aConcat)(...this.toU8aInner());
return {
inner: this.isSigned
? this.inner.inspect().inner
: this.inner.method.inspect().inner,
outer: [(0, util_1.compactToU8a)(encoded.length), new Uint8Array([this.version])]
};
}
/**
* @description Sign the extrinsic with a specific keypair
*/
sign(account, options) {
this.inner.sign(account, options);
this.__internal__hashCache = undefined;
return this;
}
/**
* @describe Adds a fake signature to the extrinsic
*/
signFake(signer, options) {
this.inner.signFake(signer, options);
this.__internal__hashCache = undefined;
return this;
}
/**
* @description Returns a hex string representation of the value
*/
toHex(isBare) {
return (0, util_1.u8aToHex)(this.toU8a(isBare));
}
/**
* @description Converts the Object to to a human-friendly JSON, with additional fields, expansion and formatting of information
*/
toHuman(isExpanded, disableAscii) {
return (0, util_1.objectSpread)({}, {
isSigned: this.isSigned,
method: this.method.toHuman(isExpanded, disableAscii)
}, this.isSigned
? {
assetId: this.assetId ? this.assetId.toHuman(isExpanded, disableAscii) : null,
era: this.era.toHuman(isExpanded, disableAscii),
metadataHash: this.metadataHash ? this.metadataHash.toHex() : null,
mode: this.mode ? this.mode.toHuman() : null,
nonce: this.nonce.toHuman(isExpanded, disableAscii),
signature: this.signature.toHex(),
signer: this.signer.toHuman(isExpanded, disableAscii),
tip: this.tip.toHuman(isExpanded, disableAscii)
}
: null);
}
/**
* @description Converts the Object to JSON, typically used for RPC transfers
*/
toJSON() {
return this.toHex();
}
/**
* @description Returns the base runtime type name for this instance
*/
toRawType() {
return 'Extrinsic';
}
/**
* @description Encodes the value as a Uint8Array as per the SCALE specifications
* @param isBare true when the value is not length-prefixed
*/
toU8a(isBare) {
const encoded = (0, util_1.u8aConcat)(...this.toU8aInner());
return isBare
? encoded
: (0, util_1.compactAddLength)(encoded);
}
toU8aInner() {
// we do not apply bare to the internal values, rather this only determines out length addition,
// where we strip all lengths this creates an extrinsic that cannot be decoded
return [
new Uint8Array([this.version]),
this.inner.toU8a()
];
}
}
exports.GenericExtrinsic = GenericExtrinsic;