Skip to content

Commit 30cbfae

Browse files
committed
PLMC AH transfer test
1 parent 7129d98 commit 30cbfae

File tree

18 files changed

+235
-107
lines changed

18 files changed

+235
-107
lines changed

integration-tests/chopsticks/.papi/descriptors/.gitignore

-3
This file was deleted.

integration-tests/chopsticks/.papi/descriptors/package.json

-24
This file was deleted.
Binary file not shown.
Binary file not shown.
Binary file not shown.

integration-tests/chopsticks/.papi/polkadot-api.json

-21
This file was deleted.

integration-tests/chopsticks/src/managers/PolimecManager.ts

+12-5
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ export class PolimecManager extends BaseChainManager {
5050
case Asset.WETH:
5151
// Placeholder
5252
return AssetSourceRelation.Self;
53+
case Asset.PLMC:
54+
return AssetSourceRelation.Self;
5355
}
5456
}
5557

@@ -69,12 +71,17 @@ export class PolimecManager extends BaseChainManager {
6971
return 0n;
7072
}
7173

72-
async getLocalXcmFee() {
74+
async getXcmFee() {
7375
const api = this.getApi(Chains.Polimec);
7476
const events = await api.event.PolkadotXcm.FeesPaid.pull();
75-
if (!events.length) return 0n;
76-
const fees = events[0]?.payload?.fees;
77-
if (!fees?.length) return 0n;
78-
return (fees[0]?.fun?.value as bigint) || 0n;
77+
console.dir(events, { depth: null });
78+
79+
return events[0]?.payload.fees?.[0]?.fun?.value ?? 0n;
80+
}
81+
82+
async getTransactionFee() {
83+
const api = this.getApi(Chains.Polimec);
84+
const events = await api.event.TransactionPayment.TransactionFeePaid.pull();
85+
return (events[0]?.payload.actual_fee as bigint) || 0n;
7986
}
8087
}

integration-tests/chopsticks/src/managers/PolkadotHubManager.ts

+2
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ export class PolkadotHubManager extends BaseChainManager {
4646
case Asset.WETH:
4747
// This is not actually used, so we use Self as a placeholder
4848
return AssetSourceRelation.Self;
49+
case Asset.PLMC:
50+
return AssetSourceRelation.Sibling;
4951
}
5052
}
5153

integration-tests/chopsticks/src/setup.ts

+1
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ export class ChainSetup {
100100
'wasm-override': POLIMEC_WASM,
101101
'import-storage': polimec_storage,
102102
'build-block-mode': BuildBlockMode.Instant,
103+
'runtime-log-level': 5,
103104
});
104105
}
105106

integration-tests/chopsticks/src/tests/polimec.test.ts

+35-21
Original file line numberDiff line numberDiff line change
@@ -19,33 +19,47 @@ describe('Polimec -> Hub Transfer Tests', () => {
1919
});
2020
afterAll(async () => await chainSetup.cleanup());
2121

22-
test(
23-
'Send USDC to Hub',
24-
() =>
25-
transferTest.testTransfer({
26-
account: Accounts.BOB,
27-
assets: [[Asset.USDC, TRANSFER_AMOUNTS.TOKENS, AssetSourceRelation.Sibling]],
28-
}),
29-
{ timeout: 25000 },
30-
);
22+
// test(
23+
// 'Send USDC to Hub',
24+
// () =>
25+
// transferTest.testTransfer({
26+
// account: Accounts.BOB,
27+
// assets: [[Asset.USDC, TRANSFER_AMOUNTS.TOKENS, AssetSourceRelation.Sibling]],
28+
// }),
29+
// { timeout: 25000 },
30+
// );
31+
//
32+
// test(
33+
// 'Send USDT to Hub',
34+
// () =>
35+
// transferTest.testTransfer({
36+
// account: Accounts.BOB,
37+
// assets: [[Asset.USDT, TRANSFER_AMOUNTS.TOKENS, AssetSourceRelation.Sibling]],
38+
// }),
39+
// { timeout: 25000 },
40+
// );
3141

