From b1b31033c730283f43144b128268ab00421becd0 Mon Sep 17 00:00:00 2001 From: Cameron Marshall Date: Tue, 14 Jan 2025 00:21:58 -0500 Subject: [PATCH] test: update e2e tests (#498) --- .../bifrostPolkadot.ethereum.spec.ts.snap | 639 ++++++++++++++++++ .../moonbeam.ethereum.spec.ts.snap | 639 ++++++++++++++++++ .../polkadotAssetHub.hydration.spec.ts.snap | 6 +- e2e-tests/bifrostPolkadot.ethereum.spec.ts | 228 +++++++ e2e-tests/hydration.ethereum.spec.ts | 6 +- e2e-tests/moonbeam.ethereum.spec.ts | 228 +++++++ e2e-tests/moonbeam.hydration.spec.ts | 8 +- e2e-tests/polkadotAssetHub.bridgeHub.spec.ts | 5 +- e2e-tests/polkadotAssetHub.hydration.spec.ts | 20 +- src/consts.ts | 4 + .../transferAssetsUsingTypeAndThen.spec.ts | 84 +++ src/errors/checkXcmTxInputs.spec.ts | 10 +- src/errors/checkXcmTxInputs.ts | 23 +- 13 files changed, 1859 insertions(+), 41 deletions(-) create mode 100644 e2e-tests/__snapshots__/bifrostPolkadot.ethereum.spec.ts.snap create mode 100644 e2e-tests/__snapshots__/moonbeam.ethereum.spec.ts.snap create mode 100644 e2e-tests/bifrostPolkadot.ethereum.spec.ts create mode 100644 e2e-tests/moonbeam.ethereum.spec.ts diff --git a/e2e-tests/__snapshots__/bifrostPolkadot.ethereum.spec.ts.snap b/e2e-tests/__snapshots__/bifrostPolkadot.ethereum.spec.ts.snap new file mode 100644 index 00000000..a5781eb8 --- /dev/null +++ b/e2e-tests/__snapshots__/bifrostPolkadot.ethereum.spec.ts.snap @@ -0,0 +1,639 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Bifrost Polkadot <> Ethereum > XCM V3 > Transfer Snowbridge WETH from Bifrost Polkadot to Ethereum > assetHub xcmp message sent 1`] = ` +[ + { + "data": { + "messageHash": "(hash)", + }, + "method": "XcmpMessageSent", + "section": "xcmpQueue", + }, +] +`; + +exports[`Bifrost Polkadot <> Ethereum > XCM V3 > Transfer Snowbridge WETH from Bifrost Polkadot to Ethereum > assethub foreign assets burned 1`] = ` +[ + { + "data": { + "assetId": { + "interior": { + "X2": [ + { + "GlobalConsensus": { + "Ethereum": { + "chainId": 1, + }, + }, + }, + { + "AccountKey20": { + "key": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "network": null, + }, + }, + ], + }, + "parents": 2, + }, + "balance": 750000000000000, + "owner": "13cKp89TtYknbyYnqnF6dWN75q5ZosvFSuqzoEVkUAaNR47A", + }, + "method": "Burned", + "section": "foreignAssets", + }, +] +`; + +exports[`Bifrost Polkadot <> Ethereum > XCM V3 > Transfer Snowbridge WETH from Bifrost Polkadot to Ethereum > bifrost polkadot xcm message sent 1`] = ` +[ + { + "data": { + "outcome": { + "Complete": { + "used": { + "proofSize": 0, + "refTime": 100000000, + }, + }, + }, + }, + "method": "Attempted", + "section": "polkadotXcm", + }, + { + "data": { + "fees": [], + "paying": { + "interior": { + "X1": [ + { + "AccountId32": { + "id": "(hash)", + "network": "Polkadot", + }, + }, + ], + }, + "parents": 0, + }, + }, + "method": "FeesPaid", + "section": "polkadotXcm", + }, + { + "data": { + "destination": { + "interior": { + "X1": [ + { + "Parachain": 1000, + }, + ], + }, + "parents": 1, + }, + "message": [ + { + "WithdrawAsset": [ + { + "fun": { + "Fungible": 100000000000, + }, + "id": { + "interior": "Here", + "parents": 1, + }, + }, + { + "fun": { + "Fungible": 750000000000000, + }, + "id": { + "interior": { + "X2": [ + { + "GlobalConsensus": { + "Ethereum": { + "chainId": 1, + }, + }, + }, + { + "AccountKey20": { + "key": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "network": null, + }, + }, + ], + }, + "parents": 2, + }, + }, + ], + }, + "ClearOrigin", + { + "BuyExecution": { + "fees": { + "fun": { + "Fungible": 100000000000, + }, + "id": { + "interior": "Here", + "parents": 1, + }, + }, + "weightLimit": "Unlimited", + }, + }, + { + "SetAppendix": [ + { + "DepositAsset": { + "assets": { + "Wild": "All", + }, + "beneficiary": { + "interior": { + "X1": [ + { + "AccountId32": { + "id": "(hash)", + "network": null, + }, + }, + ], + }, + "parents": 0, + }, + }, + }, + ], + }, + { + "InitiateReserveWithdraw": { + "assets": { + "Wild": { + "AllOf": { + "fun": "Fungible", + "id": { + "interior": { + "X2": [ + { + "GlobalConsensus": { + "Ethereum": { + "chainId": 1, + }, + }, + }, + { + "AccountKey20": { + "key": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "network": null, + }, + }, + ], + }, + "parents": 2, + }, + }, + }, + }, + "reserve": { + "interior": { + "X1": [ + { + "GlobalConsensus": { + "Ethereum": { + "chainId": 1, + }, + }, + }, + ], + }, + "parents": 2, + }, + "xcm": [ + { + "BuyExecution": { + "fees": { + "fun": { + "Fungible": 1, + }, + "id": { + "interior": { + "X1": [ + { + "AccountKey20": { + "key": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "network": null, + }, + }, + ], + }, + "parents": 0, + }, + }, + "weightLimit": "Unlimited", + }, + }, + { + "DepositAsset": { + "assets": { + "Wild": { + "AllCounted": 1, + }, + }, + "beneficiary": { + "interior": { + "X1": [ + { + "AccountKey20": { + "key": "0xf24ff3a9cf04c71dbc94d0b566f7a27b94566cac", + "network": null, + }, + }, + ], + }, + "parents": 0, + }, + }, + }, + { + "SetTopic": 0, + }, + ], + }, + }, + { + "SetTopic": 0, + }, + ], + "messageId": "(redacted)", + "origin": { + "interior": { + "X1": [ + { + "AccountId32": { + "id": "(hash)", + "network": "Polkadot", + }, + }, + ], + }, + "parents": 0, + }, + }, + "method": "Sent", + "section": "polkadotXcm", + }, +] +`; + +exports[`Bifrost Polkadot <> Ethereum > XCM V3 > Transfer Snowbridge WETH from Bifrost Polkadot to Ethereum > bridgehub ethereum outbound queue events 1`] = ` +[ + { + "data": { + "id": 0, + }, + "method": "MessageQueued", + "section": "ethereumOutboundQueue", + }, + { + "data": { + "id": 0, + "nonce": "(redacted)", + }, + "method": "MessageAccepted", + "section": "ethereumOutboundQueue", + }, + { + "data": { + "count": 1, + "root": "(hash)", + }, + "method": "MessagesCommitted", + "section": "ethereumOutboundQueue", + }, +] +`; + +exports[`Bifrost Polkadot <> Ethereum > XCM V4 > Transfer Snowbridge WETH from Bifrost Polkadot to Ethereum > assetHub xcmp message sent 1`] = ` +[ + { + "data": { + "messageHash": "(hash)", + }, + "method": "XcmpMessageSent", + "section": "xcmpQueue", + }, +] +`; + +exports[`Bifrost Polkadot <> Ethereum > XCM V4 > Transfer Snowbridge WETH from Bifrost Polkadot to Ethereum > assethub foreign assets burned 1`] = ` +[ + { + "data": { + "assetId": { + "interior": { + "X2": [ + { + "GlobalConsensus": { + "Ethereum": { + "chainId": 1, + }, + }, + }, + { + "AccountKey20": { + "key": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "network": null, + }, + }, + ], + }, + "parents": 2, + }, + "balance": 750000000000000, + "owner": "13cKp89TtYknbyYnqnF6dWN75q5ZosvFSuqzoEVkUAaNR47A", + }, + "method": "Burned", + "section": "foreignAssets", + }, +] +`; + +exports[`Bifrost Polkadot <> Ethereum > XCM V4 > Transfer Snowbridge WETH from Bifrost Polkadot to Ethereum > bifrost polkadot xcm message sent 1`] = ` +[ + { + "data": { + "outcome": { + "Complete": { + "used": { + "proofSize": 0, + "refTime": 100000000, + }, + }, + }, + }, + "method": "Attempted", + "section": "polkadotXcm", + }, + { + "data": { + "fees": [], + "paying": { + "interior": { + "X1": [ + { + "AccountId32": { + "id": "(hash)", + "network": "Polkadot", + }, + }, + ], + }, + "parents": 0, + }, + }, + "method": "FeesPaid", + "section": "polkadotXcm", + }, + { + "data": { + "destination": { + "interior": { + "X1": [ + { + "Parachain": 1000, + }, + ], + }, + "parents": 1, + }, + "message": [ + { + "WithdrawAsset": [ + { + "fun": { + "Fungible": 100000000000, + }, + "id": { + "interior": "Here", + "parents": 1, + }, + }, + { + "fun": { + "Fungible": 750000000000000, + }, + "id": { + "interior": { + "X2": [ + { + "GlobalConsensus": { + "Ethereum": { + "chainId": 1, + }, + }, + }, + { + "AccountKey20": { + "key": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "network": null, + }, + }, + ], + }, + "parents": 2, + }, + }, + ], + }, + "ClearOrigin", + { + "BuyExecution": { + "fees": { + "fun": { + "Fungible": 100000000000, + }, + "id": { + "interior": "Here", + "parents": 1, + }, + }, + "weightLimit": "Unlimited", + }, + }, + { + "SetAppendix": [ + { + "DepositAsset": { + "assets": { + "Wild": "All", + }, + "beneficiary": { + "interior": { + "X1": [ + { + "AccountId32": { + "id": "(hash)", + "network": null, + }, + }, + ], + }, + "parents": 0, + }, + }, + }, + ], + }, + { + "InitiateReserveWithdraw": { + "assets": { + "Wild": { + "AllOf": { + "fun": "Fungible", + "id": { + "interior": { + "X2": [ + { + "GlobalConsensus": { + "Ethereum": { + "chainId": 1, + }, + }, + }, + { + "AccountKey20": { + "key": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "network": null, + }, + }, + ], + }, + "parents": 2, + }, + }, + }, + }, + "reserve": { + "interior": { + "X1": [ + { + "GlobalConsensus": { + "Ethereum": { + "chainId": 1, + }, + }, + }, + ], + }, + "parents": 2, + }, + "xcm": [ + { + "BuyExecution": { + "fees": { + "fun": { + "Fungible": 1, + }, + "id": { + "interior": { + "X1": [ + { + "AccountKey20": { + "key": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "network": null, + }, + }, + ], + }, + "parents": 0, + }, + }, + "weightLimit": "Unlimited", + }, + }, + { + "DepositAsset": { + "assets": { + "Wild": { + "AllCounted": 1, + }, + }, + "beneficiary": { + "interior": { + "X1": [ + { + "AccountKey20": { + "key": "0xf24ff3a9cf04c71dbc94d0b566f7a27b94566cac", + "network": null, + }, + }, + ], + }, + "parents": 0, + }, + }, + }, + { + "SetTopic": 0, + }, + ], + }, + }, + { + "SetTopic": 0, + }, + ], + "messageId": "(redacted)", + "origin": { + "interior": { + "X1": [ + { + "AccountId32": { + "id": "(hash)", + "network": "Polkadot", + }, + }, + ], + }, + "parents": 0, + }, + }, + "method": "Sent", + "section": "polkadotXcm", + }, +] +`; + +exports[`Bifrost Polkadot <> Ethereum > XCM V4 > Transfer Snowbridge WETH from Bifrost Polkadot to Ethereum > bridgehub ethereum outbound queue events 1`] = ` +[ + { + "data": { + "id": 0, + }, + "method": "MessageQueued", + "section": "ethereumOutboundQueue", + }, + { + "data": { + "id": 0, + "nonce": "(redacted)", + }, + "method": "MessageAccepted", + "section": "ethereumOutboundQueue", + }, + { + "data": { + "count": 1, + "root": "(hash)", + }, + "method": "MessagesCommitted", + "section": "ethereumOutboundQueue", + }, +] +`; diff --git a/e2e-tests/__snapshots__/moonbeam.ethereum.spec.ts.snap b/e2e-tests/__snapshots__/moonbeam.ethereum.spec.ts.snap new file mode 100644 index 00000000..77fae652 --- /dev/null +++ b/e2e-tests/__snapshots__/moonbeam.ethereum.spec.ts.snap @@ -0,0 +1,639 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Moonbeam <> Ethereum > XCM V3 > Transfer Snowbridge WETH from Moonbeam to Ethereum > Moonbeam xcm message sent 1`] = ` +[ + { + "data": { + "outcome": { + "Complete": { + "used": { + "proofSize": "(rounded 19000)", + "refTime": "(rounded 7700000000)", + }, + }, + }, + }, + "method": "Attempted", + "section": "polkadotXcm", + }, + { + "data": { + "fees": [], + "paying": { + "interior": { + "X1": [ + { + "AccountKey20": { + "key": "0xf24ff3a9cf04c71dbc94d0b566f7a27b94566cac", + "network": "Polkadot", + }, + }, + ], + }, + "parents": 0, + }, + }, + "method": "FeesPaid", + "section": "polkadotXcm", + }, + { + "data": { + "destination": { + "interior": { + "X1": [ + { + "Parachain": 1000, + }, + ], + }, + "parents": 1, + }, + "message": [ + { + "WithdrawAsset": [ + { + "fun": { + "Fungible": 100000000000, + }, + "id": { + "interior": "Here", + "parents": 1, + }, + }, + { + "fun": { + "Fungible": 750000000000000, + }, + "id": { + "interior": { + "X2": [ + { + "GlobalConsensus": { + "Ethereum": { + "chainId": 1, + }, + }, + }, + { + "AccountKey20": { + "key": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "network": null, + }, + }, + ], + }, + "parents": 2, + }, + }, + ], + }, + "ClearOrigin", + { + "BuyExecution": { + "fees": { + "fun": { + "Fungible": 100000000000, + }, + "id": { + "interior": "Here", + "parents": 1, + }, + }, + "weightLimit": "Unlimited", + }, + }, + { + "SetAppendix": [ + { + "DepositAsset": { + "assets": { + "Wild": "All", + }, + "beneficiary": { + "interior": { + "X1": [ + { + "AccountId32": { + "id": "(hash)", + "network": null, + }, + }, + ], + }, + "parents": 0, + }, + }, + }, + ], + }, + { + "InitiateReserveWithdraw": { + "assets": { + "Wild": { + "AllOf": { + "fun": "Fungible", + "id": { + "interior": { + "X2": [ + { + "GlobalConsensus": { + "Ethereum": { + "chainId": 1, + }, + }, + }, + { + "AccountKey20": { + "key": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "network": null, + }, + }, + ], + }, + "parents": 2, + }, + }, + }, + }, + "reserve": { + "interior": { + "X1": [ + { + "GlobalConsensus": { + "Ethereum": { + "chainId": 1, + }, + }, + }, + ], + }, + "parents": 2, + }, + "xcm": [ + { + "BuyExecution": { + "fees": { + "fun": { + "Fungible": 1, + }, + "id": { + "interior": { + "X1": [ + { + "AccountKey20": { + "key": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "network": null, + }, + }, + ], + }, + "parents": 0, + }, + }, + "weightLimit": "Unlimited", + }, + }, + { + "DepositAsset": { + "assets": { + "Wild": { + "AllCounted": 1, + }, + }, + "beneficiary": { + "interior": { + "X1": [ + { + "AccountKey20": { + "key": "0xf24ff3a9cf04c71dbc94d0b566f7a27b94566cac", + "network": null, + }, + }, + ], + }, + "parents": 0, + }, + }, + }, + { + "SetTopic": 0, + }, + ], + }, + }, + { + "SetTopic": 0, + }, + ], + "messageId": "(redacted)", + "origin": { + "interior": { + "X1": [ + { + "AccountKey20": { + "key": "0xf24ff3a9cf04c71dbc94d0b566f7a27b94566cac", + "network": "Polkadot", + }, + }, + ], + }, + "parents": 0, + }, + }, + "method": "Sent", + "section": "polkadotXcm", + }, +] +`; + +exports[`Moonbeam <> Ethereum > XCM V3 > Transfer Snowbridge WETH from Moonbeam to Ethereum > assetHub xcmp message sent 1`] = ` +[ + { + "data": { + "messageHash": "(hash)", + }, + "method": "XcmpMessageSent", + "section": "xcmpQueue", + }, +] +`; + +exports[`Moonbeam <> Ethereum > XCM V3 > Transfer Snowbridge WETH from Moonbeam to Ethereum > assethub foreign assets burned 1`] = ` +[ + { + "data": { + "assetId": { + "interior": { + "X2": [ + { + "GlobalConsensus": { + "Ethereum": { + "chainId": 1, + }, + }, + }, + { + "AccountKey20": { + "key": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "network": null, + }, + }, + ], + }, + "parents": 2, + }, + "balance": 750000000000000, + "owner": "13cKp89NgPL56sRoVRpBcjkGZPrk4Vf4tS6ePUD96XhAXozG", + }, + "method": "Burned", + "section": "foreignAssets", + }, +] +`; + +exports[`Moonbeam <> Ethereum > XCM V3 > Transfer Snowbridge WETH from Moonbeam to Ethereum > bridgehub ethereum outbound queue events 1`] = ` +[ + { + "data": { + "id": 0, + }, + "method": "MessageQueued", + "section": "ethereumOutboundQueue", + }, + { + "data": { + "id": 0, + "nonce": "(redacted)", + }, + "method": "MessageAccepted", + "section": "ethereumOutboundQueue", + }, + { + "data": { + "count": 1, + "root": "(hash)", + }, + "method": "MessagesCommitted", + "section": "ethereumOutboundQueue", + }, +] +`; + +exports[`Moonbeam <> Ethereum > XCM V4 > Transfer Snowbridge WETH from Moonbeam to Ethereum > Moonbeam xcm message sent 1`] = ` +[ + { + "data": { + "outcome": { + "Complete": { + "used": { + "proofSize": "(rounded 19000)", + "refTime": "(rounded 7700000000)", + }, + }, + }, + }, + "method": "Attempted", + "section": "polkadotXcm", + }, + { + "data": { + "fees": [], + "paying": { + "interior": { + "X1": [ + { + "AccountKey20": { + "key": "0xf24ff3a9cf04c71dbc94d0b566f7a27b94566cac", + "network": "Polkadot", + }, + }, + ], + }, + "parents": 0, + }, + }, + "method": "FeesPaid", + "section": "polkadotXcm", + }, + { + "data": { + "destination": { + "interior": { + "X1": [ + { + "Parachain": 1000, + }, + ], + }, + "parents": 1, + }, + "message": [ + { + "WithdrawAsset": [ + { + "fun": { + "Fungible": 100000000000, + }, + "id": { + "interior": "Here", + "parents": 1, + }, + }, + { + "fun": { + "Fungible": 750000000000000, + }, + "id": { + "interior": { + "X2": [ + { + "GlobalConsensus": { + "Ethereum": { + "chainId": 1, + }, + }, + }, + { + "AccountKey20": { + "key": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "network": null, + }, + }, + ], + }, + "parents": 2, + }, + }, + ], + }, + "ClearOrigin", + { + "BuyExecution": { + "fees": { + "fun": { + "Fungible": 100000000000, + }, + "id": { + "interior": "Here", + "parents": 1, + }, + }, + "weightLimit": "Unlimited", + }, + }, + { + "SetAppendix": [ + { + "DepositAsset": { + "assets": { + "Wild": "All", + }, + "beneficiary": { + "interior": { + "X1": [ + { + "AccountId32": { + "id": "(hash)", + "network": null, + }, + }, + ], + }, + "parents": 0, + }, + }, + }, + ], + }, + { + "InitiateReserveWithdraw": { + "assets": { + "Wild": { + "AllOf": { + "fun": "Fungible", + "id": { + "interior": { + "X2": [ + { + "GlobalConsensus": { + "Ethereum": { + "chainId": 1, + }, + }, + }, + { + "AccountKey20": { + "key": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "network": null, + }, + }, + ], + }, + "parents": 2, + }, + }, + }, + }, + "reserve": { + "interior": { + "X1": [ + { + "GlobalConsensus": { + "Ethereum": { + "chainId": 1, + }, + }, + }, + ], + }, + "parents": 2, + }, + "xcm": [ + { + "BuyExecution": { + "fees": { + "fun": { + "Fungible": 1, + }, + "id": { + "interior": { + "X1": [ + { + "AccountKey20": { + "key": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "network": null, + }, + }, + ], + }, + "parents": 0, + }, + }, + "weightLimit": "Unlimited", + }, + }, + { + "DepositAsset": { + "assets": { + "Wild": { + "AllCounted": 1, + }, + }, + "beneficiary": { + "interior": { + "X1": [ + { + "AccountKey20": { + "key": "0xf24ff3a9cf04c71dbc94d0b566f7a27b94566cac", + "network": null, + }, + }, + ], + }, + "parents": 0, + }, + }, + }, + { + "SetTopic": 0, + }, + ], + }, + }, + { + "SetTopic": 0, + }, + ], + "messageId": "(redacted)", + "origin": { + "interior": { + "X1": [ + { + "AccountKey20": { + "key": "0xf24ff3a9cf04c71dbc94d0b566f7a27b94566cac", + "network": "Polkadot", + }, + }, + ], + }, + "parents": 0, + }, + }, + "method": "Sent", + "section": "polkadotXcm", + }, +] +`; + +exports[`Moonbeam <> Ethereum > XCM V4 > Transfer Snowbridge WETH from Moonbeam to Ethereum > assetHub xcmp message sent 1`] = ` +[ + { + "data": { + "messageHash": "(hash)", + }, + "method": "XcmpMessageSent", + "section": "xcmpQueue", + }, +] +`; + +exports[`Moonbeam <> Ethereum > XCM V4 > Transfer Snowbridge WETH from Moonbeam to Ethereum > assethub foreign assets burned 1`] = ` +[ + { + "data": { + "assetId": { + "interior": { + "X2": [ + { + "GlobalConsensus": { + "Ethereum": { + "chainId": 1, + }, + }, + }, + { + "AccountKey20": { + "key": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "network": null, + }, + }, + ], + }, + "parents": 2, + }, + "balance": 750000000000000, + "owner": "13cKp89NgPL56sRoVRpBcjkGZPrk4Vf4tS6ePUD96XhAXozG", + }, + "method": "Burned", + "section": "foreignAssets", + }, +] +`; + +exports[`Moonbeam <> Ethereum > XCM V4 > Transfer Snowbridge WETH from Moonbeam to Ethereum > bridgehub ethereum outbound queue events 1`] = ` +[ + { + "data": { + "id": 0, + }, + "method": "MessageQueued", + "section": "ethereumOutboundQueue", + }, + { + "data": { + "id": 0, + "nonce": "(redacted)", + }, + "method": "MessageAccepted", + "section": "ethereumOutboundQueue", + }, + { + "data": { + "count": 1, + "root": "(hash)", + }, + "method": "MessagesCommitted", + "section": "ethereumOutboundQueue", + }, +] +`; diff --git a/e2e-tests/__snapshots__/polkadotAssetHub.hydration.spec.ts.snap b/e2e-tests/__snapshots__/polkadotAssetHub.hydration.spec.ts.snap index 91f2946f..7ce11a85 100644 --- a/e2e-tests/__snapshots__/polkadotAssetHub.hydration.spec.ts.snap +++ b/e2e-tests/__snapshots__/polkadotAssetHub.hydration.spec.ts.snap @@ -124,7 +124,7 @@ exports[`Polkadot AssetHub <> Hydration > XCM V3 > Transfer SnowBridge WETH From exports[`Polkadot AssetHub <> Hydration > XCM V3 > Transfer SnowBridge WETH From AssetHub To Hydration > hydration recipients updated snowbridge weth balance 1`] = ` { - "free": "(rounded 25000000000000)", + "free": "(rounded 20000000000000)", "frozen": 0, "reserved": 0, } @@ -208,7 +208,7 @@ exports[`Polkadot AssetHub <> Hydration > XCM V4 > Transfer SnowBridge WETH From exports[`Polkadot AssetHub <> Hydration > XCM V4 > Transfer SnowBridge WETH From AssetHub To Hydration > hydration recipients updated snowbridge weth balance 1`] = ` { - "free": "(rounded 25000000000000)", + "free": "(rounded 20000000000000)", "frozen": 0, "reserved": 0, } @@ -227,7 +227,7 @@ exports[`Polkadot AssetHub <> Hydration > XCM V4 > Transfer SnowBridge WETH From exports[`Polkadot AssetHub <> Hydration > XCM V4 > Transfer SnowBridge WETH From Hydration To AssetHub > asset hub recipients updated snowbridge weth balance 1`] = ` { - "balance": "(rounded 17000000000000)", + "balance": "(rounded 20000000000000)", "extra": null, "reason": { "sufficient": null, diff --git a/e2e-tests/bifrostPolkadot.ethereum.spec.ts b/e2e-tests/bifrostPolkadot.ethereum.spec.ts new file mode 100644 index 00000000..535d8e95 --- /dev/null +++ b/e2e-tests/bifrostPolkadot.ethereum.spec.ts @@ -0,0 +1,228 @@ +import { setupNetworks, testingPairs, withExpect } from '@acala-network/chopsticks-testing'; +import { NetworkContext } from '@acala-network/chopsticks-utils'; +import { setTimeout } from 'timers/promises'; +import { afterEach, beforeEach, expect, test } from 'vitest'; + +import { AssetTransferApi } from '../src/AssetTransferApi'; +import { ETHEREUM_MAINNET_NETWORK_GLOBAL_CONSENSUS_LOCATION } from '../src/consts'; + +const { checkSystemEvents } = withExpect(expect); + +describe('Bifrost Polkadot <> Ethereum', () => { + let bifrostPolkadot: NetworkContext; + let polkadotAssetHub: NetworkContext; + let polkadotBridgeHub: NetworkContext; + + const { alice, alith } = testingPairs(); + + beforeEach(async () => { + const { bifrostPolkadot1, polkadotBridgeHub1, polkadotAssetHub1 } = await setupNetworks({ + bifrostPolkadot1: { + endpoint: 'wss://bifrost-polkadot-rpc.dwellir.com', + db: './db.sqlite', + port: 8013, + }, + polkadotBridgeHub1: { + endpoint: 'wss://bridge-hub-polkadot-rpc.dwellir.com', + db: './db.sqlite', + port: 8014, + }, + polkadotAssetHub1: { + endpoint: 'wss://asset-hub-polkadot-rpc.dwellir.com', + db: './db.sqlite', + port: 8015, + }, + }); + + bifrostPolkadot = bifrostPolkadot1; + polkadotBridgeHub = polkadotBridgeHub1; + polkadotAssetHub = polkadotAssetHub1; + }, 1000000); + + afterEach(async () => { + await bifrostPolkadot.teardown(); + await polkadotAssetHub.teardown(); + await polkadotBridgeHub.teardown(); + }, 1000000); + + describe('XCM V3', () => { + const xcmVersion = 3; + + test('Transfer Snowbridge WETH from Bifrost Polkadot to Ethereum', async () => { + await bifrostPolkadot.dev.setStorage({ + System: { + Account: [ + [[alice.address], { providers: 1, data: { free: 10 * 1e12 } }], // BNC + ], + }, + Tokens: { + Accounts: [ + [[alice.address, { Token2: 0 }], { free: '1000000000000000000000000000000000' }], // DOT + [[alice.address, { Token2: 13 }], { free: '1000000000000000000000000000000' }], // Snowbridge WETH + ], + }, + }); + + const assetTransferApi = new AssetTransferApi(bifrostPolkadot.api, 'bifrost_polkadot', xcmVersion, { + registryType: 'NPM', + injectedRegistry: { + polkadot: { + 2030: { + tokens: [], + assetsInfo: {}, + foreignAssetsInfo: {}, + poolPairsInfo: {}, + specName: 'bifrost_polkadot', + xcAssetsData: [ + { + paraID: 0, + symbol: 'WETH.snow', + decimals: 18, + xcmV1MultiLocation: + '{"v1":{"parents":2,"interior":{"x2":[{"globalConsensus":{"ethereum":{"chainId":1}}},{"accountKey20":{"network":null,"key":"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"}}]}}}', + asset: { Token2: '13' }, + assetHubReserveLocation: '{"parents":"1","interior":{"X1":{"Parachain":"1000"}}}', + }, + ], + }, + }, + }, + }); + + const tx = await assetTransferApi.createTransferTransaction( + ETHEREUM_MAINNET_NETWORK_GLOBAL_CONSENSUS_LOCATION, + alith.address, + ['DOT', 'WETH.snow'], + ['100000000000', '750000000000000'], + { + sendersAddr: alice.address, + format: 'payload', + xcmVersion, + paysWithFeeDest: 'DOT', + }, + ); + + const extrinsic = assetTransferApi.api.registry.createType('Extrinsic', { method: tx.tx.method }, { version: 4 }); + + await bifrostPolkadot.api.tx(extrinsic).signAndSend(alice); + await bifrostPolkadot.dev.newBlock(); + + await checkSystemEvents(bifrostPolkadot, 'polkadotXcm') + .redact({ redactKeys: new RegExp('messageId') }) + .toMatchSnapshot('bifrost polkadot xcm message sent'); + + await setTimeout(10000); + await polkadotAssetHub.dev.timeTravel(1); + + await checkSystemEvents(polkadotAssetHub, 'foreignAssets').toMatchSnapshot('assethub foreign assets burned'); + await checkSystemEvents(polkadotAssetHub, 'xcmpQueue', 'XcmpMessageSent').toMatchSnapshot( + 'assetHub xcmp message sent', + ); + + await setTimeout(10000); + await polkadotBridgeHub.dev.timeTravel(1); + + await checkSystemEvents(polkadotBridgeHub, 'ethereumOutboundQueue') + .redact({ redactKeys: new RegExp('nonce') }) + .toMatchSnapshot('bridgehub ethereum outbound queue events'); + }, 200000); + }); + describe('XCM V4', () => { + const xcmVersion = 4; + + test('Transfer Snowbridge WETH from Bifrost Polkadot to Ethereum', async () => { + await bifrostPolkadot.dev.setStorage({ + System: { + Account: [ + [[alice.address], { providers: 1, data: { free: 10 * 1e12 } }], // BNC + ], + }, + Tokens: { + Accounts: [ + [[alice.address, { Token2: 0 }], { free: '1000000000000000000000000000000000' }], // DOT + [[alice.address, { Token2: 13 }], { free: '1000000000000000000000000000000' }], // Snowbridge WETH + ], + }, + }); + + const assetTransferApi = new AssetTransferApi(bifrostPolkadot.api, 'bifrost_polkadot', xcmVersion, { + registryType: 'NPM', + injectedRegistry: { + polkadot: { + 2030: { + tokens: [], + assetsInfo: {}, + foreignAssetsInfo: {}, + poolPairsInfo: {}, + specName: 'bifrost_polkadot', + xcAssetsData: [ + { + paraID: 0, + symbol: 'WETH.snow', + decimals: 18, + xcmV1MultiLocation: + '{"v1":{"parents":2,"interior":{"x2":[{"globalConsensus":{"ethereum":{"chainId":1}}},{"accountKey20":{"network":null,"key":"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"}}]}}}', + asset: { Token2: '13' }, + assetHubReserveLocation: '{"parents":"1","interior":{"X1":{"Parachain":"1000"}}}', + }, + ], + }, + }, + }, + }); + + const tx = await assetTransferApi.createTransferTransaction( + ETHEREUM_MAINNET_NETWORK_GLOBAL_CONSENSUS_LOCATION, + alith.address, + ['DOT', 'WETH.snow'], + ['100000000000', '750000000000000'], + { + sendersAddr: alice.address, + format: 'payload', + xcmVersion, + paysWithFeeDest: 'DOT', + }, + ); + + const extrinsic = assetTransferApi.api.registry.createType('Extrinsic', { method: tx.tx.method }, { version: 4 }); + + await bifrostPolkadot.api.tx(extrinsic).signAndSend(alice); + await bifrostPolkadot.dev.newBlock(); + + await checkSystemEvents(bifrostPolkadot, 'polkadotXcm') + .redact({ redactKeys: new RegExp('messageId') }) + .toMatchSnapshot('bifrost polkadot xcm message sent'); + + await setTimeout(10000); + await polkadotAssetHub.dev.timeTravel(1); + + await checkSystemEvents(polkadotAssetHub, 'foreignAssets').toMatchSnapshot('assethub foreign assets burned'); + await checkSystemEvents(polkadotAssetHub, 'xcmpQueue', 'XcmpMessageSent').toMatchSnapshot( + 'assetHub xcmp message sent', + ); + + const assetHubEvents = await polkadotAssetHub.api.query.system.events(); + + const xcmMessageProcessed = assetHubEvents[assetHubEvents.length - 1]; + expect(xcmMessageProcessed.phase.toString()).toEqual('Finalization'); + expect(xcmMessageProcessed.event.method).toEqual('Processed'); + expect(xcmMessageProcessed.event.section).toEqual('messageQueue'); + + await setTimeout(10000); + await polkadotBridgeHub.dev.timeTravel(1); + + await checkSystemEvents(polkadotBridgeHub, 'ethereumOutboundQueue') + .redact({ redactKeys: new RegExp('nonce') }) + .toMatchSnapshot('bridgehub ethereum outbound queue events'); + + const bridgeHubEvents = await polkadotBridgeHub.api.query.system.events(); + const messageAcceptedEvent = bridgeHubEvents[bridgeHubEvents.length - 3]; + expect(messageAcceptedEvent.event.section).toEqual('ethereumOutboundQueue'); + expect(messageAcceptedEvent.event.method).toEqual('MessageAccepted'); + + const messageCommittedEvent = bridgeHubEvents[bridgeHubEvents.length - 1]; + expect(messageCommittedEvent.event.section).toEqual('ethereumOutboundQueue'); + expect(messageCommittedEvent.event.method).toEqual('MessagesCommitted'); + }, 200000); + }); +}); diff --git a/e2e-tests/hydration.ethereum.spec.ts b/e2e-tests/hydration.ethereum.spec.ts index aa8d0a22..4404a5e2 100644 --- a/e2e-tests/hydration.ethereum.spec.ts +++ b/e2e-tests/hydration.ethereum.spec.ts @@ -4,11 +4,11 @@ import { setTimeout } from 'timers/promises'; import { afterEach, beforeEach, expect, test } from 'vitest'; import { AssetTransferApi } from '../src/AssetTransferApi'; +import { ETHEREUM_MAINNET_NETWORK_GLOBAL_CONSENSUS_LOCATION } from '../src/consts'; const { checkSystemEvents } = withExpect(expect); describe('Hydration <> Ethereum', () => { - const ethereumNetworkGlobalConsensusLocation = `{"parents":"2","interior":{"X1":{"GlobalConsensus":{"Ethereum":{"chainId":"1"}}}}}`; let hydration: NetworkContext; let polkadotAssetHub: NetworkContext; let polkadotBridgeHub: NetworkContext; @@ -91,7 +91,7 @@ describe('Hydration <> Ethereum', () => { }); const tx = await assetTransferApi.createTransferTransaction( - ethereumNetworkGlobalConsensusLocation, + ETHEREUM_MAINNET_NETWORK_GLOBAL_CONSENSUS_LOCATION, alith.address, ['DOT', 'WETH.snow'], ['500000000000', '75000000000000'], @@ -174,7 +174,7 @@ describe('Hydration <> Ethereum', () => { }); const tx = await assetTransferApi.createTransferTransaction( - ethereumNetworkGlobalConsensusLocation, + ETHEREUM_MAINNET_NETWORK_GLOBAL_CONSENSUS_LOCATION, alith.address, ['DOT', 'WETH.snow'], ['500000000000', '75000000000000'], diff --git a/e2e-tests/moonbeam.ethereum.spec.ts b/e2e-tests/moonbeam.ethereum.spec.ts new file mode 100644 index 00000000..9ea9ab47 --- /dev/null +++ b/e2e-tests/moonbeam.ethereum.spec.ts @@ -0,0 +1,228 @@ +import { setupNetworks, testingPairs, withExpect } from '@acala-network/chopsticks-testing'; +import { NetworkContext } from '@acala-network/chopsticks-utils'; +import { setTimeout } from 'timers/promises'; +import { afterEach, beforeEach, expect, test } from 'vitest'; + +import { AssetTransferApi } from '../src/AssetTransferApi'; +import { ETHEREUM_MAINNET_NETWORK_GLOBAL_CONSENSUS_LOCATION } from '../src/consts'; + +const { checkSystemEvents } = withExpect(expect); + +describe('Moonbeam <> Ethereum', () => { + let moonbeam: NetworkContext; + let polkadotAssetHub: NetworkContext; + let polkadotBridgeHub: NetworkContext; + + const { alice, alith } = testingPairs(); + + beforeEach(async () => { + const { moonbeam1, polkadotBridgeHub1, polkadotAssetHub1 } = await setupNetworks({ + moonbeam1: { + endpoint: 'wss://moonbeam-rpc.dwellir.com', + db: './db.sqlite', + port: 8016, + }, + polkadotBridgeHub1: { + endpoint: 'wss://bridge-hub-polkadot-rpc.dwellir.com', + db: './db.sqlite', + port: 8017, + }, + polkadotAssetHub1: { + endpoint: 'wss://asset-hub-polkadot-rpc.dwellir.com', + db: './db.sqlite', + port: 8018, + }, + }); + + moonbeam = moonbeam1; + polkadotBridgeHub = polkadotBridgeHub1; + polkadotAssetHub = polkadotAssetHub1; + }, 1000000); + + afterEach(async () => { + await moonbeam.teardown(); + await polkadotAssetHub.teardown(); + await polkadotBridgeHub.teardown(); + }, 1000000); + + describe('XCM V3', () => { + const xcmVersion = 3; + + test('Transfer Snowbridge WETH from Moonbeam to Ethereum', async () => { + await moonbeam.dev.setStorage({ + System: { + Account: [ + [[alith.address], { providers: 1, data: { free: '100000000000000000000000' } }], // GLMR + ], + }, + Assets: { + Account: [ + [['42259045809535163221576417993425387648', alith.address], { balance: '1000000000000000' }], // DOT + [['178794693648360392906933130845919698647', alith.address], { balance: '1000000000000000' }], // Snowbridge WETH + ], + }, + }); + + const assetTransferApi = new AssetTransferApi(moonbeam.api, 'moonbeam', xcmVersion, { + registryType: 'NPM', + injectedRegistry: { + polkadot: { + 2004: { + tokens: [], + assetsInfo: {}, + foreignAssetsInfo: {}, + poolPairsInfo: {}, + specName: 'moonbeam', + xcAssetsData: [ + { + paraID: 0, + symbol: 'WETH.snow', + decimals: 18, + xcmV1MultiLocation: + '{"v1":{"parents":2,"interior":{"x2":[{"globalConsensus":{"ethereum":{"chainId":1}}},{"accountKey20":{"network":null,"key":"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"}}]}}}', + asset: '178794693648360392906933130845919698647', + assetHubReserveLocation: '{"parents":"1","interior":{"X1":{"Parachain":"1000"}}}', + }, + ], + }, + }, + }, + }); + + const tx = await assetTransferApi.createTransferTransaction( + ETHEREUM_MAINNET_NETWORK_GLOBAL_CONSENSUS_LOCATION, + alith.address, + ['DOT', 'WETH.snow'], + ['100000000000', '750000000000000'], + { + sendersAddr: alice.address, + format: 'payload', + xcmVersion, + paysWithFeeDest: 'DOT', + }, + ); + + const extrinsic = assetTransferApi.api.registry.createType('Extrinsic', { method: tx.tx.method }, { version: 4 }); + + await moonbeam.api.tx(extrinsic).signAndSend(alith); + await moonbeam.dev.newBlock(); + + await checkSystemEvents(moonbeam, 'polkadotXcm') + .redact({ redactKeys: new RegExp('messageId') }) + .toMatchSnapshot('Moonbeam xcm message sent'); + + await setTimeout(10000); + await polkadotAssetHub.dev.timeTravel(1); + + await checkSystemEvents(polkadotAssetHub, 'foreignAssets').toMatchSnapshot('assethub foreign assets burned'); + await checkSystemEvents(polkadotAssetHub, 'xcmpQueue', 'XcmpMessageSent').toMatchSnapshot( + 'assetHub xcmp message sent', + ); + + await setTimeout(10000); + await polkadotBridgeHub.dev.timeTravel(1); + + await checkSystemEvents(polkadotBridgeHub, 'ethereumOutboundQueue') + .redact({ redactKeys: new RegExp('nonce') }) + .toMatchSnapshot('bridgehub ethereum outbound queue events'); + }, 200000); + }); + describe('XCM V4', () => { + const xcmVersion = 4; + + test('Transfer Snowbridge WETH from Moonbeam to Ethereum', async () => { + await moonbeam.dev.setStorage({ + System: { + Account: [ + [[alith.address], { providers: 1, data: { free: '100000000000000000000000' } }], // GLMR + ], + }, + Assets: { + Account: [ + [['42259045809535163221576417993425387648', alith.address], { balance: '1000000000000000' }], // DOT + [['178794693648360392906933130845919698647', alith.address], { balance: '1000000000000000' }], // Snowbridge WETH + ], + }, + }); + + const assetTransferApi = new AssetTransferApi(moonbeam.api, 'moonbeam', xcmVersion, { + registryType: 'NPM', + injectedRegistry: { + polkadot: { + 2030: { + tokens: [], + assetsInfo: {}, + foreignAssetsInfo: {}, + poolPairsInfo: {}, + specName: 'moonbeam', + xcAssetsData: [ + { + paraID: 0, + symbol: 'WETH.snow', + decimals: 18, + xcmV1MultiLocation: + '{"v1":{"parents":2,"interior":{"x2":[{"globalConsensus":{"ethereum":{"chainId":1}}},{"accountKey20":{"network":null,"key":"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"}}]}}}', + asset: { Token2: '13' }, + assetHubReserveLocation: '{"parents":"1","interior":{"X1":{"Parachain":"1000"}}}', + }, + ], + }, + }, + }, + }); + + const tx = await assetTransferApi.createTransferTransaction( + ETHEREUM_MAINNET_NETWORK_GLOBAL_CONSENSUS_LOCATION, + alith.address, + ['DOT', 'WETH.snow'], + ['100000000000', '750000000000000'], + { + sendersAddr: alice.address, + format: 'payload', + xcmVersion, + paysWithFeeDest: 'DOT', + }, + ); + + const extrinsic = assetTransferApi.api.registry.createType('Extrinsic', { method: tx.tx.method }, { version: 4 }); + + await moonbeam.api.tx(extrinsic).signAndSend(alith); + await moonbeam.dev.newBlock(); + + await checkSystemEvents(moonbeam, 'polkadotXcm') + .redact({ redactKeys: new RegExp('messageId') }) + .toMatchSnapshot('Moonbeam xcm message sent'); + + await setTimeout(10000); + await polkadotAssetHub.dev.timeTravel(1); + + await checkSystemEvents(polkadotAssetHub, 'foreignAssets').toMatchSnapshot('assethub foreign assets burned'); + await checkSystemEvents(polkadotAssetHub, 'xcmpQueue', 'XcmpMessageSent').toMatchSnapshot( + 'assetHub xcmp message sent', + ); + + const assetHubEvents = await polkadotAssetHub.api.query.system.events(); + + const xcmMessageProcessed = assetHubEvents[assetHubEvents.length - 1]; + expect(xcmMessageProcessed.phase.toString()).toEqual('Finalization'); + expect(xcmMessageProcessed.event.method).toEqual('Processed'); + expect(xcmMessageProcessed.event.section).toEqual('messageQueue'); + + await setTimeout(20000); + await polkadotBridgeHub.dev.timeTravel(1); + + await checkSystemEvents(polkadotBridgeHub, 'ethereumOutboundQueue') + .redact({ redactKeys: new RegExp('nonce') }) + .toMatchSnapshot('bridgehub ethereum outbound queue events'); + + const bridgeHubEvents = await polkadotBridgeHub.api.query.system.events(); + const messageAcceptedEvent = bridgeHubEvents[bridgeHubEvents.length - 3]; + expect(messageAcceptedEvent.event.section).toEqual('ethereumOutboundQueue'); + expect(messageAcceptedEvent.event.method).toEqual('MessageAccepted'); + + const messageCommittedEvent = bridgeHubEvents[bridgeHubEvents.length - 1]; + expect(messageCommittedEvent.event.section).toEqual('ethereumOutboundQueue'); + expect(messageCommittedEvent.event.method).toEqual('MessagesCommitted'); + }, 200000); + }); +}); diff --git a/e2e-tests/moonbeam.hydration.spec.ts b/e2e-tests/moonbeam.hydration.spec.ts index 5d404261..0432ab02 100644 --- a/e2e-tests/moonbeam.hydration.spec.ts +++ b/e2e-tests/moonbeam.hydration.spec.ts @@ -80,7 +80,7 @@ describe('Moonbeam <> Hydration', () => { const recipientUpdatedGLMRBalance = await hydration.api.query.tokens.accounts(hydrationRecipientAddress, 16); await check(recipientUpdatedGLMRBalance as AccountData) - .redact({ number: 2 }) + .redact({ number: 1 }) .toMatchSnapshot('hydration recipients updated glmr balance'); expect((recipientUpdatedGLMRBalance as AccountData).free.toBigInt()).toBeGreaterThan( (recipientInitialGLMRBalance as AccountData).free.toBigInt(), @@ -133,7 +133,7 @@ describe('Moonbeam <> Hydration', () => { ).unwrapOrDefault(); await check(recipientUpdatedHDXBalance) - .redact({ number: 2 }) + .redact({ number: 1 }) .toMatchSnapshot('moonbeam recipients updated hdx balance'); expect(recipientUpdatedHDXBalance.balance.toBigInt()).toBeGreaterThan( recipientInitialHDXBalance.balance.toBigInt(), @@ -181,7 +181,7 @@ describe('Moonbeam <> Hydration', () => { const recipientUpdatedGLMRBalance = await hydration.api.query.tokens.accounts(hydrationRecipientAddress, 16); await check(recipientUpdatedGLMRBalance) - .redact({ number: 2 }) + .redact({ number: 1 }) .toMatchSnapshot('hydration recipients updated glmr balance'); expect((recipientUpdatedGLMRBalance as AccountData).free.toBigInt()).toBeGreaterThan( (recipientInitialGLMRBalance as AccountData).free.toBigInt(), @@ -234,7 +234,7 @@ describe('Moonbeam <> Hydration', () => { ).unwrapOrDefault(); await check(recipientUpdatedHDXBalance) - .redact({ number: 2 }) + .redact({ number: 1 }) .toMatchSnapshot('moonbeam recipients updated hdx balance'); expect(recipientUpdatedHDXBalance.balance.toBigInt()).toBeGreaterThan( recipientInitialHDXBalance.balance.toBigInt(), diff --git a/e2e-tests/polkadotAssetHub.bridgeHub.spec.ts b/e2e-tests/polkadotAssetHub.bridgeHub.spec.ts index 3f93fd20..ccd0be69 100644 --- a/e2e-tests/polkadotAssetHub.bridgeHub.spec.ts +++ b/e2e-tests/polkadotAssetHub.bridgeHub.spec.ts @@ -4,6 +4,7 @@ import { setTimeout } from 'timers/promises'; import { afterEach, beforeEach, expect, test } from 'vitest'; import { AssetTransferApi } from '../src/AssetTransferApi'; +import { ETHEREUM_MAINNET_NETWORK_GLOBAL_CONSENSUS_LOCATION } from '../src/consts'; const { checkSystemEvents } = withExpect(expect); @@ -62,7 +63,7 @@ describe('Polkadot AssetHub <> Ethereum', () => { const assetTransferApi = new AssetTransferApi(polkadotAssetHub.api, 'asset-hub-polkadot', xcmVersion); const tx = await assetTransferApi.createTransferTransaction( - `{"parents":"2","interior":{"X1":{"GlobalConsensus":{"Ethereum":{"chainId":"1"}}}}}`, + ETHEREUM_MAINNET_NETWORK_GLOBAL_CONSENSUS_LOCATION, alith.address, [ `{"parents":"2","interior":{"X2":[{"GlobalConsensus":{"Ethereum":{"chainId":"1"}}},{"AccountKey20":{"network":null,"key":"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"}}]}}`, @@ -110,7 +111,7 @@ describe('Polkadot AssetHub <> Ethereum', () => { const assetTransferApi = new AssetTransferApi(polkadotAssetHub.api, 'asset-hub-polkadot', xcmVersion); const tx = await assetTransferApi.createTransferTransaction( - `{"parents":"2","interior":{"X1":{"GlobalConsensus":{"Ethereum":{"chainId":"1"}}}}}`, + ETHEREUM_MAINNET_NETWORK_GLOBAL_CONSENSUS_LOCATION, alith.address, [ `{"parents":"2","interior":{"X2":[{"GlobalConsensus":{"Ethereum":{"chainId":"1"}}},{"AccountKey20":{"network":null,"key":"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"}}]}}`, diff --git a/e2e-tests/polkadotAssetHub.hydration.spec.ts b/e2e-tests/polkadotAssetHub.hydration.spec.ts index c6ba960b..1e25bdf7 100644 --- a/e2e-tests/polkadotAssetHub.hydration.spec.ts +++ b/e2e-tests/polkadotAssetHub.hydration.spec.ts @@ -83,7 +83,7 @@ describe('Polkadot AssetHub <> Hydration', () => { const recipientsUpdatedDOTBalance = await polkadotAssetHub.api.query.system.account(recipientAddress); await check(recipientsUpdatedDOTBalance) - .redact({ number: 2 }) + .redact({ number: 1 }) .toMatchSnapshot('local assethub recipient updated dot balance'); expect(recipientsUpdatedDOTBalance.data.free.toNumber()).toBeGreaterThan(1000000); }, 200000); @@ -130,7 +130,7 @@ describe('Polkadot AssetHub <> Hydration', () => { ).unwrapOrDefault(); await check(recipientsUpdatedPoolAssetBalance) - .redact({ number: 2 }) + .redact({ number: 1 }) .toMatchSnapshot('local assethub recipients updated pool asset balance'); expect(recipientsUpdatedPoolAssetBalance.balance.toNumber()).toBeGreaterThan( recipientsInitialPoolAssetBalance.balance.toNumber(), @@ -177,7 +177,7 @@ describe('Polkadot AssetHub <> Hydration', () => { const recipientUpdatedHDXBalance = await hydration.api.query.tokens.accounts(recipientAddress, 0); await check(recipientUpdatedHDXBalance as AccountData) - .redact({ number: 2 }) + .redact({ number: 1 }) .toMatchSnapshot('local hydration recipient updated balance'); expect((recipientUpdatedHDXBalance as AccountData).free.toNumber()).toBeGreaterThan( (recipientInitialHDXBalance as AccountData).free.toNumber(), @@ -228,7 +228,7 @@ describe('Polkadot AssetHub <> Hydration', () => { const recipientsUpdatedDOTBalance = await polkadotAssetHub.api.query.system.account(recipientAddress); await check(recipientsUpdatedDOTBalance) - .redact({ number: 2 }) + .redact({ number: 1 }) .toMatchSnapshot('asset hub recipients updated dot balance'); expect(recipientsUpdatedDOTBalance.data.free.toNumber()).toBeGreaterThan( recipientsInitialDOTBalance.data.free.toNumber(), @@ -272,7 +272,7 @@ describe('Polkadot AssetHub <> Hydration', () => { const recipientsUpdatedDOTBalance = await hydration.api.query.tokens.accounts(recipientAddress, 5); await check(recipientsUpdatedDOTBalance as AccountData) - .redact({ number: 2 }) + .redact({ number: 1 }) .toMatchSnapshot('hydration recipients updated dot balance'); expect((recipientsUpdatedDOTBalance as AccountData).free.toNumber()).toBeGreaterThan( (recipientsInitialDOTBalance as AccountData).free.toNumber(), @@ -415,7 +415,7 @@ describe('Polkadot AssetHub <> Hydration', () => { const recipientsUpdatedWETHBalance = await hydration.api.query.tokens.accounts(recipientAddress, 1000189); await check(recipientsUpdatedWETHBalance as AccountData) - .redact({ number: 2 }) + .redact({ number: 1 }) .toMatchSnapshot('hydration recipients updated snowbridge weth balance'); expect((recipientsUpdatedWETHBalance as AccountData).free.toNumber()).toBeGreaterThan( (recipientsInitialHydrationWETHBalance as AccountData).free.toNumber(), @@ -466,7 +466,7 @@ describe('Polkadot AssetHub <> Hydration', () => { const recipientsUpdatedDOTBalance = await polkadotAssetHub.api.query.system.account(recipientAddress); await check(recipientsUpdatedDOTBalance) - .redact({ number: 2 }) + .redact({ number: 1 }) .toMatchSnapshot('asset hub recipients updated dot balance'); expect(recipientsUpdatedDOTBalance.data.free.toNumber()).toBeGreaterThan( recipientsInitialDOTBalance.data.free.toNumber(), @@ -510,7 +510,7 @@ describe('Polkadot AssetHub <> Hydration', () => { const recipientsUpdatedDOTBalance = await hydration.api.query.tokens.accounts(recipientAddress, 5); await check(recipientsUpdatedDOTBalance as AccountData) - .redact({ number: 2 }) + .redact({ number: 1 }) .toMatchSnapshot('hydration recipients updated dot balance'); expect((recipientsUpdatedDOTBalance as AccountData).free.toNumber()).toBeGreaterThan( (recipientsInitialDOTBalance as AccountData).free.toNumber(), @@ -594,7 +594,7 @@ describe('Polkadot AssetHub <> Hydration', () => { ).unwrapOrDefault(); await check(recipientsUpdatedWETHBalance) - .redact({ number: 2 }) + .redact({ number: 1 }) .toMatchSnapshot('asset hub recipients updated snowbridge weth balance'); expect(recipientsUpdatedWETHBalance.balance.toNumber()).toBeGreaterThan( recipientsInitialWETHBalance.balance.toNumber(), @@ -653,7 +653,7 @@ describe('Polkadot AssetHub <> Hydration', () => { const recipientsUpdatedWETHBalance = await hydration.api.query.tokens.accounts(recipientAddress, 1000189); await check(recipientsUpdatedWETHBalance as AccountData) - .redact({ number: 2 }) + .redact({ number: 1 }) .toMatchSnapshot('hydration recipients updated snowbridge weth balance'); expect((recipientsUpdatedWETHBalance as AccountData).free.toNumber()).toBeGreaterThan( (recipientsInitialHydrationWETHBalance as AccountData).free.toNumber(), diff --git a/src/consts.ts b/src/consts.ts index 4323655d..9f4257ea 100644 --- a/src/consts.ts +++ b/src/consts.ts @@ -85,3 +85,7 @@ export const SYSTEM_AND_PARACHAINS_RELAY_ASSET_LOCATION = '{"parents":"1","inter * The asset location of the native relay chain asset from the perspective of the Relay chain */ export const RELAY_CHAINS_NATIVE_ASSET_LOCATION = '{"parents":"0","interior":{"Here":""}}'; +/** + * The global location of the Ethereum Mainnet + */ +export const ETHEREUM_MAINNET_NETWORK_GLOBAL_CONSENSUS_LOCATION = `{"parents":"2","interior":{"X1":{"GlobalConsensus":{"Ethereum":{"chainId":"1"}}}}}`; diff --git a/src/createXcmCalls/polkadotXcm/transferAssetsUsingTypeAndThen.spec.ts b/src/createXcmCalls/polkadotXcm/transferAssetsUsingTypeAndThen.spec.ts index d74d3c04..4e5b9ec2 100644 --- a/src/createXcmCalls/polkadotXcm/transferAssetsUsingTypeAndThen.spec.ts +++ b/src/createXcmCalls/polkadotXcm/transferAssetsUsingTypeAndThen.spec.ts @@ -1,6 +1,9 @@ // Copyright 2024 Parity Technologies (UK) Ltd. +import { ETHEREUM_MAINNET_NETWORK_GLOBAL_CONSENSUS_LOCATION } from '../../consts'; import { Registry } from '../../registry'; +import { adjustedMockBifrostParachainApi } from '../../testHelpers/adjustedMockBifrostParachainApi'; +import { adjustedmockHydrationParachainApi } from '../../testHelpers/adjustedMockHydrationParachainApi'; import { adjustedMockSystemApiV1016000 } from '../../testHelpers/adjustedMockSystemApiV1016000'; import { Direction, XcmBaseArgs, XcmDirection } from '../../types'; import { transferAssetsUsingTypeAndThen } from './transferAssetsUsingTypeAndThen'; @@ -290,4 +293,85 @@ describe('transferAssetsUsingTypeAndThen', () => { }); }); }); + describe('ParaToEthereum', () => { + it('Should correctly construct a transferAssetsUsingTypeAndThen tx from Hydration to Ethereum', async () => { + const registry = new Registry('hydradx', { + injectedRegistry: { + polkadot: { + 2034: { + tokens: [], + assetsInfo: {}, + foreignAssetsInfo: {}, + poolPairsInfo: {}, + specName: 'hydradx', + xcAssetsData: [ + { + paraID: 0, + symbol: 'WETH.snow', + decimals: 18, + xcmV1MultiLocation: + '{"v1":{"parents":2,"interior":{"x2":[{"globalConsensus":{"ethereum":{"chainId":1}}},{"accountKey20":{"network":null,"key":"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"}}]}}}', + asset: '1000189', + assetHubReserveLocation: '{"parents":"1","interior":{"X1":{"Parachain":"1000"}}}', + }, + ], + }, + }, + }, + }); + const isLiquidTokenTransfer = false; + const isForeignAssetsTransfer = false; + const baseArgs: XcmBaseArgs = { + api: adjustedmockHydrationParachainApi, + direction: Direction.ParaToEthereum as XcmDirection, + destAddr: '0xf5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b', + assetIds: ['DOT', 'WETH.snow'], + amounts: ['1000000000000', '1000000000000'], + destChainId: ETHEREUM_MAINNET_NETWORK_GLOBAL_CONSENSUS_LOCATION, + xcmVersion: 4, + specName: 'hydradx', + registry, + }; + + const ext = await transferAssetsUsingTypeAndThen(baseArgs, { + paysWithFeeDest: 'DOT', + sendersAddr: '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY', + isLiquidTokenTransfer, + isForeignAssetsTransfer, + assetTransferType: 'DestinationReserve', + feesTransferType: 'DestinationReserve', + }); + + expect(ext.toHex()).toBe( + '0x4904046b0d04010100a10f0408010000070010a5d4e802020907040300c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200070010a5d4e80204010002040c16040d010000010100d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d10010102020907040300c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20002010907040c1300010300c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20004000d01020400010100f5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b2c00000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000000000', + ); + }); + it('Should correctly throw an error when a valid ERC20 token is not provided in assetIds', async () => { + const registry = new Registry('bifrost_polkadot', {}); + const isLiquidTokenTransfer = false; + const isForeignAssetsTransfer = false; + const baseArgs: XcmBaseArgs = { + api: adjustedMockBifrostParachainApi, + direction: Direction.ParaToEthereum as XcmDirection, + destAddr: '0xf5d5714c084c112843aca74f8c498da06cc5a2d63153b825189baa51043b1f0b', + assetIds: ['DOT', 'BNC'], + amounts: ['1000000000000', '1000000000000'], + destChainId: ETHEREUM_MAINNET_NETWORK_GLOBAL_CONSENSUS_LOCATION, // location destChainId, + xcmVersion: 4, + specName: 'bifrost_polkadot', + registry, + }; + + await expect(async () => { + await transferAssetsUsingTypeAndThen(baseArgs, { + paysWithFeeDest: 'DOT', + sendersAddr: '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY', + isLiquidTokenTransfer, + isForeignAssetsTransfer, + assetTransferType: 'DestinationReserve', + feesTransferType: 'DestinationReserve', + }); + }).rejects.toThrow('A valid Snowbridge ERC20 token must provided for ParaToEthereum Direction.'); + }); + }); }); diff --git a/src/errors/checkXcmTxInputs.spec.ts b/src/errors/checkXcmTxInputs.spec.ts index b40a72d2..0ab6f1d6 100644 --- a/src/errors/checkXcmTxInputs.spec.ts +++ b/src/errors/checkXcmTxInputs.spec.ts @@ -9,10 +9,10 @@ import { mockSystemApi } from '../testHelpers/mockSystemApi'; import { Direction } from '../types'; import { checkAssetIdInput, + checkAssetIdsAndAmountsMatch, checkAssetIdsAreOfSameAssetIdType, checkAssetIdsHaveNoDuplicates, checkAssetIdsLengthIsValid, - checkAssetsAmountMatch, checkBridgeTxInputs, checkDestAddrIsValid, checkDryRunCallOptionIncludesSendersAddressAndXcmFeeAsset, @@ -94,13 +94,11 @@ describe('checkRelayAmountsLength', () => { }); }); -describe('checkAssetsAmountMatch', () => { +describe('checkAssetIdsAndAmountsMatch', () => { it("Should error when inputted assetId's dont match amounts length", () => { - const err = () => checkAssetsAmountMatch(['1'], ['10', '10']); + const err = () => checkAssetIdsAndAmountsMatch(['1'], ['10', '10']); - expect(err).toThrow( - '`amounts`, and `assetIds` fields should match in length when constructing a tx from a parachain to a parachain or locally on a system parachain.', - ); + expect(err).toThrow('`amounts`, and `assetIds` fields should match in length when constructing a tx.'); }); }); diff --git a/src/errors/checkXcmTxInputs.ts b/src/errors/checkXcmTxInputs.ts index e24dec00..dab19e3e 100644 --- a/src/errors/checkXcmTxInputs.ts +++ b/src/errors/checkXcmTxInputs.ts @@ -137,14 +137,10 @@ export const checkMultiLocationAmountsLength = (amounts: string[]) => { * @param assetIds * @param amounts */ -export const checkAssetsAmountMatch = ( - assetIds: string[], - amounts: string[], - isParachainPrimaryNativeAsset?: boolean, -) => { - if (!isParachainPrimaryNativeAsset && assetIds.length !== amounts.length) { +export const checkAssetIdsAndAmountsMatch = (assetIds: string[], amounts: string[]) => { + if (assetIds.length !== amounts.length) { throw new BaseError( - '`amounts`, and `assetIds` fields should match in length when constructing a tx from a parachain to a parachain or locally on a system parachain.', + '`amounts`, and `assetIds` fields should match in length when constructing a tx.', BaseErrorsEnum.InvalidInput, ); } @@ -1234,15 +1230,15 @@ export const checkXcmTxInputs = async (baseArgs: XcmBaseArgsWithPallet, opts: Ch if (isForeignAssetsTransfer) { checkMultiLocationIdLength(assetIds); checkMultiLocationAmountsLength(amounts); - checkAssetsAmountMatch(assetIds, amounts); + checkAssetIdsAndAmountsMatch(assetIds, amounts); checkMultiLocationsContainOnlyNativeOrForeignAssetsOfDestChain(direction, destChainId, assetIds); } - checkAssetsAmountMatch(assetIds, amounts); + checkAssetIdsAndAmountsMatch(assetIds, amounts); } if (direction === Direction.SystemToSystem) { if (isForeignAssetsTransfer) { - checkAssetsAmountMatch(assetIds, amounts); + checkAssetIdsAndAmountsMatch(assetIds, amounts); checkMultiLocationsContainOnlyNativeOrForeignAssetsOfDestChain(direction, destChainId, assetIds); } checkIfNativeRelayChainAssetPresentInMultiAssetIdList(assetIds, registry); @@ -1251,7 +1247,7 @@ export const checkXcmTxInputs = async (baseArgs: XcmBaseArgsWithPallet, opts: Ch if (direction === Direction.SystemToBridge) { checkMultiLocationIdLength(assetIds); checkMultiLocationAmountsLength(amounts); - checkAssetsAmountMatch(assetIds, amounts); + checkAssetIdsAndAmountsMatch(assetIds, amounts); getGlobalConsensusSystemName(destChainId); checkBridgeTxInputs( paysWithFeeDest, @@ -1266,7 +1262,7 @@ export const checkXcmTxInputs = async (baseArgs: XcmBaseArgsWithPallet, opts: Ch if (direction === Direction.ParaToSystem || direction === Direction.ParaToPara) { CheckXTokensPalletOriginIsNonForeignAssetTx(direction, xcmPallet, isForeignAssetsTransfer); - checkAssetsAmountMatch(assetIds, amounts, isPrimaryParachainNativeAsset); + checkAssetIdsAndAmountsMatch(assetIds, amounts); } if (direction === Direction.ParaToRelay) { @@ -1275,6 +1271,7 @@ export const checkXcmTxInputs = async (baseArgs: XcmBaseArgsWithPallet, opts: Ch } if (direction === Direction.ParaToEthereum) { + checkAssetIdsAndAmountsMatch(assetIds, amounts); checkParaToEthereum(destAddr, sendersAddr, paysWithFeeDest); } }; @@ -1301,7 +1298,7 @@ export const checkParaToEthereum = (destAddr: string, sendersAddr?: string, pays }; export const checkClaimAssetsInputs = (assets: string[], amounts: string[]) => { - checkAssetsAmountMatch(assets, amounts); + checkAssetIdsAndAmountsMatch(assets, amounts); checkAssetIdsAreOfSameAssetIdType(assets); };