UNPKG

@polkadot/types

Version:
167 lines (166 loc) 5.77 kB
import { Struct } from '@polkadot/types-codec'; import { isU8a, isUndefined, objectProperties, objectSpread, stringify, u8aToHex } from '@polkadot/util'; import { EMPTY_U8A, IMMORTAL_ERA } from '../constants.js'; import { GenericExtrinsicPayloadV4 } from './ExtrinsicPayload.js'; const FAKE_SIGNATURE = new Uint8Array(256).fill(1); function toAddress(registry, address) { return registry.createTypeUnsafe('Address', [isU8a(address) ? u8aToHex(address) : address]); } /** * @name GenericExtrinsicSignatureV4 * @description * A container for the [[Signature]] associated with a specific [[Extrinsic]] */ export class GenericExtrinsicSignatureV4 extends Struct { __internal__signKeys; constructor(registry, value, { isSigned } = {}) { const signTypes = registry.getSignedExtensionTypes(); super(registry, objectSpread( // eslint-disable-next-line sort-keys { signer: 'Address', signature: 'ExtrinsicSignature' }, signTypes), GenericExtrinsicSignatureV4.decodeExtrinsicSignature(value, isSigned)); this.__internal__signKeys = Object.keys(signTypes); objectProperties(this, this.__internal__signKeys, (k) => this.get(k)); } /** @internal */ static decodeExtrinsicSignature(value, isSigned = false) { if (!value) { return EMPTY_U8A; } else if (value instanceof GenericExtrinsicSignatureV4) { return value; } return isSigned ? value : EMPTY_U8A; } /** * @description The length of the value when encoded as a Uint8Array */ get encodedLength() { return this.isSigned ? super.encodedLength : 0; } /** * @description `true` if the signature is valid */ get isSigned() { return !this.signature.isEmpty; } /** * @description The [[ExtrinsicEra]] (mortal or immortal) this signature applies to */ get era() { return this.getT('era'); } /** * @description The [[Index]] for the signature */ get nonce() { return this.getT('nonce'); } /** * @description The actual [[EcdsaSignature]], [[Ed25519Signature]] or [[Sr25519Signature]] */ get signature() { // the second case here is when we don't have an enum signature, treat as raw return (this.multiSignature.value || this.multiSignature); } /** * @description The raw [[ExtrinsicSignature]] */ get multiSignature() { return this.getT('signature'); } /** * @description The [[Address]] that signed */ get signer() { return this.getT('signer'); } /** * @description The [[Balance]] tip */ get tip() { return this.getT('tip'); } /** * @description The [[u32]] or [[MultiLocation]] assetId */ get assetId() { return this.getT('assetId'); } /** * @description the [[u32]] mode */ get mode() { return this.getT('mode'); } /** * @description The [[Hash]] for the metadata */ get metadataHash() { return this.getT('metadataHash'); } _injectSignature(signer, signature, payload) { // use the fields exposed to guide the getters for (let i = 0, count = this.__internal__signKeys.length; i < count; i++) { const k = this.__internal__signKeys[i]; const v = payload.get(k); if (!isUndefined(v)) { this.set(k, v); } } // additional fields (exposed in struct itself) this.set('signer', signer); this.set('signature', signature); return this; } /** * @description Adds a raw signature */ addSignature(signer, signature, payload) { return this._injectSignature(toAddress(this.registry, signer), this.registry.createTypeUnsafe('ExtrinsicSignature', [signature]), new GenericExtrinsicPayloadV4(this.registry, payload)); } /** * @description Creates a payload from the supplied options */ createPayload(method, options) { const { era, runtimeVersion: { specVersion, transactionVersion } } = options; return new GenericExtrinsicPayloadV4(this.registry, objectSpread({}, options, { era: era || IMMORTAL_ERA, method: method.toHex(), specVersion, transactionVersion })); } /** * @description Generate a payload and applies the signature from a keypair */ sign(method, account, options) { if (!account?.addressRaw) { throw new Error(`Expected a valid keypair for signing, found ${stringify(account)}`); } const payload = this.createPayload(method, options); return this._injectSignature(toAddress(this.registry, account.addressRaw), this.registry.createTypeUnsafe('ExtrinsicSignature', [payload.sign(account)]), payload); } /** * @description Generate a payload and applies a fake signature */ signFake(method, address, options) { if (!address) { throw new Error(`Expected a valid address for signing, found ${stringify(address)}`); } const payload = this.createPayload(method, options); return this._injectSignature(toAddress(this.registry, address), this.registry.createTypeUnsafe('ExtrinsicSignature', [FAKE_SIGNATURE]), payload); } /** * @description Encodes the value as a Uint8Array as per the SCALE specifications * @param isBare true when the value has none of the type-specific prefixes (internal) */ toU8a(isBare) { return this.isSigned ? super.toU8a(isBare) : EMPTY_U8A; } }