32-
test(
33-
'Send USDT to Hub',
34-
() =>
35-
transferTest.testTransfer({
36-
account: Accounts.BOB,
37-
assets: [[Asset.USDT, TRANSFER_AMOUNTS.TOKENS, AssetSourceRelation.Sibling]],
38-
}),
39-
{ timeout: 25000 },
40-
);
42+
// test(
43+
// 'Send DOT to Hub',
44+
// () =>
45+
// transferTest.testTransfer({
46+
// account: Accounts.BOB,
47+
// assets: [[Asset.DOT, TRANSFER_AMOUNTS.NATIVE, AssetSourceRelation.Parent]],
48+
// }),
49+
// { timeout: 25000 },
50+
// );
4151

4252
test(
43-
'Send DOT to Hub',
53+
'Send PLMC to Hub',
4454
() =>
4555
transferTest.testTransfer({
4656
account: Accounts.BOB,
47-
assets: [[Asset.DOT, TRANSFER_AMOUNTS.NATIVE, AssetSourceRelation.Parent]],
57+
assets: [
58+
[Asset.PLMC, TRANSFER_AMOUNTS.NATIVE, AssetSourceRelation.Self],
59+
[Asset.DOT, TRANSFER_AMOUNTS.NATIVE, AssetSourceRelation.Parent],
60+
],
61+
fee_asset_item: 1,
4862
}),
49-
{ timeout: 25000 },
63+
{ timeout: 25000000 },
5064
);
5165
});

integration-tests/chopsticks/src/transfers/BaseTransfer.ts

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { sleep } from 'bun';
1313
export interface TransferOptions {
1414
account: Accounts;
1515
assets: [Asset, bigint, AssetSourceRelation][];
16+
fee_asset_item?: number;
1617
}
1718

