Skip to content

Commit

Permalink
E2E tests: Cross chain trasfer between Rococo and RegionX (#113)
Browse files Browse the repository at this point in the history
* WIP: rococo to regionx

* tests failing

* remove sleep

* regionx to rococo

* fixes | working now

* add assertion checks

* add tolerance

---------

Co-authored-by: Sergej <[email protected]>
  • Loading branch information
cuteolaf and Szegoo authored May 8, 2024
1 parent 3f11943 commit 4bd4262
Show file tree
Hide file tree
Showing 6 changed files with 221 additions and 85 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

106 changes: 57 additions & 49 deletions e2e_tests/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,56 +2,64 @@ import { ApiPromise } from "@polkadot/api";
import { SubmittableExtrinsic, SignerOptions } from "@polkadot/api/types";
import { KeyringPair } from "@polkadot/keyring/types";

const RELAY_ASSET_ID = 1;

async function submitExtrinsic(signer: KeyringPair, call: SubmittableExtrinsic<"promise">, options: Partial<SignerOptions>): Promise<void> {
return new Promise(async (resolve, reject) => {
const unsub = await call.signAndSend(signer, options, (result) => {
console.log(`Current status is ${result.status}`);
if (result.status.isInBlock) {
console.log(
`Transaction included at blockHash ${result.status.asInBlock}`
);
} else if (result.status.isFinalized) {
console.log(
`Transaction finalized at blockHash ${result.status.asFinalized}`
);
unsub();
return resolve();
} else if (result.isError) {
console.log(`Transaction error`);
unsub();
return reject();
}
});
});
const RELAY_ASSET_ID = 1;

async function submitExtrinsic(
signer: KeyringPair,
call: SubmittableExtrinsic<"promise">,
options: Partial<SignerOptions>
): Promise<void> {
return new Promise(async (resolve, reject) => {
const unsub = await call.signAndSend(signer, options, (result) => {
console.log(`Current status is ${result.status}`);
if (result.status.isInBlock) {
console.log(`Transaction included at blockHash ${result.status.asInBlock}`);
} else if (result.status.isFinalized) {
console.log(`Transaction finalized at blockHash ${result.status.asFinalized}`);
unsub();
return resolve();
} else if (result.isError) {
console.log(`Transaction error`);
unsub();
return reject();
}
});
});
}

async function setupRelayAsset(
api: ApiPromise,
signer: KeyringPair,
initialBalance: bigint = 10n ** 12n
) {
const assetMetadata = {
decimals: 12,
name: "ROC",
symbol: "ROC",
existentialDeposit: 10n ** 3n,
location: null,
additional: null,
};

const assetSetupCalls = [
api.tx.assetRegistry.registerAsset(assetMetadata, RELAY_ASSET_ID),
api.tx.assetRate.create(RELAY_ASSET_ID, 1_000_000_000_000_000_000n), // 1 on 1
];

if (initialBalance > BigInt(0)) {
assetSetupCalls.push(
api.tx.tokens.setBalance(signer.address, RELAY_ASSET_ID, initialBalance, 0)
);
}

const batchCall = api.tx.utility.batch(assetSetupCalls);
const sudoCall = api.tx.sudo.sudo(batchCall);

await submitExtrinsic(signer, sudoCall, {});
}

async function setupRelayAsset(api: ApiPromise, signer: KeyringPair) {
const assetMetadata = {
decimals: 12,
name: "ROC",
symbol: "ROC",
existentialDeposit: 10n ** 3n,
location: null,
additional: null,
};

const assetSetupCalls = [
api.tx.assetRegistry.registerAsset(assetMetadata, RELAY_ASSET_ID),
api.tx.assetRate.create(RELAY_ASSET_ID, 1_000_000_000_000_000_000n), // 1 on 1
api.tx.tokens.setBalance(
signer.address,
RELAY_ASSET_ID,
10n ** 12n,
0,
),
];

const batchCall = api.tx.utility.batch(assetSetupCalls);
const sudoCall = api.tx.sudo.sudo(batchCall);

await submitExtrinsic(signer, sudoCall, {});
async function sleep(milliseconds: number) {
return new Promise((resolve) => setTimeout(resolve, milliseconds));
}

export { submitExtrinsic, setupRelayAsset, RELAY_ASSET_ID }
export { submitExtrinsic, setupRelayAsset, sleep, RELAY_ASSET_ID };
36 changes: 1 addition & 35 deletions e2e_tests/fee-payment/custom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,45 +26,11 @@ async function run(nodeName: string, networkInfo: any, _jsArgs: any) {
const setXcmVersion = rococoApi.tx.xcmPallet.forceDefaultXcmVersion([3]);
await submitExtrinsic(alice, rococoApi.tx.sudo.sudo(setXcmVersion), {});

await setupRelayAsset(regionXApi, alice);
await setupRelayAsset(regionXApi, alice, 10n**12n);

const receiverKeypair = new Keyring();
receiverKeypair.addFromAddress(alice.address);

const feeAssetItem = 0;
const weightLimit = "Unlimited";
const reserveTransfer = rococoApi.tx.xcmPallet.limitedReserveTransferAssets(
{ V3: { parents: 0, interior: { X1: { Parachain: 2000 } } } }, //dest
{
V3: {
parents: 0,
interior: {
X1: {
AccountId32: {
chain: "Any",
id: receiverKeypair.pairs[0].publicKey,
},
},
},
},
}, //beneficiary
{
V3: [
{
id: {
Concrete: { parents: 0, interior: "Here" },
},
fun: {
Fungible: 10n ** 9n,
},
},
],
}, //asset
feeAssetItem,
weightLimit
);
await submitExtrinsic(alice, reserveTransfer, {});

// Try to pay for fees with relay chain asset.
const remarkCall = regionXApi.tx.system.remark("0x44");
await submitExtrinsic(alice, remarkCall, { assetId: RELAY_ASSET_ID });
Expand Down
126 changes: 126 additions & 0 deletions e2e_tests/xc-transfer/asset-transfer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import { ApiPromise, WsProvider, Keyring } from "@polkadot/api";
import { RELAY_ASSET_ID, setupRelayAsset, sleep, submitExtrinsic } from "../common";

import assert from "node:assert";

const TOLERANCE = 10n**10n;

async function run(nodeName: string, networkInfo: any, _jsArgs: any) {
const { wsUri: regionXUri } = networkInfo.nodesByName[nodeName];
const { wsUri: rococoUri } = networkInfo.nodesByName["rococo-validator01"];

const rococoApi = await ApiPromise.create({ provider: new WsProvider(rococoUri) });
const regionXApi = await ApiPromise.create({ provider: new WsProvider(regionXUri) });

// account to submit tx
const keyring = new Keyring({ type: "sr25519" });
const alice = keyring.addFromUri("//Alice");

const setXcmVersion = rococoApi.tx.xcmPallet.forceDefaultXcmVersion([3]);
await submitExtrinsic(alice, rococoApi.tx.sudo.sudo(setXcmVersion), {});

await setupRelayAsset(regionXApi, alice);

const receiverKeypair = new Keyring();
receiverKeypair.addFromAddress(alice.address);

const assertRegionXBalance = async (address: string, balance: bigint) => {
const { free } = (
await regionXApi.query.tokens.accounts(address, RELAY_ASSET_ID)
).toHuman() as any;

console.log(`RegionX: ${free}`);
assert(balance - BigInt(free.toString().replace(/,/g, "")) < TOLERANCE);
};

const assertRococoBalance = async (address: string, balance: bigint) => {
const {
data: { free },
} = (await rococoApi.query.system.account(address)).toHuman() as any;

console.log(`Rococo: ${free}`);
assert(balance - BigInt(free.toString().replace(/,/g, "")) < TOLERANCE);
};

await assertRegionXBalance(alice.address, 10n ** 12n);
await assertRococoBalance(alice.address, 10n ** 18n);

const feeAssetItem = 0;
const weightLimit = "Unlimited";
const rococoReserveTransfer = rococoApi.tx.xcmPallet.limitedReserveTransferAssets(
{ V3: { parents: 0, interior: { X1: { Parachain: 2000 } } } }, //dest
{
V3: {
parents: 0,
interior: {
X1: {
AccountId32: {
chain: "Any",
id: receiverKeypair.pairs[0].publicKey,
},
},
},
},
}, //beneficiary
{
V3: [
{
id: {
Concrete: { parents: 0, interior: "Here" },
},
fun: {
Fungible: 3n * 10n ** 12n,
},
},
],
}, //asset
feeAssetItem,
weightLimit
);
await submitExtrinsic(alice, rococoReserveTransfer, {});

await sleep(5 * 1000);

await assertRegionXBalance(alice.address, 4n * 10n ** 12n);
await assertRococoBalance(alice.address, 10n ** 18n - 3n * 10n ** 12n);

const regionXReserveTransfer = regionXApi.tx.polkadotXcm.limitedReserveTransferAssets(
{ V3: { parents: 1, interior: "Here" } }, //dest
{
V3: {
parents: 0,
interior: {
X1: {
AccountId32: {
chain: "Any",
id: receiverKeypair.pairs[0].publicKey,
},
},
},
},
}, //beneficiary
{
V3: [
{
id: {
Concrete: { parents: 1, interior: "Here" },
},
fun: {
Fungible: 10n ** 12n,
},
},
],
}, //asset
feeAssetItem,
weightLimit
);

await submitExtrinsic(alice, regionXReserveTransfer, {});

await sleep(5 * 1000);

await assertRegionXBalance(alice.address, 4n * 10n ** 12n);
await assertRococoBalance(alice.address, 10n ** 18n - 3n * 10n ** 12n);
}

export { run };
25 changes: 25 additions & 0 deletions zombienet_tests/0006-xc-transfer.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[settings]
timeout = 1000

[relaychain]
chain = "rococo-local"
command = "polkadot"

[[relaychain.nodes]]
name = "rococo-validator01"
args = [ "--log=xcm=trace" ]
validator = true

[[relaychain.nodes]]
name = "rococo-validator02"
args = [ "--log=xcm=trace" ]
validator = true

[[parachains]]
id = 2000
addToGenesis = false

[parachains.collator]
name = "regionx-collator01"
command = "regionx-node"
args = [ "--log=xcm=trace" ]
11 changes: 11 additions & 0 deletions zombienet_tests/0006-xc-transfer.zndsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Description: Cross chain transfer between RegionX and the relay chain
Network: ./0006-xc-transfer.toml
Creds: config

rococo-validator01: is up
rococo-validator02: is up

rococo-validator01: parachain 2000 is registered within 225 seconds

regionx-collator01: js-script ../e2e_tests/build/xc-transfer/asset-transfer.js return is 0 within 400 seconds

0 comments on commit 4bd4262

Please sign in to comment.