Skip to content

Commit

Permalink
Ensure assetId is handled correctly when addSignature is used (#6083)
Browse files Browse the repository at this point in the history
* Ensure assetId is handled correctly when addSignature is used

* Adjust test to mirror real failure

* test empty input on GenericExtrinsicPayload
  • Loading branch information
TarikGul authored Jan 29, 2025
1 parent ad6f3d4 commit c129dda
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 39 deletions.
18 changes: 18 additions & 0 deletions packages/types/src/extrinsic/ExtrinsicPayload.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,22 @@ describe('ExtrinsicPayload', (): void => {
'0x940600ffd7568e5f0a7eda67a82691ff379ac4bba4f9c9b859fe779b5d46363b61ad2db9e56c0703d148e25901007b000000dcd1346701ca8396496e52aa2785b1748deb6db09551b72159dcb3e08991025bde8f69eeb5e065e18c6950ff708d7e551f68dc9bf59a07c52367c0280f805ec7'
);
});

it('has a sane inspect of an empty value', (): void => {
const reg = new TypeRegistry();

reg.setSignedExtensions(fallbackExtensions.concat(['ChargeAssetTxPayment']));
expect(new ExtrinsicPayload(reg, undefined).inspect()).toEqual({
inner: [
{ name: 'method', outer: [new Uint8Array()] },
{ inner: undefined, name: 'era', outer: [new Uint8Array([0]), new Uint8Array([0])] },
{ name: 'nonce', outer: [new Uint8Array([0])] },
{ name: 'tip', outer: [new Uint8Array([0])] },
{ name: 'assetId', outer: [new Uint8Array([0])] },
{ name: 'specVersion', outer: [new Uint8Array([0, 0, 0, 0])] },
{ name: 'genesisHash', outer: [new Uint8Array(32)] },
{ name: 'blockHash', outer: [new Uint8Array(32)] }
]
});
});
});
54 changes: 29 additions & 25 deletions packages/types/src/extrinsic/ExtrinsicPayload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,39 +39,43 @@ const PREAMBLES = {
general: 'ExtrinsicPayloadV5'
};