1819
export abstract class BaseTransferTest {
@@ -63,6 +64,7 @@ export abstract class BaseTransferTest {
6364
protected async verifyExecution() {
6465
const events = await this.destManager.getMessageQueueEvents();
6566

67+
console.dir(events, { depth: null });
6668
expect(events).not.toBeEmpty();
6769
expect(events).toBeArray();
6870
expect(events).toHaveLength(1);

integration-tests/chopsticks/src/transfers/PolimecToHub.ts

+141-19
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,22 @@ import { expect } from 'bun:test';
22
import { INITIAL_BALANCES } from '@/constants';
33
import type { PolimecManager } from '@/managers/PolimecManager';
44
import type { PolkadotHubManager } from '@/managers/PolkadotHubManager';
5-
import { Asset, type BalanceCheck, Chains, getVersionedAssets } from '@/types';
6-
import { createTransferData } from '@/utils';
5+
import {
6+
Asset,
7+
AssetSourceRelation,
8+
type BalanceCheck,
9+
Chains,
10+
ParaId,
11+
type PolimecBalanceCheck,
12+
getVersionedAssets,
13+
} from '@/types';
14+
import { createTransferData, unwrap } from '@/utils';
15+
import {
16+
DispatchRawOrigin,
17+
XcmVersionedAssetId,
18+
type XcmVersionedLocation,
19+
type XcmVersionedXcm,
20+
} from '@polkadot-api/descriptors';
721
import { BaseTransferTest, type TransferOptions } from './BaseTransfer';
822

923
export class PolimecToHubTransfer extends BaseTransferTest {
@@ -14,7 +28,7 @@ export class PolimecToHubTransfer extends BaseTransferTest {
1428
super(sourceManager, destManager);
1529
}
1630

17-
async executeTransfer({ account, assets }: TransferOptions) {
31+
async executeTransfer({ account, assets, fee_asset_item }: TransferOptions) {
1832
const [sourceBlock, destBlock] = await Promise.all([
1933
this.sourceManager.getBlockNumber(),
2034
this.destManager.getBlockNumber(),
@@ -25,12 +39,14 @@ export class PolimecToHubTransfer extends BaseTransferTest {
2539
toChain: Chains.PolkadotHub,
2640
assets: versioned_assets,
2741
recv: account,
42+
fee_asset_item: fee_asset_item ?? 0,
2843
});
2944

3045
const res = await this.sourceManager
3146
.getXcmPallet()
3247
.transfer_assets(data)
3348
.signAndSubmit(this.sourceManager.getSigner(account));
49+
console.dir(res, { depth: null });
3450

3551
expect(res.ok).toBeTrue();
3652
return { sourceBlock, destBlock };
@@ -48,23 +64,129 @@ export class PolimecToHubTransfer extends BaseTransferTest {
4864
return { asset_balances: [{ source, destination }] };
4965
}
5066

51-
verifyFinalBalances(
52-
initialBalances: BalanceCheck[],
53-
finalBalances: BalanceCheck[],
54-
options: TransferOptions,
67+
// verifyFinalBalances(
68+
// initialBalances: BalanceCheck[],
69+
// finalBalances: BalanceCheck[],
70+
// options: TransferOptions,
71+
// ) {
72+
// // TODO: At the moment we exclude fees from the balance check since the PAPI team is wotking on some utilies to calculate fees.
73+
// const initialBalance =
74+
// options.assets[0][0] === Asset.DOT
75+
// ? INITIAL_BALANCES.DOT
76+
// : options.assets[0][0] === Asset.USDT
77+
// ? INITIAL_BALANCES.USDT
78+
// : INITIAL_BALANCES.USDC;
79+
// for (let i = 0; i < options.assets.length; i++) {
80+
// expect(initialBalances[i].destination).toBe(0n);
81+
// expect(initialBalances[i].source).toBe(initialBalance);
82+
// expect(finalBalances[i].source).toBeLessThan(initialBalances[i].source);
83+
// expect(finalBalances[i].destination).toBeGreaterThan(initialBalances[i].destination);
84+
// }
85+
// }
86+
87+
async verifyFinalBalances(
88+
assetInitialBalances: PolimecBalanceCheck[],
89+
assetFinalBalances: PolimecBalanceCheck[],
90+
transferOptions: TransferOptions,
5591
) {
56-
// TODO: At the moment we exclude fees from the balance check since the PAPI team is wotking on some utilies to calculate fees.
57-
const initialBalance =
58-
options.assets[0][0] === Asset.DOT
59-
? INITIAL_BALANCES.DOT
60-
: options.assets[0][0] === Asset.USDT
61-
? INITIAL_BALANCES.USDT
62-
: INITIAL_BALANCES.USDC;
63-
for (let i = 0; i < options.assets.length; i++) {
64-
expect(initialBalances[i].destination).toBe(0n);
65-
expect(initialBalances[i].source).toBe(initialBalance);
66-
expect(finalBalances[i].source).toBeLessThan(initialBalances[i].source);
67-
expect(finalBalances[i].destination).toBeGreaterThan(initialBalances[i].destination);
92+
const native_extrinsic_fee_amount = await this.sourceManager.getTransactionFee();
93+
const source_xcm_asset_fee_amount = await this.sourceManager.getXcmFee();
94+
const dest_xcm_asset_fee_amount = await this.calculatePolkadotHubXcmFee(transferOptions);
95+
96+
const fee_asset = transferOptions.assets[0][0];
97+
98+
for (let i = 0; i < transferOptions.assets.length; i++) {
99+
const initialBalances = assetInitialBalances[i];
100+
const finalBalances = assetFinalBalances[i];
101+
const send_amount = transferOptions.assets[i][1];
102+
const asset = transferOptions.assets[i][0];
103+
104+
let expectedSourceBalanceSpent = send_amount;
105+
let expectedDestBalanceSpent = 0n;
106+
let expectedTreasuryBalanceGained = 0n;
107+
108+
if (asset === Asset.PLMC) {
109+
expectedSourceBalanceSpent += native_extrinsic_fee_amount + source_xcm_asset_fee_amount;
110+
}
111+
if (asset === fee_asset) {
112+
expectedDestBalanceSpent += dest_xcm_asset_fee_amount;
113+
expectedTreasuryBalanceGained += dest_xcm_asset_fee_amount;
114+
}
115+
116+
expect(finalBalances.source).toBe(initialBalances.source - expectedSourceBalanceSpent);
117+
expect(finalBalances.destination).toBe(
118+
initialBalances.destination + send_amount - expectedDestBalanceSpent,
119+
);
120+
expect(finalBalances.treasury).toBe(initialBalances.treasury + expectedTreasuryBalanceGained);
121+
}
122+
}
123+
124+
async calculatePolkadotHubXcmFee(transferOptions: TransferOptions): Promise<bigint> {
125+
let destinationExecutionFee: bigint;
126+
127+
const sourceApi = this.sourceManager.getApi(Chains.Polimec);
128+
const destApi = this.destManager.getApi(Chains.PolkadotHub);
129+
130+
const versioned_assets = getVersionedAssets(transferOptions.assets);
131+
const transferData = createTransferData({
132+
toChain: Chains.Polimec,
133+
assets: versioned_assets,
134+
recv: transferOptions.account,
135+
fee_asset_item: transferOptions.fee_asset_item ?? 0,
136+
});
137+
138+
let remoteFeeAssetId: XcmVersionedAssetId;
139+
const feeAsset = unwrap(transferOptions.assets.at(transferData.fee_asset_item));
140+
if (feeAsset[2] === AssetSourceRelation.Self) {
141+
feeAsset[2] = AssetSourceRelation.Sibling;
142+
}
143+
const versioned_asset = getVersionedAssets([feeAsset]);
144+
if (versioned_asset.type === 'V4') {
145+
remoteFeeAssetId = XcmVersionedAssetId.V4(unwrap(versioned_asset.value.at(0)).id);
146+
} else {
147+
throw new Error('Invalid versioned assets');
148+
}
149+
150+
const localDryRunResult = await sourceApi.apis.DryRunApi.dry_run_call(
151+
{ type: 'system', value: DispatchRawOrigin.Signed(transferOptions.account) },
152+
{ type: 'PolkadotXcm', value: { type: 'transfer_assets', value: transferData } },
153+
);
154+
155+
let forwardedXcms: [XcmVersionedLocation, XcmVersionedXcm[]][] = [];
156+
if (localDryRunResult.success && localDryRunResult.value.forwarded_xcms) {
157+
forwardedXcms = localDryRunResult.value.forwarded_xcms;
158+
} else {
159+
throw new Error('Dry run failed');
160+
}
161+
162+
const xcmsToPHub = forwardedXcms.find(
163+
([location, _]) =>
164+
location.type === 'V4' &&
165+
location.value.parents === 1 &&
166+
location.value.interior.type === 'X1' &&
167+
location.value.interior.value.type === 'Parachain' &&
168+
location.value.interior.value.value === ParaId[Chains.PolkadotHub],
169+
);
170+
if (!xcmsToPHub) {
171+
throw new Error('Could not find xcm to polimec');
172+
}
173+
const messages = xcmsToPHub[1];
174+
const remoteXcm = messages[0];
175+
const remoteXcmWeightResult = await destApi.apis.XcmPaymentApi.query_xcm_weight(remoteXcm);
176+
if (remoteXcmWeightResult.success) {
177+
const remoteExecutionFeesResult = await destApi.apis.XcmPaymentApi.query_weight_to_asset_fee(
178+
remoteXcmWeightResult.value,
179+
remoteFeeAssetId,
180+
);
181+
if (remoteExecutionFeesResult.success) {
182+
destinationExecutionFee = remoteExecutionFeesResult.value;
183+
} else {
184+
throw new Error('Could not calculate destination xcm fee');
185+
}
186+
} else {
187+
throw new Error('Could not calculate xcm weight');
68188
}
189+
190+
return destinationExecutionFee;
69191
}
70192
}

0 commit comments

Comments
 (0)