Skip to content

Commit 9ea36ad

Browse files
mmaurelloRihyx
andauthored
Fixes after integration (#384)
* adjustments after integrating XCM in the dapp * normalize extrinsic build for xcm versions * update test snapshot * add tests for normalize functions * remove unused import * change wording and remove log * Update packages/builder/src/extrinsic/ExtrinsicBuilder.utils.ts Co-authored-by: Richard Kenigs <[email protected]> * apply type inference and fix tyeps --------- Co-authored-by: Richard Kenigs <[email protected]>
1 parent 82e55a4 commit 9ea36ad

File tree

11 files changed

+211
-96
lines changed

11 files changed

+211
-96
lines changed
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { describe, expect, it } from 'vitest';
2+
import { XcmVersion } from './ExtrinsicBuilder.interfaces';
3+
import { normalizeConcrete, normalizeX1 } from './ExtrinsicBuilder.utils';
4+
5+
describe('normalizeX1', () => {
6+
it('should return original object when xcmVersion < v4', () => {
7+
const input = {
8+
interior: { X1: { Parachain: 1000 } },
9+
};
10+
11+
expect(normalizeX1(XcmVersion.v3, input)).toEqual(input);
12+
});
13+
14+
it('should wrap X1 value in array for xcmVersion >= v4', () => {
15+
const input = {
16+
interior: { X1: { Parachain: 1000 } },
17+
};
18+
const expected = {
19+
interior: { X1: [{ Parachain: 1000 }] },
20+
};
21+
22+
expect(normalizeX1(XcmVersion.v4, input)).toEqual(expected);
23+
});
24+
25+
it('should handle lowercase x1 key', () => {
26+
const input = {
27+
interior: { x1: { Parachain: 1000 } },
28+
};
29+
const expected = {
30+
interior: { x1: [{ Parachain: 1000 }] },
31+
};
32+
33+
expect(normalizeX1(XcmVersion.v4, input)).toEqual(expected);
34+
});
35+
36+
it('should not modify object without X1/x1 key', () => {
37+
const input = {
38+
interior: { X2: [{ Parachain: 1000 }, { GeneralIndex: 1 }] },
39+
};
40+
41+
expect(normalizeX1(XcmVersion.v4, input)).toEqual(input);
42+
});
43+
});
44+
45+
describe('normalizeConcrete', () => {
46+
it('should return original object for xcmVersion >= v4', () => {
47+
const input = { Parachain: 1000 };
48+
49+
expect(normalizeConcrete(XcmVersion.v4, input)).toBe(input);
50+
expect(normalizeConcrete(XcmVersion.v5, input)).toBe(input);
51+
});
52+
53+
it('should wrap object in Concrete for xcmVersion < v4', () => {
54+
const input = { Parachain: 1000 };
55+
const expected = { Concrete: { Parachain: 1000 } };
56+
57+
expect(normalizeConcrete(XcmVersion.v3, input)).toEqual(expected);
58+
expect(normalizeConcrete(XcmVersion.v2, input)).toEqual(expected);
59+
expect(normalizeConcrete(XcmVersion.v1, input)).toEqual(expected);
60+
});
61+
});

packages/builder/src/extrinsic/ExtrinsicBuilder.utils.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { SubmittableExtrinsicFunction } from '@polkadot/api/types';
22
import { getTypeDef } from '@polkadot/types';
3+
import type { AnyJson } from '@polkadot/types/types';
34
import { u8aToHex } from '@polkadot/util';
45
import { decodeAddress } from '@polkadot/util-crypto';
56
import { XcmVersion } from './ExtrinsicBuilder.interfaces';
@@ -61,3 +62,40 @@ export function getExtrinsicAccount(address: string) {
6162
},
6263
};
6364
}
65+
66+
export function isXcmV4(xcmVersion: XcmVersion): boolean {
67+
return xcmVersion >= XcmVersion.v4;
68+
}
69+
70+
export function normalizeX1(
71+
xcmVersion: XcmVersion,
72+
versionedObject: Record<string, AnyJson | object>,
73+
) {
74+
if (!isXcmV4(xcmVersion)) return versionedObject;
75+
76+
const normalizedObject = { ...versionedObject };
77+
const interior = normalizedObject.interior as object;
78+
79+
if ('X1' in interior && interior?.X1 && !Array.isArray(interior.X1)) {
80+
interior.X1 = [interior.X1];
81+
} else if ('x1' in interior && interior?.x1 && !Array.isArray(interior.x1)) {
82+
interior.x1 = [interior.x1];
83+
}
84+
85+
return normalizedObject;
86+
}
87+
88+
export function normalizeConcrete(
89+
xcmVersion: XcmVersion,
90+
versionedObject: object,
91+
) {
92+
return isXcmV4(xcmVersion)
93+
? versionedObject
94+
: applyConcreteWrapper(versionedObject);
95+
}
96+
97+
export function applyConcreteWrapper(versionedObject: object) {
98+
return {
99+
Concrete: { ...versionedObject },
100+
};
101+
}