/** @internal */
function decodeExtrinsicPayload (registry: Registry, value?: GenericExtrinsicPayload | ExtrinsicPayloadValue | Uint8Array | string, version = LATEST_EXTRINSIC_VERSION, preamble: Preamble = DEFAULT_PREAMBLE): ExtrinsicPayloadVx {
if (value instanceof GenericExtrinsicPayload) {
return value.unwrap();
}

const extVersion = version === 5 ? PREAMBLES[preamble] : VERSIONS[version] || VERSIONS[0];
/**
* HACK: In order to change the assetId from `number | object` to HexString (While maintaining the true type ie Option<TAssetConversion>),
* to allow for easier generalization of the SignerPayloadJSON interface the below check is necessary. The ExtrinsicPayloadV4 class does not like
* a value passed in as an Option, and can't decode it properly. Therefore, we ensure to convert the following below, and then pass the option as a unwrapped
* JSON value.
*
* ref: https://github.com/polkadot-js/api/pull/5968
* ref: https://github.com/polkadot-js/api/pull/5967
*/
export function decodeAssetId (registry: Registry, payload?: ExtrinsicPayloadValue | Uint8Array | HexString) {
const maybeAsset = (payload as ExtrinsicPayloadValue)?.assetId;

/**
* HACK: In order to change the assetId from `number | object` to HexString (While maintaining the true type ie Option<TAssetConversion>),
* to allow for easier generalization of the SignerPayloadJSON interface the below check is necessary. The ExtrinsicPayloadV4 class does not like
* a value passed in as an Option, and can't decode it properly. Therefore, we ensure to convert the following below, and then pass the option as a unwrapped
* JSON value.
*
* ref: https://github.com/polkadot-js/api/pull/5968
* ref: https://github.com/polkadot-js/api/pull/5967
*/
if (value && (value as ExtrinsicPayloadValue).assetId && isHex((value as ExtrinsicPayloadValue).assetId)) {
const assetId = registry.createType('TAssetConversion', hexToU8a((value as ExtrinsicPayloadValue).assetId));
if (maybeAsset && isHex(maybeAsset)) {
const assetId = registry.createType('TAssetConversion', hexToU8a(maybeAsset));

// we only want to adjust the payload if the hex passed has the option
if ((value as ExtrinsicPayloadValue).assetId === '0x00' ||
(value as ExtrinsicPayloadValue).assetId === '0x01' + assetId.toHex().slice(2)) {
const adjustedPayload = {
...(value as ExtrinsicPayloadValue),
if (maybeAsset === '0x00' || maybeAsset === '0x01' + assetId.toHex().slice(2)) {
return {
...(payload as ExtrinsicPayloadValue),
assetId: assetId.toJSON()
};

return registry.createTypeUnsafe(extVersion, [adjustedPayload, { version }]);
}
}

return registry.createTypeUnsafe(extVersion, [value, { version }]);
return payload;
}

/** @internal */
function decodeExtrinsicPayload (registry: Registry, value?: GenericExtrinsicPayload | ExtrinsicPayloadValue | Uint8Array | string, version = LATEST_EXTRINSIC_VERSION, preamble: Preamble = DEFAULT_PREAMBLE): ExtrinsicPayloadVx {
if (value instanceof GenericExtrinsicPayload) {
return value.unwrap();
}

const extVersion = version === 5 ? PREAMBLES[preamble] : VERSIONS[version] || VERSIONS[0];
const payload = decodeAssetId(registry, value as ExtrinsicPayloadValue);

return registry.createTypeUnsafe(extVersion, [payload, { version }]);
}

/**
Expand Down
26 changes: 13 additions & 13 deletions packages/types/src/extrinsic/v4/ExtrinsicPayload.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,24 +38,24 @@ describe('ExtrinsicPayload', (): void => {
});
});

it('correctly decodes assetId', () => {
const TEST_WITH_ASSET = {
address: '5DTestUPts3kjeXSTMyerHihn1uwMfLj8vU8sqF7qYrFabHE',
assetId: '0x0002043205011f' as `0x${string}`,
blockHash: '0xde8f69eeb5e065e18c6950ff708d7e551f68dc9bf59a07c52367c0280f805ec7',
era: '0x0703',
genesisHash: '0xdcd1346701ca8396496e52aa2785b1748deb6db09551b72159dcb3e08991025b',
method: '0x0600ffd7568e5f0a7eda67a82691ff379ac4bba4f9c9b859fe779b5d46363b61ad2db9e56c',
nonce: '0x00001234',
specVersion: 123,
tip: '0x00000000000000000000000000005678',
transactionVersion: '0x00'
it('Correctly decodes assetId', () => {
const TEST_VALUE = {
address: 'J97drEQy6sYPXf2D1uj1hJfeHsxjvwr4tVGKs9o8VDSht8r',
assetId: '0x010002043205011f' as `0x${string}`,
blockHash: '0x28a464e6b40fccec3b9e7989db97d2627d3653c644a3c801f8239910eaaa58a8',
era: '0x4401',
genesisHash: '0x48239ef607d7928874027a43a67689209727dfb3d3dc5e5b03a39bdc2eda771a',
method: '0x0a0300d27001b334c34489c67b81dfbbdc86eba5b433163bd08226d89b081914e9aa490284d717',
nonce: '0x0000000a',
specVersion: '0x000f4dfc',
tip: '0x00000000000000000000000000000000',
transactionVersion: '0x0000000f'
};

const reg = new TypeRegistry();

reg.setSignedExtensions(fallbackExtensions.concat(['ChargeAssetTxPayment']));
const ext = new ExtrinsicPayload(reg, TEST_WITH_ASSET);
const ext = new ExtrinsicPayload(reg, TEST_VALUE);

expect(ext.assetId.toJSON()).toEqual({
interior: {
Expand Down
3 changes: 2 additions & 1 deletion packages/types/src/extrinsic/v4/ExtrinsicPayload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type { ExtrinsicPayloadValue, ICompact, IKeyringPair, INumber, IOption }
import { Enum, Struct } from '@polkadot/types-codec';
import { objectSpread } from '@polkadot/util';

import { decodeAssetId } from '../ExtrinsicPayload.js';
import { sign } from '../util.js';

/**
Expand All @@ -29,7 +30,7 @@ export class GenericExtrinsicPayloadV4 extends Struct {
{ method: 'Bytes' },
registry.getSignedExtensionTypes(),
registry.getSignedExtensionExtra()
), value);
), decodeAssetId(registry, value));

// Do detection for the type of extrinsic, in the case of MultiSignature
// this is an enum, in the case of AnySignature, this is a Hash only
Expand Down

0 comments on commit c129dda

Please sign in to comment.