packages/builder/src/extrinsic/pallets/polkadotXcm/polkadotXcm.ts

Lines changed: 49 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import { ExtrinsicConfig } from '../../../types/substrate/ExtrinsicConfig';
22
import type { ExtrinsicConfigBuilder } from '../../ExtrinsicBuilder.interfaces';
3+
import {
4+
getExtrinsicArgumentVersion,
5+
normalizeConcrete,
6+
} from '../../ExtrinsicBuilder.utils';
37
import {
48
getPolkadotXcmExtrinsicArgs,
59
shouldFeeAssetPrecedeAsset,
@@ -75,25 +79,24 @@ export function polkadotXcm() {
7579
func,
7680
getArgs: (extrinsicFunction) => {
7781
const isAssetDifferent = !params.asset.isSame(params.fee);
82+
const version = getExtrinsicArgumentVersion(extrinsicFunction);
7883

7984
const assets = [
8085
{
81-
id: {
82-
Concrete: {
83-
parents: 0,
84-
interior: {
85-
X2: [
86-
{
87-
PalletInstance:
88-
params.asset.getAssetPalletInstance(),
89-
},
90-
{
91-
GeneralIndex: params.asset.getAssetId(),
92-
},
93-
],
94-
},
86+
id: normalizeConcrete(version, {
87+
parents: 0,
88+
interior: {
89+
X2: [
90+
{
91+
PalletInstance:
92+
params.asset.getAssetPalletInstance(),
93+
},
94+
{
95+
GeneralIndex: params.asset.getAssetId(),
96+
},
97+
],
9598
},
96-
},
99+
}),
97100
fun: {
98101
Fungible: params.asset.amount,
99102
},
@@ -105,22 +108,19 @@ export function polkadotXcm() {
105108

106109
if (isAssetDifferent) {
107110
const feeAsset = {
108-
id: {
109-
Concrete: {
110-
parents: 0,
111-
interior: {
112-
X2: [
113-
{
114-
PalletInstance:
115-
params.fee.getAssetPalletInstance(),
116-
},
117-
{
118-
GeneralIndex: params.fee.getAssetId(),
119-
},
120-
],
121-
},
111+
id: normalizeConcrete(version, {
112+
parents: 0,
113+
interior: {
114+
X2: [
115+
{
116+
PalletInstance: params.fee.getAssetPalletInstance(),
117+
},
118+
{
119+
GeneralIndex: params.fee.getAssetId(),
120+
},
121+
],
122122
},
123-
},
123+
}),
124124
fun: {
125125
Fungible: params.fee.amount,
126126
},
@@ -153,34 +153,35 @@ export function polkadotXcm() {
153153
new ExtrinsicConfig({
154154
module: pallet,
155155
func,
156-
getArgs: (extrinsicFunction) =>
157-
getPolkadotXcmExtrinsicArgs({
156+
getArgs: (extrinsicFunction) => {
157+
const version = getExtrinsicArgumentVersion(extrinsicFunction);
158+
159+
return getPolkadotXcmExtrinsicArgs({
158160
...params,
159161
func: extrinsicFunction,
160162
asset: [
161163
{
162-
id: {
163-
Concrete: {
164-
parents: 1,
165-
interior: {
166-
X2: [
167-
{
168-
Parachain: params.destination.parachainId,
169-
},
170-
{
171-
PalletInstance:
172-
params.asset.getAssetPalletInstance(),
173-
},
174-
],
175-
},
164+
id: normalizeConcrete(version, {
165+
parents: 1,
166+
interior: {
167+
X2: [
168+
{
169+
Parachain: params.destination.parachainId,
170+
},
171+
{
172+
PalletInstance:
173+
params.asset.getAssetPalletInstance(),
174+
},
175+
],
176176
},
177-
},
177+
}),
178178
fun: {
179179
Fungible: params.asset.amount,
180180
},
181181
},
182182
],
183-
}),
183+
});
184+
},
184185
}),
185186
}),
186187
};

packages/builder/src/extrinsic/pallets/polkadotXcm/polkadotXcm.util.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { Parents } from '../../ExtrinsicBuilder.interfaces';
44
import {
55
getExtrinsicAccount,
66
getExtrinsicArgumentVersion,
7+
normalizeX1,
78
} from '../../ExtrinsicBuilder.utils';
89

910
export interface GetExtrinsicParams extends BuilderParams {
@@ -27,22 +28,22 @@ export function getPolkadotXcmExtrinsicArgs({
2728

2829
return [
2930
{
30-
[version]: {
31+
[version]: normalizeX1(version, {
3132
parents,
3233
interior: {
3334
X1: {
3435
Parachain: destination.parachainId,
3536
},
3637
},
37-
},
38+
}),
3839
},
3940
{
40-
[version]: {
41+
[version]: normalizeX1(version, {
4142
parents: 0,
4243
interior: {
4344
X1: getExtrinsicAccount(destinationAddress),
4445
},
45-
},
46+
}),
4647
},
4748
{
4849
[version]: asset,

packages/builder/src/fee/FeeBuilder.utils.ts

Lines changed: 11 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import type {
33
ChainAsset,
44
ChainAssetId,
55
} from '@moonbeam-network/xcm-types';
6-
import { isHexString } from '@moonbeam-network/xcm-utils';
76
import type { ApiPromise } from '@polkadot/api';
87
import type { Option, Result, u128 } from '@polkadot/types';
98
import type {
@@ -12,6 +11,10 @@ import type {
1211
} from '@polkadot/types/interfaces';
1312
import type { AnyJson } from '@polkadot/types/types';
1413
import { XcmVersion } from '../extrinsic';
14+
import {
15+
normalizeConcrete,
16+
normalizeX1,
17+
} from '../extrinsic/ExtrinsicBuilder.utils';
1518
import type { MoonbeamRuntimeXcmConfigAssetType } from './FeeBuilder.interfaces';
1619

1720
const DEFAULT_AMOUNT = 10 ** 6;
@@ -24,23 +27,6 @@ function isXcmV4() {
2427
return XCM_VERSION === XcmVersion.v4;
2528
}
2629

27-
function normalizeX1(assetType: Record<string, AnyJson>) {
28-
if (!isXcmV4()) {
29-
return assetType;
30-
}
31-
const normalizedAssetType = { ...assetType };
32-
if (
33-
normalizedAssetType.interior &&
34-
typeof normalizedAssetType.interior === 'object' &&
35-
'x1' in normalizedAssetType.interior
36-
) {
37-
if (!Array.isArray(normalizedAssetType.interior.x1)) {
38-
normalizedAssetType.interior.x1 = [normalizedAssetType.interior.x1];
39-
}
40-
}
41-
return normalizedAssetType;
42-
}
43-
4430
export function getWithdrawAssetInstruction(assetTypes: object[]) {
4531
return {
4632
WithdrawAsset: assetTypes.map((assetType) => ({
@@ -121,12 +107,6 @@ export function getSetTopicInstruction() {
121107
};
122108
}
123109

124-
function applyConcreteWrapper(id: object) {
125-
return {
126-
Concrete: { ...id },
127-
};
128-
}
129-
130110
// TODO this is for Moonbeam, when applying to all we have to
131111
// configure the multilocation of the native asset in the chain
132112
function getNativeAssetId(palletInstanceNumber: number | undefined): object {
@@ -145,8 +125,7 @@ function getNativeAssetId(palletInstanceNumber: number | undefined): object {
145125
},
146126
parents: '0',
147127
};
148-
149-
return isXcmV4() ? id : applyConcreteWrapper(id);
128+
return normalizeConcrete(XCM_VERSION, id);
150129
}
151130

152131
function getConcreteAssetIdWithAccountKey20(
@@ -175,7 +154,7 @@ function getConcreteAssetIdWithAccountKey20(
175154
},
176155
parents: '0',
177156
};
178-
return isXcmV4() ? id : applyConcreteWrapper(id);
157+
return normalizeConcrete(XCM_VERSION, id);
179158
}
180159

181160
export async function getAssetIdType(
@@ -201,22 +180,20 @@ export async function getVersionedAssetId(
201180
const assetId = asset.getAssetId();
202181
const palletInstance = asset.getAssetPalletInstance();
203182

204-
if (assetId === chain.nativeAsset.address) {
183+
if (assetId === chain.nativeAsset.originSymbol) {
205184
return getNativeAssetId(palletInstance);
206185
}
207186

208-
if (isHexString(assetId)) {
209-
return getConcreteAssetIdWithAccountKey20(assetId, palletInstance);
187+
if (asset.hasOnlyAddress()) {
188+
return getConcreteAssetIdWithAccountKey20(asset.address, palletInstance);
210189
}
211190

212191
const assetType = await getAssetIdType(api, assetId);
213192
const assetTypeObject = assetType.unwrap().asXcm.toJSON();
214193

215-
const normalizedAssetTypeObject = normalizeX1(assetTypeObject);
194+
const normalizedAssetTypeObject = normalizeX1(XCM_VERSION, assetTypeObject);
216195

217-
return isXcmV4()
218-
? normalizedAssetTypeObject
219-
: applyConcreteWrapper(normalizedAssetTypeObject);
196+
return normalizeConcrete(XCM_VERSION, normalizedAssetTypeObject);
220197
}
221198

222199
export async function getFeeForXcmInstructionsAndAsset(

0 commit comments

Comments
 (0)