From 345bc358b79b1756dcd5564ffc7a777967079063 Mon Sep 17 00:00:00 2001 From: Freddy Li Date: Thu, 14 Mar 2024 16:05:51 -0400 Subject: [PATCH 1/9] add xcm test scripts --- scripts/xcm/e2e_tests.js | 41 ++ scripts/xcm/package.json | 17 + scripts/xcm/setup.js | 125 +++++ scripts/xcm/util.js | 505 ++++++++++++++++++ substrate-node/parachain/runtime/src/lib.rs | 25 +- .../parachain/runtime/src/xcm_config.rs | 23 +- .../bridge_hub_rococo_local_network.toml | 2 + 7 files changed, 726 insertions(+), 12 deletions(-) create mode 100644 scripts/xcm/e2e_tests.js create mode 100644 scripts/xcm/package.json create mode 100644 scripts/xcm/setup.js create mode 100644 scripts/xcm/util.js diff --git a/scripts/xcm/e2e_tests.js b/scripts/xcm/e2e_tests.js new file mode 100644 index 0000000..3899ee9 --- /dev/null +++ b/scripts/xcm/e2e_tests.js @@ -0,0 +1,41 @@ +// The Licensed Work is (c) 2022 Sygma +// SPDX-License-Identifier: LGPL-3.0-only + +require('dotenv').config(); + +const {ApiPromise, WsProvider, Keyring} = require('@polkadot/api'); +const {cryptoWaitReady} = require('@polkadot/util-crypto'); +const { + getNativeMultiAsset, + getUSDCMultiAsset, + getAssetDepositDest, + depositLocal, +} = require("./util"); + +async function main() { + // asset hub parachain + const assetHubProvider = new WsProvider(process.env.ASSETHUBENDPOINT || 'ws://127.0.0.1:9910'); + const assetHubApi = await ApiPromise.create({ + provider: assetHubProvider, + }); + + // bridge hub parachain + const bridgeHubProvider = new WsProvider(process.env.BRIDGEHUBENDPOINT || 'ws://127.0.0.1:8943'); + const bridgeHubApi = await ApiPromise.create({ + provider: bridgeHubProvider, + }); + + // prepare keyring + await cryptoWaitReady(); + const keyring = new Keyring({type: 'sr25519'}); + const sudo = keyring.addFromUri('//Alice'); + + // testcase 1: Native token deposit on Bridge hub, dest on relayer + await depositLocal(bridgeHubApi, getNativeMultiAsset(bridgeHubApi, 10000000000000), getAssetDepositDest(bridgeHubApi), true, sudo) + + // testcase 2: Foreign token deposit on Bridge hub, dest on relayer + await depositLocal(bridgeHubApi, getUSDCMultiAsset(bridgeHubApi, 10000000000000), getAssetDepositDest(bridgeHubApi), true, sudo) + +} + +main().catch(console.error).finally(() => process.exit()); diff --git a/scripts/xcm/package.json b/scripts/xcm/package.json new file mode 100644 index 0000000..965d934 --- /dev/null +++ b/scripts/xcm/package.json @@ -0,0 +1,17 @@ +{ + "name": "sygma-substrate-pallet-xcm-scripts", + "version": "1.0.0", + "main": "index.js", + "dependencies": { + "@polkadot/api": "^10.11.2", + "@polkadot/api-augment": "^10.11.2", + "@polkadot/keyring": "^12.6.2", + "@polkadot/rpc-provider": "^10.11.2", + "@polkadot/types": "^10.11.2", + "@polkadot/util": "^12.6.2", + "@polkadot/util-crypto": "^12.6.2", + "bn.js": "^5.2.1", + "dotenv": "^16.0.3" + } +} + diff --git a/scripts/xcm/setup.js b/scripts/xcm/setup.js new file mode 100644 index 0000000..2284023 --- /dev/null +++ b/scripts/xcm/setup.js @@ -0,0 +1,125 @@ +// The Licensed Work is (c) 2022 Sygma +// SPDX-License-Identifier: LGPL-3.0-only + +require('dotenv').config(); + +const {ApiPromise, WsProvider, Keyring} = require('@polkadot/api'); +const {cryptoWaitReady} = require('@polkadot/util-crypto'); +const { + transferBalance, + setFeeHandler, + setMpcAddress, + registerDomain, + setFee, + setFeeRate, + getNativeAssetId, + createAsset, + setAssetMetadata, + mintAsset, + getUSDCAssetId, + queryBridgePauseStatus +} = require("./util"); + +const BN = require('bn.js'); +const bn1e12 = new BN(10).pow(new BN(12)); +const bn1e18 = new BN(10).pow(new BN(18)); +const bn1e20 = new BN(10).pow(new BN(20)); + +const feeHandlerType = { + BasicFeeHandler: "BasicFeeHandler", + PercentageFeeHandler: "PercentageFeeHandler", + DynamicFeeHandler: "DynamicFeeHandler" +} + +const supportedDestDomains = [ + { + domainID: 1, + chainID: 1 + } +] + +// those account are configured in the substrate-node runtime, and are only applicable for sygma pallet standalone node, +// other parachain might have different runtime config so those account address need to be adjusted accordingly +const FeeReserveAccountAddress = "5ELLU7ibt5ZrNEYRwohtaRBDBa3TzcWwwPELBPSWWd2mbgv3"; +const NativeTokenTransferReserveAccount = "5EYCAe5jLbHcAAMKvLFSXgCTbPrLgBJusvPwfKcaKzuf5X5e"; +const OtherTokenTransferReserveAccount = "5EYCAe5jLbHcAAMKvLFiGhk3htXY8jQncbLTDGJQnpnPMAVp"; + +async function main() { + // asset hub parachain + const assetHubProvider = new WsProvider(process.env.ASSETHUBENDPOINT || 'ws://127.0.0.1:9910'); + const assetHubApi = await ApiPromise.create({ + provider: assetHubProvider, + }); + + // bridge hub parachain + const bridgeHubProvider = new WsProvider(process.env.BRIDGEHUBENDPOINT || 'ws://127.0.0.1:8943'); + const bridgeHubApi = await ApiPromise.create({ + provider: bridgeHubProvider, + }); + + // prepare keyring + await cryptoWaitReady(); + const keyring = new Keyring({type: 'sr25519'}); + const sudo = keyring.addFromUri('//Alice'); + + // UsdcAssetId: AssetId defined in runtime.rs + const usdcAssetID = 2000; + const usdcAdmin = sudo.address; + const usdcMinBalance = 100; + const usdcName = "USDC test asset"; + const usdcSymbol = "USDC"; + const usdcDecimal = 12; + + // create USDC test asset (foreign asset) on asset hub + await createAsset(assetHubApi, usdcAssetID, usdcAdmin, usdcMinBalance, true, sudo); + await setAssetMetadata(assetHubApi, usdcAssetID, usdcName, usdcSymbol, usdcDecimal, true, sudo); + await mintAsset(assetHubApi, usdcAssetID, usdcAdmin, bn1e12.mul(new BN(100)), true, sudo); // mint 100 USDC to Alice + + // create USDC test asset (foreign asset) on bridge hub + await createAsset(bridgeHubApi, usdcAssetID, usdcAdmin, usdcMinBalance, true, sudo); + await setAssetMetadata(bridgeHubApi, usdcAssetID, usdcName, usdcSymbol, usdcDecimal, true, sudo); + await mintAsset(bridgeHubApi, usdcAssetID, usdcAdmin, bn1e12.mul(new BN(100)), true, sudo); // mint 100 USDC to Alice + + // make sure access segregator is set up for Alice before setting up all sygma pallet! + // sygma config + const basicFeeAmount = bn1e12.mul(new BN(1)); // 1 * 10 ** 12 + const percentageFeeRate = 500; // 5% + const feeRateLowerBound = 0; + const feeRateUpperBound = bn1e12.mul(new BN(1000)); // 1000 * 10 ** 12 + const mpcAddr = process.env.MPCADDR || "0x1c5541A79AcC662ab2D2647F3B141a3B7Cdb2Ae4"; + + // register sygma on bridge hub parachain + // register dest domains + for (const domain of supportedDestDomains) { + await registerDomain(bridgeHubApi, domain.domainID, domain.chainID, true, sudo); + } + // set fee rate for native asset for domains + for (const domain of supportedDestDomains) { + await setFeeHandler(bridgeHubApi, domain.domainID, getNativeAssetId(bridgeHubApi), feeHandlerType.PercentageFeeHandler, true, sudo) + await setFeeRate(bridgeHubApi, domain.domainID, getNativeAssetId(bridgeHubApi), percentageFeeRate, feeRateLowerBound, feeRateUpperBound, true, sudo); + } + // set fee for tokens with domains on bridge hub + for (const domain of supportedDestDomains) { + await setFeeHandler(bridgeHubApi, domain.domainID, getUSDCAssetId(bridgeHubApi), feeHandlerType.PercentageFeeHandler, true, sudo) + await setFeeRate(bridgeHubApi, domain.domainID, getUSDCAssetId(bridgeHubApi), percentageFeeRate, feeRateLowerBound, feeRateUpperBound,true, sudo); + } + + // transfer some native asset to FeeReserveAccount and TransferReserveAccount as Existential Deposit(aka ED) on bridge hub + await transferBalance(bridgeHubApi, FeeReserveAccountAddress, bn1e12.mul(new BN(10000)), true, sudo); // set balance to 10000 native asset + await transferBalance(bridgeHubApi, NativeTokenTransferReserveAccount, bn1e12.mul(new BN(10000)), true, sudo); // set balance to 10000 native asset reserved account + await transferBalance(bridgeHubApi, OtherTokenTransferReserveAccount, bn1e12.mul(new BN(10000)), true, sudo); // set balance to 10000 other asset reserved account + + // set up MPC address(will also unpause all registered domains) on bridge hub + if (mpcAddr) { + console.log(`set up mpc address: ${mpcAddr}`); + await setMpcAddress(bridgeHubApi, mpcAddr, true, sudo); + // bridge should be unpaused by the end of the setup + for (const domain of supportedDestDomains) { + if (!await queryBridgePauseStatus(bridgeHubApi, domain.domainID)) console.log(`DestDomainID: ${domain.domainID} is readyβœ…`); + } + } + + console.log('πŸš€ Sygma substrate pallet setup is done! πŸš€'); +} + +main().catch(console.error).finally(() => process.exit()); diff --git a/scripts/xcm/util.js b/scripts/xcm/util.js new file mode 100644 index 0000000..55bc8a6 --- /dev/null +++ b/scripts/xcm/util.js @@ -0,0 +1,505 @@ +// The Licensed Work is (c) 2022 Sygma +// SPDX-License-Identifier: LGPL-3.0-only + +require('dotenv').config(); + +async function transferBalance(api, who, value, finalization, sudo) { + return new Promise(async (resolve, reject) => { + const nonce = Number((await api.query.system.account(sudo.address)).nonce); + + console.log( + `--- Submitting extrinsic to transfer balance of ${who} to ${value}. (nonce: ${nonce}) ---` + ); + const unsub = await api.tx.balances.transferKeepAlive(who, value) + .signAndSend(sudo, {nonce: nonce, era: 0}, (result) => { + console.log(`Current status is ${result.status}`); + if (result.status.isInBlock) { + console.log( + `Transaction included at blockHash ${result.status.asInBlock}` + ); + if (finalization) { + console.log('Waiting for finalization...'); + } else { + unsub(); + resolve(); + } + } else if (result.status.isFinalized) { + console.log( + `Transaction finalized at blockHash ${result.status.asFinalized}` + ); + unsub(); + resolve(); + } else if (result.isError) { + console.log(`Transaction Error`); + reject(`Transaction Error`); + } + }); + }); +} + +async function setFeeHandler(api, domainID, asset, feeHandlerType, finalization, sudo) { + return new Promise(async (resolve, reject) => { + const nonce = Number((await api.query.system.account(sudo.address)).nonce); + + console.log( + `--- Submitting extrinsic to set fee handler on domainID ${domainID}. (nonce: ${nonce}) ---` + ); + const unsub = await api.tx.sygmaFeeHandlerRouter.setFeeHandler(domainID, asset, feeHandlerType) + .signAndSend(sudo, {nonce: nonce, era: 0}, (result) => { + console.log(`Current status is ${result.status}`); + if (result.status.isInBlock) { + console.log( + `Transaction included at blockHash ${result.status.asInBlock}` + ); + if (finalization) { + console.log('Waiting for finalization...'); + } else { + unsub(); + resolve(); + } + } else if (result.status.isFinalized) { + console.log( + `Transaction finalized at blockHash ${result.status.asFinalized}` + ); + unsub(); + resolve(); + } else if (result.isError) { + console.log(`Transaction Error`); + reject(`Transaction Error`); + } + }); + }); +} + +async function setFee(api, domainID, asset, amount, finalization, sudo) { + return new Promise(async (resolve, reject) => { + const nonce = Number((await api.query.system.account(sudo.address)).nonce); + + console.log( + `--- Submitting extrinsic to set basic fee on domainID ${domainID}. (nonce: ${nonce}) ---` + ); + const unsub = await api.tx.sygmaBasicFeeHandler.setFee(domainID, asset, amount) + .signAndSend(sudo, {nonce: nonce, era: 0}, (result) => { + console.log(`Current status is ${result.status}`); + if (result.status.isInBlock) { + console.log( + `Transaction included at blockHash ${result.status.asInBlock}` + ); + if (finalization) { + console.log('Waiting for finalization...'); + } else { + unsub(); + resolve(); + } + } else if (result.status.isFinalized) { + console.log( + `Transaction finalized at blockHash ${result.status.asFinalized}` + ); + unsub(); + resolve(); + } else if (result.isError) { + console.log(`Transaction Error`); + reject(`Transaction Error`); + } + }); + }); +} + +async function setFeeRate(api, domainID, asset, feeRate, feeRateLowerBound, feeRateUpperBound, finalization, sudo) { + return new Promise(async (resolve, reject) => { + const nonce = Number((await api.query.system.account(sudo.address)).nonce); + + console.log( + `--- Submitting extrinsic to set percentage fee rate on domainID ${domainID}. (nonce: ${nonce}) ---` + ); + const unsub = await api.tx.sygmaPercentageFeeHandler.setFeeRate(domainID, asset, feeRate, feeRateLowerBound, feeRateUpperBound) + .signAndSend(sudo, {nonce: nonce, era: 0}, (result) => { + console.log(`Current status is ${result.status}`); + if (result.status.isInBlock) { + console.log( + `Transaction included at blockHash ${result.status.asInBlock}` + ); + if (finalization) { + console.log('Waiting for finalization...'); + } else { + unsub(); + resolve(); + } + } else if (result.status.isFinalized) { + console.log( + `Transaction finalized at blockHash ${result.status.asFinalized}` + ); + unsub(); + resolve(); + } else if (result.isError) { + console.log(`Transaction Error`); + reject(`Transaction Error`); + } + }); + }); +} + +async function setMpcAddress(api, mpcAddr, finalization, sudo) { + return new Promise(async (resolve, reject) => { + const nonce = Number((await api.query.system.account(sudo.address)).nonce); + + console.log( + `--- Submitting extrinsic to set MPC address. (nonce: ${nonce}) ---` + ); + const unsub = await api.tx.sygmaBridge.setMpcAddress(mpcAddr) + .signAndSend(sudo, {nonce: nonce, era: 0}, (result) => { + console.log(`Current status is ${result.status}`); + if (result.status.isInBlock) { + console.log( + `Transaction included at blockHash ${result.status.asInBlock}` + ); + if (finalization) { + console.log('Waiting for finalization...'); + } else { + unsub(); + resolve(); + } + } else if (result.status.isFinalized) { + console.log( + `Transaction finalized at blockHash ${result.status.asFinalized}` + ); + unsub(); + resolve(); + } else if (result.isError) { + console.log(`Transaction Error`); + reject(`Transaction Error`); + } + }); + }); +} + +async function queryBridgePauseStatus(api, domainID) { + let result = await api.query.sygmaBridge.isPaused(domainID); + return result.toJSON() +} + +async function createAsset(api, id, admin, minBalance, finalization, sudo) { + return new Promise(async (resolve, reject) => { + const nonce = Number((await api.query.system.account(sudo.address)).nonce); + + console.log( + `--- Submitting extrinsic to create asset: (nonce: ${nonce}) ---` + ); + + const unsub = await api.tx.assets.create(id, admin, minBalance) + .signAndSend(sudo, {nonce: nonce, era: 0}, (result) => { + console.log(`Current status is ${result.status}`); + if (result.status.isInBlock) { + console.log( + `Transaction included at blockHash ${result.status.asInBlock}` + ); + if (finalization) { + console.log('Waiting for finalization...'); + } else { + unsub(); + resolve(); + } + } else if (result.status.isFinalized) { + console.log( + `Transaction finalized at blockHash ${result.status.asFinalized}` + ); + unsub(); + resolve(); + } else if (result.isError) { + console.log(`Transaction Error`); + reject(`Transaction Error`); + } + }); + }); +} + +async function setAssetMetadata(api, id, name, symbol, decimals, finalization, sudo) { + return new Promise(async (resolve, reject) => { + const nonce = Number((await api.query.system.account(sudo.address)).nonce); + + console.log( + `--- Submitting extrinsic to register asset metadata: (nonce: ${nonce}) ---` + ); + const unsub = await api.tx.assets.setMetadata(id, name, symbol, decimals) + .signAndSend(sudo, {nonce: nonce, era: 0}, (result) => { + console.log(`Current status is ${result.status}`); + if (result.status.isInBlock) { + console.log( + `Transaction included at blockHash ${result.status.asInBlock}` + ); + if (finalization) { + console.log('Waiting for finalization...'); + } else { + unsub(); + resolve(); + } + } else if (result.status.isFinalized) { + console.log( + `Transaction finalized at blockHash ${result.status.asFinalized}` + ); + unsub(); + resolve(); + } else if (result.isError) { + console.log(`Transaction Error`); + reject(`Transaction Error`); + } + }); + }); +} + +async function mintAsset(api, id, recipient, amount, finalization, sudo) { + return new Promise(async (resolve, reject) => { + const nonce = Number((await api.query.system.account(sudo.address)).nonce); + + console.log( + `--- Submitting extrinsic to mint asset to ${recipient}: (nonce: ${nonce}) ---` + ); + const unsub = await api.tx.assets.mint(id, recipient, amount) + .signAndSend(sudo, {nonce: nonce, era: 0}, (result) => { + console.log(`Current status is ${result.status}`); + if (result.status.isInBlock) { + console.log( + `Transaction included at blockHash ${result.status.asInBlock}` + ); + if (finalization) { + console.log('Waiting for finalization...'); + } else { + unsub(); + resolve(); + } + } else if (result.status.isFinalized) { + console.log( + `Transaction finalized at blockHash ${result.status.asFinalized}` + ); + unsub(); + resolve(); + } else if (result.isError) { + console.log(`Transaction Error`); + reject(`Transaction Error`); + } + }); + }); +} + +async function registerDomain(api, domainID, chainID, finalization, sudo) { + return new Promise(async (resolve, reject) => { + const nonce = Number((await api.query.system.account(sudo.address)).nonce); + + console.log( + `--- Submitting extrinsic to register domainID ${domainID} with chainID ${chainID}. (nonce: ${nonce}) ---` + ); + const unsub = await api.tx.sygmaBridge.registerDomain(domainID, chainID) + .signAndSend(sudo, {nonce: nonce, era: 0}, (result) => { + console.log(`Current status is ${result.status}`); + if (result.status.isInBlock) { + console.log( + `Transaction included at blockHash ${result.status.asInBlock}` + ); + if (finalization) { + console.log('Waiting for finalization...'); + } else { + unsub(); + resolve(); + } + } else if (result.status.isFinalized) { + console.log( + `Transaction finalized at blockHash ${result.status.asFinalized}` + ); + unsub(); + resolve(); + } else if (result.isError) { + console.log(`Transaction Error`); + reject(`Transaction Error`); + } + }); + }); +} + +function getNativeAssetId(api) { + return api.createType('StagingXcmV3MultiassetAssetId', { + Concrete: api.createType('StagingXcmV3MultiLocation', { + parents: 0, + interior: api.createType('StagingXcmV3Junctions', 'Here') + }) + }) +} + +function getNativeMultiAsset(api, amount) { + return api.createType('StagingXcmV3MultiAsset', { + id: getNativeAssetId(api), + fun: api.createType('StagingXcmV3MultiassetFungibility', { + Fungible: api.createType('Compact', amount) + }) + }) +} + +function getAssetDepositDest(api) { + return api.createType('StagingXcmV3MultiLocation', { + parents: 0, + interior: api.createType('StagingXcmV3Junctions', { + X3: [ + api.createType('StagingXcmV3Junction', { + GeneralKey: { + length: 5, + data: '0x7379676d61000000000000000000000000000000000000000000000000000000' + } + }), + api.createType('StagingXcmV3Junction', { + GeneralIndex: '1' + }), + api.createType('StagingXcmV3Junction', { + GeneralKey: { + length: 20, + data: '0x1abd6948e422a1b6ced1ba28ba72ca562333df01000000000000000000000000' + } + }), + ] + }) + }) +} + +function getUSDCAssetId(api) { + return api.createType('StagingXcmV3MultiassetAssetId', { + Concrete: api.createType('StagingXcmV3MultiLocation', { + parents: 1, + interior: api.createType('StagingXcmV3Junctions', { + X3: [ + api.createType('StagingXcmV3Junction', { + Parachain: api.createType('Compact', 2005) + }), + api.createType('StagingXcmV3Junction', { + // 0x7379676d61 is general key of "sygma" defined in sygma substrate pallet runtime for testing + // see UsdcLocation definition in runtime.rs + GeneralKey: { + length: 5, + data: '0x7379676d61000000000000000000000000000000000000000000000000000000' + } + }), + api.createType('StagingXcmV3Junction', { + // 0x75736463 is general key of "usdc" defined in sygma substrate pallet runtime for testing + // see UsdcLocation definition in runtime.rs + GeneralKey: { + length: 4, + data: '0x7573646300000000000000000000000000000000000000000000000000000000' + } + }), + ] + }) + }) + }) +} + +function getUSDCMultiAsset(api, amount) { + return api.createType('StagingXcmV3MultiAsset', { + id: getUSDCAssetId(api), + fun: api.createType('StagingXcmV3MultiassetFungibility', { + Fungible: api.createType('Compact', amount) + }) + }) +} + +async function executeProposal(api, proposalList, signature, finalization, sudo) { + return new Promise(async (resolve, reject) => { + const nonce = Number((await api.query.system.account(sudo.address)).nonce); + + console.log( + `--- Submitting extrinsic to execute dummy proposal for testing : (nonce: ${nonce}) ---` + ); + const unsub = await api.tx.sygmaBridge.executeProposal(proposalList, signature) + .signAndSend(sudo, {nonce: nonce, era: 0}, (result) => { + console.log(`Current status is ${result.status}`); + if (result.status.isInBlock) { + console.log( + `Transaction included at blockHash ${result.status.asInBlock}` + ); + if (finalization) { + console.log('Waiting for finalization...'); + } else { + unsub(); + resolve(); + } + } else if (result.status.isFinalized) { + console.log( + `Transaction finalized at blockHash ${result.status.asFinalized}` + ); + unsub(); + resolve(); + } else if (result.isError) { + console.log(`Transaction Error`); + reject(`Transaction Error`); + } + }); + }); +} + +async function depositLocal(api, asset, dest, finalization, sudo) { + return new Promise(async (resolve, reject) => { + const nonce = Number((await api.query.system.account(sudo.address)).nonce); + + console.log( + `--- Submitting extrinsic to deposit. (nonce: ${nonce}) ---` + ); + const unsub = await api.tx.sygmaBridge.deposit(asset, dest) + .signAndSend(sudo, {nonce: nonce, era: 0}, (result) => { + console.log(`Current status is ${result.status}`); + if (result.status.isInBlock) { + console.log( + `Transaction included at blockHash ${result.status.asInBlock}` + ); + if (finalization) { + console.log('Waiting for finalization...'); + } else { + unsub(); + resolve(); + } + } else if (result.status.isFinalized) { + console.log( + `Transaction finalized at blockHash ${result.status.asFinalized}` + ); + unsub(); + resolve(); + } else if (result.isError) { + console.log(`Transaction Error`); + reject(`Transaction Error`); + } + }); + }); +} + +async function queryAssetBalance(api, assetID, account) { + let result = await api.query.assets.account(assetID, account); + return result.toHuman() +} + +async function queryBalance(api, account) { + let result = await api.query.system.account(account); + return result.toHuman() +} + +async function queryMPCAddress(api) { + let result = await api.query.sygmaBridge.mpcAddr(); + return result.toJSON() +} + +module.exports = { + getNativeAssetId, + getNativeMultiAsset, + getAssetDepositDest, + getUSDCAssetId, + getUSDCMultiAsset, + depositLocal, + registerDomain, + mintAsset, + setAssetMetadata, + createAsset, + queryBridgePauseStatus, + setMpcAddress, + setFee, + setFeeRate, + setFeeHandler, + transferBalance, + executeProposal, + queryAssetBalance, + queryBalance, + queryMPCAddress, +} diff --git a/substrate-node/parachain/runtime/src/lib.rs b/substrate-node/parachain/runtime/src/lib.rs index 655b911..9ad48f9 100644 --- a/substrate-node/parachain/runtime/src/lib.rs +++ b/substrate-node/parachain/runtime/src/lib.rs @@ -883,18 +883,25 @@ impl ExtractDestinationData for DestinationDataParser { match (dest.parents, &dest.interior) { ( 0, - Junctions::X2( - GeneralKey { length: recipient_len, data: recipient }, - GeneralKey { length: _domain_len, data: dest_domain_id }, + Junctions::X3( + GeneralKey { + length: path_len, + data: sygma_path, + }, + GeneralIndex(dest_domain_id), + GeneralKey { + length: recipient_len, + data: recipient, + }, ), ) => { - let d = u8::default(); - let domain_id = dest_domain_id.as_slice().first().unwrap_or(&d); - if *domain_id == d { - return None; + if &sygma_path[..*path_len as usize] == &[0x73, 0x79, 0x67, 0x6d, 0x61] { + return TryInto::::try_into(*dest_domain_id).ok().map( + |domain_id| (recipient[..*recipient_len as usize].to_vec(), domain_id), + ); } - Some((recipient[..*recipient_len as usize].to_vec(), *domain_id)) - }, + None + } _ => None, } } diff --git a/substrate-node/parachain/runtime/src/xcm_config.rs b/substrate-node/parachain/runtime/src/xcm_config.rs index 4037bc3..9caa62e 100644 --- a/substrate-node/parachain/runtime/src/xcm_config.rs +++ b/substrate-node/parachain/runtime/src/xcm_config.rs @@ -25,12 +25,17 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowTopLevelPaidExecutionFrom, CurrencyAdapter, DenyReserveTransferToRelayChain, DenyThenTry, EnsureXcmOrigin, - FixedWeightBounds, IsConcrete, NativeAsset, ParentIsPreset, RelayChainAsNative, + FixedWeightBounds, NativeAsset, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, WithComputedOrigin, WithUniqueTopic, }; -use xcm_executor::XcmExecutor; +use xcm_executor::{ + traits::{MatchesFungible}, + XcmExecutor, +}; +use sp_runtime::traits::CheckedConversion; + parameter_types! { pub const RelayLocation: MultiLocation = MultiLocation::parent(); @@ -59,7 +64,7 @@ pub type LocalAssetTransactor = CurrencyAdapter< // Use this currency: Balances, // Use this currency when it is a fungible asset matching the given location or name: - IsConcrete, + NativeAssetMatcher>, // Do a simple punn to convert an AccountId32 MultiLocation into a native chain account ID: LocationToAccountId, // Our chain's account ID type (we can't get away without mentioning it explicitly): @@ -221,6 +226,18 @@ impl sygma_bridge_forwarder::Config for Runtime { type XCMBridge = BridgeImpl; } +pub struct NativeAssetMatcher(PhantomData); +impl> MatchesFungible for NativeAssetMatcher { + fn matches_fungible(a: &MultiAsset) -> Option { + match (&a.id, &a.fun) { + (Concrete(_), Fungible(ref amount)) if C::is_native_asset(a) => { + CheckedConversion::checked_from(*amount) + } + _ => None, + } + } +} + /// NativeAssetTypeIdentifier impl AssetTypeIdentifier for XCMAssetTransactor /// This impl is only for local mock purpose, the integrated parachain might have their own version pub struct NativeAssetTypeIdentifier(PhantomData); diff --git a/zombienet/bridge_hub_rococo_local_network.toml b/zombienet/bridge_hub_rococo_local_network.toml index ccf13c8..e2042f6 100644 --- a/zombienet/bridge_hub_rococo_local_network.toml +++ b/zombienet/bridge_hub_rococo_local_network.toml @@ -75,6 +75,8 @@ cumulus_based = true [[parachains.collators]] name = "asset-hub-bob-collator" + rpc_port = 9811 + ws_port = 9810 command = "./polkadot-parachain" args = [ "-lparachain=debug,xcm=trace,runtime::bridge-transfer=trace", From 96e1ae9646786c42afdfb2904db710b7758f4a55 Mon Sep 17 00:00:00 2001 From: Freddy Li Date: Fri, 15 Mar 2024 12:47:07 -0400 Subject: [PATCH 2/9] add checks for e2e test --- scripts/xcm/e2e_tests.js | 107 +++++++++++++++++++++++++++++++++++---- scripts/xcm/setup.js | 35 ++++++------- scripts/xcm/util.js | 29 +++++++++++ 3 files changed, 145 insertions(+), 26 deletions(-) diff --git a/scripts/xcm/e2e_tests.js b/scripts/xcm/e2e_tests.js index 3899ee9..5532a09 100644 --- a/scripts/xcm/e2e_tests.js +++ b/scripts/xcm/e2e_tests.js @@ -6,21 +6,28 @@ require('dotenv').config(); const {ApiPromise, WsProvider, Keyring} = require('@polkadot/api'); const {cryptoWaitReady} = require('@polkadot/util-crypto'); const { + assetHubProvider, + bridgeHubProvider, getNativeMultiAsset, getUSDCMultiAsset, getAssetDepositDest, depositLocal, + queryBalance, + queryAssetBalance, + FeeReserveAccount, + NativeTokenTransferReserveAccount, + OtherTokenTransferReserveAccount, + usdcAssetID, + usdcMinBalance, + usdcName, + usdcSymbol, + usdcDecimal, } = require("./util"); async function main() { - // asset hub parachain - const assetHubProvider = new WsProvider(process.env.ASSETHUBENDPOINT || 'ws://127.0.0.1:9910'); const assetHubApi = await ApiPromise.create({ provider: assetHubProvider, }); - - // bridge hub parachain - const bridgeHubProvider = new WsProvider(process.env.BRIDGEHUBENDPOINT || 'ws://127.0.0.1:8943'); const bridgeHubApi = await ApiPromise.create({ provider: bridgeHubProvider, }); @@ -30,12 +37,94 @@ async function main() { const keyring = new Keyring({type: 'sr25519'}); const sudo = keyring.addFromUri('//Alice'); - // testcase 1: Native token deposit on Bridge hub, dest on relayer - await depositLocal(bridgeHubApi, getNativeMultiAsset(bridgeHubApi, 10000000000000), getAssetDepositDest(bridgeHubApi), true, sudo) + // collection of failed testcases + let failedTestcases = []; + + // run testcases + await testcase1(bridgeHubApi, sudo, failedTestcases); + await testcase2(bridgeHubApi, sudo, failedTestcases); + + // checking if any testcase failed + for (const item of failedTestcases) { + console.error('\x1b[31m%s\x1b[0m', item); + return + } + console.log('\x1b[32m%s\x1b[0m', "All testcases pass"); +} + +// testcase 1: Native token deposit on Bridge hub, dest on relayer +async function testcase1(bridgeHubApi, sudo, failedTestcases) { + const nativeBalanceBeforeAlice = await queryBalance(bridgeHubApi, sudo.address); + console.log('Alice native asset balance before: ', nativeBalanceBeforeAlice.data.free); + + const nativeBalanceBeforeNativeTokenTransferReserveAccount = await queryBalance(bridgeHubApi, NativeTokenTransferReserveAccount); + console.log('token reserve account native asset balance before: ', nativeBalanceBeforeNativeTokenTransferReserveAccount.data.free); + + const nativeBalanceBeforeFeeAccount = await queryBalance(bridgeHubApi, FeeReserveAccount); + console.log('fee account native asset balance before: ', nativeBalanceBeforeFeeAccount.data.free); + + const depositAmount = 10000000000000; + await depositLocal(bridgeHubApi, getNativeMultiAsset(bridgeHubApi, depositAmount), getAssetDepositDest(bridgeHubApi), true, sudo) + + const nativeBalanceAfterAlice = await queryBalance(bridgeHubApi, sudo.address); + console.log('Alice native asset balance after: ', nativeBalanceAfterAlice.data.free); + + const nativeBalanceAfterNativeTokenTransferReserveAccount = await queryBalance(bridgeHubApi, NativeTokenTransferReserveAccount); + console.log('token reserve account native asset balance after: ', nativeBalanceAfterNativeTokenTransferReserveAccount.data.free); - // testcase 2: Foreign token deposit on Bridge hub, dest on relayer - await depositLocal(bridgeHubApi, getUSDCMultiAsset(bridgeHubApi, 10000000000000), getAssetDepositDest(bridgeHubApi), true, sudo) + const nativeBalanceAfterFeeAccount = await queryBalance(bridgeHubApi, FeeReserveAccount); + console.log('fee account native asset balance after: ', nativeBalanceAfterFeeAccount.data.free); + // Alice balance should be deducted by 10 + tx fee, so the before - after should be greater than 10 tokens + if (nativeBalanceBeforeAlice.data.free - nativeBalanceAfterAlice.data.free <= depositAmount) { + failedTestcases.push("testcase 1 failed: Alice balance not match") + } + // balance reserve account should add deposit amount - fee which is 9,500,000,000,000 + if (nativeBalanceBeforeNativeTokenTransferReserveAccount.data.free - nativeBalanceAfterNativeTokenTransferReserveAccount.data.free === 9500000000000) { + failedTestcases.push("testcase 1 failed: NativeTokenTransferReserveAccount balance not match") + } + // fee account should add 9,500,000,000,000 + if (nativeBalanceAfterFeeAccount.data.free - nativeBalanceBeforeFeeAccount.data.free === 9500000000000) { + failedTestcases.push("testcase 1 failed: FeeAccount balance not match") + } +} + +// testcase 2: Foreign token deposit on Bridge hub, dest on relayer +async function testcase2(bridgeHubApi, sudo, failedTestcases) { + const usdcBalanceBeforeAlice = await queryAssetBalance(bridgeHubApi, usdcAssetID, sudo.address); + console.log('Alice USDC asset balance before: ', usdcBalanceBeforeAlice.balance); + + const usdcBalanceBeforeOtherTokenTransferReserveAccount = await queryAssetBalance(bridgeHubApi, usdcAssetID, OtherTokenTransferReserveAccount); + console.log('OtherTokenTransferReserveAccount USDC asset balance before: ', usdcBalanceBeforeOtherTokenTransferReserveAccount.balance); + + const usdcBalanceBeforeFeeReserveAccount = await queryAssetBalance(bridgeHubApi, usdcAssetID, FeeReserveAccount); + console.log('FeeReserveAccountAddress USDC asset balance before: ', usdcBalanceBeforeFeeReserveAccount.balance); + + const usdcDepositAmount = 10000000000000; + await depositLocal(bridgeHubApi, getUSDCMultiAsset(bridgeHubApi, usdcDepositAmount), getAssetDepositDest(bridgeHubApi), true, sudo) + + const usdcBalanceAfterAlice = await queryAssetBalance(bridgeHubApi, usdcAssetID, sudo.address); + console.log('Alice USDC asset balance after: ', usdcBalanceAfterAlice.balance); + + const usdcBalanceAfterOtherTokenTransferReserveAccount = await queryAssetBalance(bridgeHubApi, usdcAssetID, OtherTokenTransferReserveAccount); + console.log('OtherTokenTransferReserveAccount USDC asset balance after: ', usdcBalanceAfterOtherTokenTransferReserveAccount.balance); + + const usdcBalanceAfterFeeReserveAccount = await queryAssetBalance(bridgeHubApi, usdcAssetID, FeeReserveAccount); + console.log('FeeReserveAccountAddress USDC asset balance after: ', usdcBalanceAfterFeeReserveAccount.balance); + + // Alice should be deducted by 10 USDC tokens + if (usdcBalanceBeforeAlice.balance - usdcBalanceAfterAlice.balance === usdcDepositAmount) { + failedTestcases.push("testcase 2 failed: Alice USDC token balance not match") + } + // OtherTokenTransferReserveAccount should add deposit amount - fee + if (usdcBalanceBeforeOtherTokenTransferReserveAccount.balance - usdcBalanceAfterOtherTokenTransferReserveAccount.balance === usdcDepositAmount - 500000000000) { + failedTestcases.push("testcase 2 failed: OtherTokenTransferReserveAccount USDC token balance not match") + } + // FeeReserveAccount should add fee which is 500000000000 + if (usdcBalanceAfterFeeReserveAccount.balance - usdcBalanceBeforeFeeReserveAccount.balance === 500000000000) { + failedTestcases.push("testcase 2 failed: FeeReserveAccount USDC token balance not match") + } } main().catch(console.error).finally(() => process.exit()); + diff --git a/scripts/xcm/setup.js b/scripts/xcm/setup.js index 2284023..5aac619 100644 --- a/scripts/xcm/setup.js +++ b/scripts/xcm/setup.js @@ -6,6 +6,8 @@ require('dotenv').config(); const {ApiPromise, WsProvider, Keyring} = require('@polkadot/api'); const {cryptoWaitReady} = require('@polkadot/util-crypto'); const { + assetHubProvider, + bridgeHubProvider, transferBalance, setFeeHandler, setMpcAddress, @@ -17,7 +19,15 @@ const { setAssetMetadata, mintAsset, getUSDCAssetId, - queryBridgePauseStatus + queryBridgePauseStatus, + FeeReserveAccount, + NativeTokenTransferReserveAccount, + OtherTokenTransferReserveAccount, + usdcAssetID, + usdcMinBalance, + usdcName, + usdcSymbol, + usdcDecimal, } = require("./util"); const BN = require('bn.js'); @@ -38,21 +48,12 @@ const supportedDestDomains = [ } ] -// those account are configured in the substrate-node runtime, and are only applicable for sygma pallet standalone node, -// other parachain might have different runtime config so those account address need to be adjusted accordingly -const FeeReserveAccountAddress = "5ELLU7ibt5ZrNEYRwohtaRBDBa3TzcWwwPELBPSWWd2mbgv3"; -const NativeTokenTransferReserveAccount = "5EYCAe5jLbHcAAMKvLFSXgCTbPrLgBJusvPwfKcaKzuf5X5e"; -const OtherTokenTransferReserveAccount = "5EYCAe5jLbHcAAMKvLFiGhk3htXY8jQncbLTDGJQnpnPMAVp"; - async function main() { // asset hub parachain - const assetHubProvider = new WsProvider(process.env.ASSETHUBENDPOINT || 'ws://127.0.0.1:9910'); const assetHubApi = await ApiPromise.create({ provider: assetHubProvider, }); - // bridge hub parachain - const bridgeHubProvider = new WsProvider(process.env.BRIDGEHUBENDPOINT || 'ws://127.0.0.1:8943'); const bridgeHubApi = await ApiPromise.create({ provider: bridgeHubProvider, }); @@ -62,13 +63,8 @@ async function main() { const keyring = new Keyring({type: 'sr25519'}); const sudo = keyring.addFromUri('//Alice'); - // UsdcAssetId: AssetId defined in runtime.rs - const usdcAssetID = 2000; + // USDC token admin const usdcAdmin = sudo.address; - const usdcMinBalance = 100; - const usdcName = "USDC test asset"; - const usdcSymbol = "USDC"; - const usdcDecimal = 12; // create USDC test asset (foreign asset) on asset hub await createAsset(assetHubApi, usdcAssetID, usdcAdmin, usdcMinBalance, true, sudo); @@ -105,10 +101,15 @@ async function main() { } // transfer some native asset to FeeReserveAccount and TransferReserveAccount as Existential Deposit(aka ED) on bridge hub - await transferBalance(bridgeHubApi, FeeReserveAccountAddress, bn1e12.mul(new BN(10000)), true, sudo); // set balance to 10000 native asset + await transferBalance(bridgeHubApi, FeeReserveAccount, bn1e12.mul(new BN(10000)), true, sudo); // set balance to 10000 native asset await transferBalance(bridgeHubApi, NativeTokenTransferReserveAccount, bn1e12.mul(new BN(10000)), true, sudo); // set balance to 10000 native asset reserved account await transferBalance(bridgeHubApi, OtherTokenTransferReserveAccount, bn1e12.mul(new BN(10000)), true, sudo); // set balance to 10000 other asset reserved account + // mint 1 USDC to reserve and fee acount so that in the testcase they will not have null as balance + await mintAsset(bridgeHubApi, usdcAssetID, OtherTokenTransferReserveAccount, bn1e12.mul(new BN(1)), true, sudo); // mint 1 USDC to OtherTokenTransferReserveAccount + await mintAsset(bridgeHubApi, usdcAssetID, FeeReserveAccount, bn1e12.mul(new BN(1)), true, sudo); // mint 1 USDC to FeeReserveAccount + + // set up MPC address(will also unpause all registered domains) on bridge hub if (mpcAddr) { console.log(`set up mpc address: ${mpcAddr}`); diff --git a/scripts/xcm/util.js b/scripts/xcm/util.js index 55bc8a6..d595f6f 100644 --- a/scripts/xcm/util.js +++ b/scripts/xcm/util.js @@ -1,8 +1,27 @@ // The Licensed Work is (c) 2022 Sygma // SPDX-License-Identifier: LGPL-3.0-only +const {WsProvider} = require("@polkadot/api"); require('dotenv').config(); +// those account are configured in the substrate-node runtime, and are only applicable for sygma pallet standalone node, +// other parachain might have different runtime config so those account address need to be adjusted accordingly +const FeeReserveAccount = "5ELLU7ibt5ZrNEYRwohtaRBDBa3TzcWwwPELBPSWWd2mbgv3"; +const NativeTokenTransferReserveAccount = "5EYCAe5jLbHcAAMKvLFSXgCTbPrLgBJusvPwfKcaKzuf5X5e"; +const OtherTokenTransferReserveAccount = "5EYCAe5jLbHcAAMKvLFiGhk3htXY8jQncbLTDGJQnpnPMAVp"; + +// UsdcAssetId: AssetId defined in runtime.rs +const usdcAssetID = 2000; +const usdcMinBalance = 100; +const usdcName = "USDC test asset"; +const usdcSymbol = "USDC"; +const usdcDecimal = 12; + +// asset hub parachain +const assetHubProvider = new WsProvider(process.env.ASSETHUBENDPOINT || 'ws://127.0.0.1:9910'); +// bridge hub parachain +const bridgeHubProvider = new WsProvider(process.env.BRIDGEHUBENDPOINT || 'ws://127.0.0.1:8943'); + async function transferBalance(api, who, value, finalization, sudo) { return new Promise(async (resolve, reject) => { const nonce = Number((await api.query.system.account(sudo.address)).nonce); @@ -482,6 +501,8 @@ async function queryMPCAddress(api) { } module.exports = { + assetHubProvider, + bridgeHubProvider, getNativeAssetId, getNativeMultiAsset, getAssetDepositDest, @@ -502,4 +523,12 @@ module.exports = { queryAssetBalance, queryBalance, queryMPCAddress, + FeeReserveAccount, + NativeTokenTransferReserveAccount, + OtherTokenTransferReserveAccount, + usdcAssetID, + usdcMinBalance, + usdcName, + usdcSymbol, + usdcDecimal, } From 902623c2e4318d0dbbe75176e2959bb3f48c84ed Mon Sep 17 00:00:00 2001 From: Freddy Li Date: Wed, 20 Mar 2024 12:39:04 -0400 Subject: [PATCH 3/9] add hrmp channel setup --- scripts/xcm/e2e_tests.js | 44 +++++++++++++- scripts/xcm/setup.js | 55 ++++++++++++++++- scripts/xcm/util.js | 125 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 216 insertions(+), 8 deletions(-) diff --git a/scripts/xcm/e2e_tests.js b/scripts/xcm/e2e_tests.js index 5532a09..d4c0f68 100644 --- a/scripts/xcm/e2e_tests.js +++ b/scripts/xcm/e2e_tests.js @@ -11,7 +11,7 @@ const { getNativeMultiAsset, getUSDCMultiAsset, getAssetDepositDest, - depositLocal, + deposit, queryBalance, queryAssetBalance, FeeReserveAccount, @@ -43,6 +43,7 @@ async function main() { // run testcases await testcase1(bridgeHubApi, sudo, failedTestcases); await testcase2(bridgeHubApi, sudo, failedTestcases); + // await testcase3(assetHubApi, sudo, failedTestcases); // checking if any testcase failed for (const item of failedTestcases) { @@ -64,7 +65,7 @@ async function testcase1(bridgeHubApi, sudo, failedTestcases) { console.log('fee account native asset balance before: ', nativeBalanceBeforeFeeAccount.data.free); const depositAmount = 10000000000000; - await depositLocal(bridgeHubApi, getNativeMultiAsset(bridgeHubApi, depositAmount), getAssetDepositDest(bridgeHubApi), true, sudo) + await deposit(bridgeHubApi, getNativeMultiAsset(bridgeHubApi, depositAmount), getAssetDepositDest(bridgeHubApi), true, sudo) const nativeBalanceAfterAlice = await queryBalance(bridgeHubApi, sudo.address); console.log('Alice native asset balance after: ', nativeBalanceAfterAlice.data.free); @@ -101,7 +102,7 @@ async function testcase2(bridgeHubApi, sudo, failedTestcases) { console.log('FeeReserveAccountAddress USDC asset balance before: ', usdcBalanceBeforeFeeReserveAccount.balance); const usdcDepositAmount = 10000000000000; - await depositLocal(bridgeHubApi, getUSDCMultiAsset(bridgeHubApi, usdcDepositAmount), getAssetDepositDest(bridgeHubApi), true, sudo) + await deposit(bridgeHubApi, getUSDCMultiAsset(bridgeHubApi, usdcDepositAmount), getAssetDepositDest(bridgeHubApi), true, sudo) const usdcBalanceAfterAlice = await queryAssetBalance(bridgeHubApi, usdcAssetID, sudo.address); console.log('Alice USDC asset balance after: ', usdcBalanceAfterAlice.balance); @@ -126,5 +127,42 @@ async function testcase2(bridgeHubApi, sudo, failedTestcases) { } } +// testcase 3: Native token deposit on Asset hub, dest on Bridge hub +async function testcase3(assetHubApi, sudo, failedTestcases) { + // const nativeBalanceBeforeAlice = await queryBalance(bridgeHubApi, sudo.address); + // console.log('Alice native asset balance before: ', nativeBalanceBeforeAlice.data.free); + // + // const nativeBalanceBeforeNativeTokenTransferReserveAccount = await queryBalance(bridgeHubApi, NativeTokenTransferReserveAccount); + // console.log('token reserve account native asset balance before: ', nativeBalanceBeforeNativeTokenTransferReserveAccount.data.free); + // + // const nativeBalanceBeforeFeeAccount = await queryBalance(bridgeHubApi, FeeReserveAccount); + // console.log('fee account native asset balance before: ', nativeBalanceBeforeFeeAccount.data.free); + + const depositAmount = 10000000000000; + await deposit(assetHubApi, getNativeMultiAsset(assetHubApi, depositAmount), getAssetDepositDest(assetHubApi), true, sudo) + + // const nativeBalanceAfterAlice = await queryBalance(bridgeHubApi, sudo.address); + // console.log('Alice native asset balance after: ', nativeBalanceAfterAlice.data.free); + // + // const nativeBalanceAfterNativeTokenTransferReserveAccount = await queryBalance(bridgeHubApi, NativeTokenTransferReserveAccount); + // console.log('token reserve account native asset balance after: ', nativeBalanceAfterNativeTokenTransferReserveAccount.data.free); + // + // const nativeBalanceAfterFeeAccount = await queryBalance(bridgeHubApi, FeeReserveAccount); + // console.log('fee account native asset balance after: ', nativeBalanceAfterFeeAccount.data.free); + // + // // Alice balance should be deducted by 10 + tx fee, so the before - after should be greater than 10 tokens + // if (nativeBalanceBeforeAlice.data.free - nativeBalanceAfterAlice.data.free <= depositAmount) { + // failedTestcases.push("testcase 1 failed: Alice balance not match") + // } + // // balance reserve account should add deposit amount - fee which is 9,500,000,000,000 + // if (nativeBalanceBeforeNativeTokenTransferReserveAccount.data.free - nativeBalanceAfterNativeTokenTransferReserveAccount.data.free === 9500000000000) { + // failedTestcases.push("testcase 1 failed: NativeTokenTransferReserveAccount balance not match") + // } + // // fee account should add 9,500,000,000,000 + // if (nativeBalanceAfterFeeAccount.data.free - nativeBalanceBeforeFeeAccount.data.free === 9500000000000) { + // failedTestcases.push("testcase 1 failed: FeeAccount balance not match") + // } +} + main().catch(console.error).finally(() => process.exit()); diff --git a/scripts/xcm/setup.js b/scripts/xcm/setup.js index 5aac619..3dd3e94 100644 --- a/scripts/xcm/setup.js +++ b/scripts/xcm/setup.js @@ -6,6 +6,7 @@ require('dotenv').config(); const {ApiPromise, WsProvider, Keyring} = require('@polkadot/api'); const {cryptoWaitReady} = require('@polkadot/util-crypto'); const { + relayChainProvider, assetHubProvider, bridgeHubProvider, transferBalance, @@ -23,6 +24,10 @@ const { FeeReserveAccount, NativeTokenTransferReserveAccount, OtherTokenTransferReserveAccount, + hrmpChannelOpenRequest, + getHRMPChannelMessage, + getHRMPChannelDest, + delay, usdcAssetID, usdcMinBalance, usdcName, @@ -49,6 +54,10 @@ const supportedDestDomains = [ ] async function main() { + // relay chain + const relayChainApi = await ApiPromise.create({ + provider: relayChainProvider, + }); // asset hub parachain const assetHubApi = await ApiPromise.create({ provider: assetHubProvider, @@ -63,6 +72,23 @@ async function main() { const keyring = new Keyring({type: 'sr25519'}); const sudo = keyring.addFromUri('//Alice'); + // sovereignaccount of parachain 1000 on relaychain: + const sovereignAccount1000 = "5Ec4AhPZk8STuex8Wsi9TwDtJQxKqzPJRCH7348Xtcs9vZLJ"; + // sovereignaccount of parachain 1013 on relaychain: + const sovereignAccount1013 = "5Ec4AhPcMD9pfD1dC3vbyKXoZdZjigWthS9nEwGqaSJksLJv"; + // transfer native asset to parachain sovereignaccount on relay chain + await transferBalance(relayChainApi, sovereignAccount1000, bn1e12.mul(new BN(100)), true, sudo); // set balance to 100 native asset + await transferBalance(relayChainApi, sovereignAccount1013, bn1e12.mul(new BN(100)), true, sudo); // set balance to 100 native asset + + // checking if parachains start to producing blocks + let currentBlockNumber = 0; + while (currentBlockNumber < 1) { + const signedBlock = await assetHubApi.rpc.chain.getBlock(); + const blockNumber = signedBlock.block.header.number.toHuman(); + await delay(1000); + currentBlockNumber = blockNumber + } + // USDC token admin const usdcAdmin = sudo.address; @@ -97,7 +123,7 @@ async function main() { // set fee for tokens with domains on bridge hub for (const domain of supportedDestDomains) { await setFeeHandler(bridgeHubApi, domain.domainID, getUSDCAssetId(bridgeHubApi), feeHandlerType.PercentageFeeHandler, true, sudo) - await setFeeRate(bridgeHubApi, domain.domainID, getUSDCAssetId(bridgeHubApi), percentageFeeRate, feeRateLowerBound, feeRateUpperBound,true, sudo); + await setFeeRate(bridgeHubApi, domain.domainID, getUSDCAssetId(bridgeHubApi), percentageFeeRate, feeRateLowerBound, feeRateUpperBound, true, sudo); } // transfer some native asset to FeeReserveAccount and TransferReserveAccount as Existential Deposit(aka ED) on bridge hub @@ -105,11 +131,10 @@ async function main() { await transferBalance(bridgeHubApi, NativeTokenTransferReserveAccount, bn1e12.mul(new BN(10000)), true, sudo); // set balance to 10000 native asset reserved account await transferBalance(bridgeHubApi, OtherTokenTransferReserveAccount, bn1e12.mul(new BN(10000)), true, sudo); // set balance to 10000 other asset reserved account - // mint 1 USDC to reserve and fee acount so that in the testcase they will not have null as balance + // mint 1 USDC to reserve and fee account so that in the testcase they will not have null as balance await mintAsset(bridgeHubApi, usdcAssetID, OtherTokenTransferReserveAccount, bn1e12.mul(new BN(1)), true, sudo); // mint 1 USDC to OtherTokenTransferReserveAccount await mintAsset(bridgeHubApi, usdcAssetID, FeeReserveAccount, bn1e12.mul(new BN(1)), true, sudo); // mint 1 USDC to FeeReserveAccount - // set up MPC address(will also unpause all registered domains) on bridge hub if (mpcAddr) { console.log(`set up mpc address: ${mpcAddr}`); @@ -121,6 +146,30 @@ async function main() { } console.log('πŸš€ Sygma substrate pallet setup is done! πŸš€'); + + // setup HRMP channel between two parachains + // init HRMP channel open request from 1000 to 1013 + const openHRMPChannelRequestEncodedData1000To1013 = "0x3c00f50300000800000000001000"; + await hrmpChannelOpenRequest(assetHubApi, getHRMPChannelDest(assetHubApi), getHRMPChannelMessage(assetHubApi, openHRMPChannelRequestEncodedData1000To1013, 1000), 1000, 1013, true, sudo); + console.log("wait processing on the relay chain...") + await delay(15000); + // accept HRMP channel open request on 1013 + const acceptHRMPChannelRequestEncodedData1000To1013 = "0x3c01e8030000"; + await hrmpChannelOpenRequest(bridgeHubApi, getHRMPChannelDest(bridgeHubApi), getHRMPChannelMessage(bridgeHubApi, acceptHRMPChannelRequestEncodedData1000To1013, 1013), 1000, 1013, true, sudo); + + await delay(15000); + + // init HRMP channel open request from 1013 to 1000 + const openHRMPChannelRequestEncodedData1013To1000 = "0x3c00e80300000800000000001000"; + await hrmpChannelOpenRequest(bridgeHubApi, getHRMPChannelDest(bridgeHubApi), getHRMPChannelMessage(bridgeHubApi, openHRMPChannelRequestEncodedData1013To1000, 1013), 1013, 1000, true, sudo); + console.log("wait processing on the relay chain...") + await delay(15000); + // accept HRMP channel open request on 1000 + const acceptHRMPChannelRequestEncodedData1013To1000 = "0x3c01f5030000"; + await hrmpChannelOpenRequest(assetHubApi, getHRMPChannelDest(assetHubApi), getHRMPChannelMessage(assetHubApi, acceptHRMPChannelRequestEncodedData1013To1000, 1000), 1013, 1000, true, sudo); + + console.log('πŸš€ HRMP Channel setup is done! πŸš€'); } + main().catch(console.error).finally(() => process.exit()); diff --git a/scripts/xcm/util.js b/scripts/xcm/util.js index d595f6f..87a4c59 100644 --- a/scripts/xcm/util.js +++ b/scripts/xcm/util.js @@ -17,6 +17,8 @@ const usdcName = "USDC test asset"; const usdcSymbol = "USDC"; const usdcDecimal = 12; +// relay chain +const relayChainProvider = new WsProvider(process.env.RELAYCHAINENDPOINT || 'ws://127.0.0.1:9942'); // asset hub parachain const assetHubProvider = new WsProvider(process.env.ASSETHUBENDPOINT || 'ws://127.0.0.1:9910'); // bridge hub parachain @@ -451,7 +453,7 @@ async function executeProposal(api, proposalList, signature, finalization, sudo) }); } -async function depositLocal(api, asset, dest, finalization, sudo) { +async function deposit(api, asset, dest, finalization, sudo) { return new Promise(async (resolve, reject) => { const nonce = Number((await api.query.system.account(sudo.address)).nonce); @@ -485,6 +487,116 @@ async function depositLocal(api, asset, dest, finalization, sudo) { }); } +function getHRMPChannelDest(api) { + return api.createType('StagingXcmVersionedMultiLocation', { + V2: api.createType('StagingXcmV2MultiLocation', { + parents: 1, + interior: api.createType('StagingXcmV3Junctions', 'Here') + }) + }) +} + +function getHRMPChannelMessage(api, encodedData, fromParaID) { + return api.createType('StagingXcmVersionedXcm', { + V2: api.createType('StagingXcmV2Xcm', [ + api.createType('StagingXcmV2Instruction', { + WithdrawAsset: [ + api.createType('StagingXcmV2MultiAsset', { + id: api.createType('StagingXcmV2MultiassetAssetId', { + Concrete: api.createType('StagingXcmV2MultiLocation', { + parents: 0, + interior: api.createType('StagingXcmV2MultilocationJunctions', 'Here') + }), + }), + fun: api.createType('StagingXcmV2MultiassetFungibility', { + Fungible: api.createType('Compact', 1000000000000) + }) + }) + ] + }), + api.createType('StagingXcmV2Instruction', { + BuyExecution: { + fees: api.createType('StagingXcmV2MultiAsset', { + id: api.createType('StagingXcmV2MultiassetAssetId', { + Concrete: api.createType('StagingXcmV2MultiLocation', { + parents: 0, + interior: api.createType('StagingXcmV2MultilocationJunctions', 'Here') + }), + }), + fun: api.createType('StagingXcmV2MultiassetFungibility', { + Fungible: api.createType('Compact', 1000000000000) + }) + }), + weightLimit: api.createType('StagingXcmV2WeightLimit', "Unlimited") + }, + }), + api.createType('StagingXcmV2Instruction', { + Transact: { + originType: api.createType('StagingXcmV2OriginKind', "Native"), + requireWeightAtMost: api.createType('Compact', 4000000000), + call: api.createType('StagingXcmDoubleEncoded', { + encoded: api.createType('Bytes', encodedData), + }), + }, + }), + api.createType('StagingXcmV2Instruction', { + RefundSurplus: {}, + }), + api.createType('StagingXcmV2Instruction', { + DepositAsset: { + assets: api.createType('StagingXcmV2MultiassetMultiAssetFilter', { + Wild: api.createType('StagingXcmV2MultiassetWildMultiAsset', "All") + }), + maxAssets: api.createType('Compact', 1), + beneficiary: api.createType('StagingXcmV2MultiLocation', { + parents: 0, + interior: api.createType('StagingXcmV2MultilocationJunctions', { + X1: api.createType('StagingXcmV2Junction', { + Parachain: api.createType('Compact', fromParaID) + }) + }) + }) + } + }) + ]) + }) +} + +async function hrmpChannelOpenRequest(api, dest, message, fromParachainID, toParachainID, finalization, sudo) { + return new Promise(async (resolve, reject) => { + const nonce = Number((await api.query.system.account(sudo.address)).nonce); + + console.log( + `--- Submitting HRMP channel open request from ${fromParachainID} to ${toParachainID}. (nonce: ${nonce}) ---` + ); + const unsub = await api.tx.sudo + .sudo(api.tx.polkadotXcm.send(dest, message)) + .signAndSend(sudo, {nonce: nonce, era: 0}, (result) => { + console.log(`Current status is ${result.status}`); + if (result.status.isInBlock) { + console.log( + `Transaction included at blockHash ${result.status.asInBlock}` + ); + if (finalization) { + console.log('Waiting for finalization...'); + } else { + unsub(); + resolve(); + } + } else if (result.status.isFinalized) { + console.log( + `Transaction finalized at blockHash ${result.status.asFinalized}` + ); + unsub(); + resolve(); + } else if (result.isError) { + console.log(`Transaction Error`); + reject(`Transaction Error`); + } + }); + }); +} + async function queryAssetBalance(api, assetID, account) { let result = await api.query.assets.account(assetID, account); return result.toHuman() @@ -500,7 +612,12 @@ async function queryMPCAddress(api) { return result.toJSON() } +function delay(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + module.exports = { + relayChainProvider, assetHubProvider, bridgeHubProvider, getNativeAssetId, @@ -508,7 +625,7 @@ module.exports = { getAssetDepositDest, getUSDCAssetId, getUSDCMultiAsset, - depositLocal, + deposit, registerDomain, mintAsset, setAssetMetadata, @@ -523,6 +640,10 @@ module.exports = { queryAssetBalance, queryBalance, queryMPCAddress, + hrmpChannelOpenRequest, + getHRMPChannelMessage, + getHRMPChannelDest, + delay, FeeReserveAccount, NativeTokenTransferReserveAccount, OtherTokenTransferReserveAccount, From f3b884adaa82f32e2afee6d1afeb4e154faf3410 Mon Sep 17 00:00:00 2001 From: Freddy Li Date: Thu, 28 Mar 2024 13:18:14 -0400 Subject: [PATCH 4/9] add hrmp and relaychain setup --- scripts/xcm/e2e_tests.js | 2 +- scripts/xcm/setup.js | 30 ++++++++++++++++++++++++------ scripts/xcm/util.js | 4 ++-- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/scripts/xcm/e2e_tests.js b/scripts/xcm/e2e_tests.js index d4c0f68..32d8a24 100644 --- a/scripts/xcm/e2e_tests.js +++ b/scripts/xcm/e2e_tests.js @@ -127,7 +127,7 @@ async function testcase2(bridgeHubApi, sudo, failedTestcases) { } } -// testcase 3: Native token deposit on Asset hub, dest on Bridge hub +// testcase 3: Foreign token(USDC) deposit on Asset hub, dest on Bridge hub async function testcase3(assetHubApi, sudo, failedTestcases) { // const nativeBalanceBeforeAlice = await queryBalance(bridgeHubApi, sudo.address); // console.log('Alice native asset balance before: ', nativeBalanceBeforeAlice.data.free); diff --git a/scripts/xcm/setup.js b/scripts/xcm/setup.js index 3dd3e94..5449751 100644 --- a/scripts/xcm/setup.js +++ b/scripts/xcm/setup.js @@ -24,7 +24,7 @@ const { FeeReserveAccount, NativeTokenTransferReserveAccount, OtherTokenTransferReserveAccount, - hrmpChannelOpenRequest, + hrmpChannelRequest, getHRMPChannelMessage, getHRMPChannelDest, delay, @@ -72,6 +72,9 @@ async function main() { const keyring = new Keyring({type: 'sr25519'}); const sudo = keyring.addFromUri('//Alice'); + console.log('======= Relaychain setup begin ======='); + + // relaychain setup // sovereignaccount of parachain 1000 on relaychain: const sovereignAccount1000 = "5Ec4AhPZk8STuex8Wsi9TwDtJQxKqzPJRCH7348Xtcs9vZLJ"; // sovereignaccount of parachain 1013 on relaychain: @@ -80,7 +83,10 @@ async function main() { await transferBalance(relayChainApi, sovereignAccount1000, bn1e12.mul(new BN(100)), true, sudo); // set balance to 100 native asset await transferBalance(relayChainApi, sovereignAccount1013, bn1e12.mul(new BN(100)), true, sudo); // set balance to 100 native asset + console.log('======= Relaychain setup is done ======='); + // checking if parachains start to producing blocks + console.log("wait for the parachain stars producting blocks...") let currentBlockNumber = 0; while (currentBlockNumber < 1) { const signedBlock = await assetHubApi.rpc.chain.getBlock(); @@ -89,6 +95,7 @@ async function main() { currentBlockNumber = blockNumber } + console.log('======= Parchain setup begin ======='); // USDC token admin const usdcAdmin = sudo.address; @@ -131,6 +138,7 @@ async function main() { await transferBalance(bridgeHubApi, NativeTokenTransferReserveAccount, bn1e12.mul(new BN(10000)), true, sudo); // set balance to 10000 native asset reserved account await transferBalance(bridgeHubApi, OtherTokenTransferReserveAccount, bn1e12.mul(new BN(10000)), true, sudo); // set balance to 10000 other asset reserved account + // TODO: create another token on bridge hub for testcase 1 and 2, USDC will be used for xcm transfer testcase // mint 1 USDC to reserve and fee account so that in the testcase they will not have null as balance await mintAsset(bridgeHubApi, usdcAssetID, OtherTokenTransferReserveAccount, bn1e12.mul(new BN(1)), true, sudo); // mint 1 USDC to OtherTokenTransferReserveAccount await mintAsset(bridgeHubApi, usdcAssetID, FeeReserveAccount, bn1e12.mul(new BN(1)), true, sudo); // mint 1 USDC to FeeReserveAccount @@ -144,29 +152,39 @@ async function main() { if (!await queryBridgePauseStatus(bridgeHubApi, domain.domainID)) console.log(`DestDomainID: ${domain.domainID} is readyβœ…`); } } - console.log('πŸš€ Sygma substrate pallet setup is done! πŸš€'); + // transfer native asset to parachain sovereignaccount on each parachain + await transferBalance(assetHubApi, sovereignAccount1013, bn1e12.mul(new BN(1)), true, sudo); // set balance to 1 native asset + await transferBalance(bridgeHubApi, sovereignAccount1000, bn1e12.mul(new BN(1)), true, sudo); // set balance to 1 native asset + console.log('======= Parachain setup is done ======='); + + console.log('======= HRMP channel setup begin ======='); // setup HRMP channel between two parachains // init HRMP channel open request from 1000 to 1013 const openHRMPChannelRequestEncodedData1000To1013 = "0x3c00f50300000800000000001000"; - await hrmpChannelOpenRequest(assetHubApi, getHRMPChannelDest(assetHubApi), getHRMPChannelMessage(assetHubApi, openHRMPChannelRequestEncodedData1000To1013, 1000), 1000, 1013, true, sudo); + await hrmpChannelRequest(assetHubApi, getHRMPChannelDest(assetHubApi), getHRMPChannelMessage(assetHubApi, openHRMPChannelRequestEncodedData1000To1013, 1000), 1000, 1013, true, sudo); console.log("wait processing on the relay chain...") await delay(15000); // accept HRMP channel open request on 1013 const acceptHRMPChannelRequestEncodedData1000To1013 = "0x3c01e8030000"; - await hrmpChannelOpenRequest(bridgeHubApi, getHRMPChannelDest(bridgeHubApi), getHRMPChannelMessage(bridgeHubApi, acceptHRMPChannelRequestEncodedData1000To1013, 1013), 1000, 1013, true, sudo); + await hrmpChannelRequest(bridgeHubApi, getHRMPChannelDest(bridgeHubApi), getHRMPChannelMessage(bridgeHubApi, acceptHRMPChannelRequestEncodedData1000To1013, 1013), 1000, 1013, true, sudo); await delay(15000); // init HRMP channel open request from 1013 to 1000 const openHRMPChannelRequestEncodedData1013To1000 = "0x3c00e80300000800000000001000"; - await hrmpChannelOpenRequest(bridgeHubApi, getHRMPChannelDest(bridgeHubApi), getHRMPChannelMessage(bridgeHubApi, openHRMPChannelRequestEncodedData1013To1000, 1013), 1013, 1000, true, sudo); + await hrmpChannelRequest(bridgeHubApi, getHRMPChannelDest(bridgeHubApi), getHRMPChannelMessage(bridgeHubApi, openHRMPChannelRequestEncodedData1013To1000, 1013), 1013, 1000, true, sudo); console.log("wait processing on the relay chain...") await delay(15000); // accept HRMP channel open request on 1000 const acceptHRMPChannelRequestEncodedData1013To1000 = "0x3c01f5030000"; - await hrmpChannelOpenRequest(assetHubApi, getHRMPChannelDest(assetHubApi), getHRMPChannelMessage(assetHubApi, acceptHRMPChannelRequestEncodedData1013To1000, 1000), 1013, 1000, true, sudo); + await hrmpChannelRequest(assetHubApi, getHRMPChannelDest(assetHubApi), getHRMPChannelMessage(assetHubApi, acceptHRMPChannelRequestEncodedData1013To1000, 1000), 1013, 1000, true, sudo); + + // transfer native asset to FungiblesTransactor CheckingAccount on both parachains + const CheckingAccount = "5EYCAe5ijiYgWYWi1fs8Xz1td1djEtJVVnNfzvDRP4VtLL7Y"; + await transferBalance(assetHubApi, CheckingAccount, bn1e12.mul(new BN(1)), true, sudo); // set balance to 1 native asset + await transferBalance(bridgeHubApi, CheckingAccount, bn1e12.mul(new BN(1)), true, sudo); // set balance to 1 native asset console.log('πŸš€ HRMP Channel setup is done! πŸš€'); } diff --git a/scripts/xcm/util.js b/scripts/xcm/util.js index 87a4c59..02f69da 100644 --- a/scripts/xcm/util.js +++ b/scripts/xcm/util.js @@ -562,7 +562,7 @@ function getHRMPChannelMessage(api, encodedData, fromParaID) { }) } -async function hrmpChannelOpenRequest(api, dest, message, fromParachainID, toParachainID, finalization, sudo) { +async function hrmpChannelRequest(api, dest, message, fromParachainID, toParachainID, finalization, sudo) { return new Promise(async (resolve, reject) => { const nonce = Number((await api.query.system.account(sudo.address)).nonce); @@ -640,7 +640,7 @@ module.exports = { queryAssetBalance, queryBalance, queryMPCAddress, - hrmpChannelOpenRequest, + hrmpChannelRequest, getHRMPChannelMessage, getHRMPChannelDest, delay, From 259e1473799ecbbe4c1287945472f65c36032b26 Mon Sep 17 00:00:00 2001 From: Freddy Li Date: Tue, 2 Apr 2024 12:13:39 -0400 Subject: [PATCH 5/9] add testcases --- scripts/xcm/e2e_tests.js | 298 +++++++++++++++++++++++++++-------- scripts/xcm/setup.js | 88 ++++++++--- scripts/xcm/util.js | 326 +++++++++++++++++++++++++++++++++------ 3 files changed, 585 insertions(+), 127 deletions(-) diff --git a/scripts/xcm/e2e_tests.js b/scripts/xcm/e2e_tests.js index 32d8a24..980dc8d 100644 --- a/scripts/xcm/e2e_tests.js +++ b/scripts/xcm/e2e_tests.js @@ -10,8 +10,19 @@ const { bridgeHubProvider, getNativeMultiAsset, getUSDCMultiAsset, + getUSDCMultiAssetX2, + getUSDCAssetIdX2, + getTTTMultiAsset, + getAHNMultiAsset, getAssetDepositDest, + teleportTokenViaXCM, + getAssetHubTeleportDest, + getAssetHubTeleportBeneficiary, + getAssetHubTeleportBeneficiaryToSygma, + getAssetHubTeleportAsset, + getAssetHubTeleportWeightLimit, deposit, + subEvents, queryBalance, queryAssetBalance, FeeReserveAccount, @@ -22,6 +33,12 @@ const { usdcName, usdcSymbol, usdcDecimal, + ahnAssetID, + tttAssetID, + tttMinBalance, + tttName, + tttSymbol, + tttDecimal, } = require("./util"); async function main() { @@ -43,16 +60,23 @@ async function main() { // run testcases await testcase1(bridgeHubApi, sudo, failedTestcases); await testcase2(bridgeHubApi, sudo, failedTestcases); - // await testcase3(assetHubApi, sudo, failedTestcases); + await testcase3(assetHubApi, bridgeHubApi, sudo, failedTestcases); + await testcase4(assetHubApi, bridgeHubApi, sudo, failedTestcases); + await testcase5(assetHubApi, bridgeHubApi, sudo, failedTestcases); + await testcase6(assetHubApi, bridgeHubApi, sudo, failedTestcases); // checking if any testcase failed for (const item of failedTestcases) { - console.error('\x1b[31m%s\x1b[0m', item); + console.error('\x1b[31m%s\x1b[0m\n', item); return } console.log('\x1b[32m%s\x1b[0m', "All testcases pass"); } +function str2BigInt(a) { + return BigInt(a.replaceAll(',', '')); +} + // testcase 1: Native token deposit on Bridge hub, dest on relayer async function testcase1(bridgeHubApi, sudo, failedTestcases) { const nativeBalanceBeforeAlice = await queryBalance(bridgeHubApi, sudo.address); @@ -64,7 +88,7 @@ async function testcase1(bridgeHubApi, sudo, failedTestcases) { const nativeBalanceBeforeFeeAccount = await queryBalance(bridgeHubApi, FeeReserveAccount); console.log('fee account native asset balance before: ', nativeBalanceBeforeFeeAccount.data.free); - const depositAmount = 10000000000000; + const depositAmount = 10000000000000; // 10 tokens await deposit(bridgeHubApi, getNativeMultiAsset(bridgeHubApi, depositAmount), getAssetDepositDest(bridgeHubApi), true, sudo) const nativeBalanceAfterAlice = await queryBalance(bridgeHubApi, sudo.address); @@ -77,92 +101,238 @@ async function testcase1(bridgeHubApi, sudo, failedTestcases) { console.log('fee account native asset balance after: ', nativeBalanceAfterFeeAccount.data.free); // Alice balance should be deducted by 10 + tx fee, so the before - after should be greater than 10 tokens - if (nativeBalanceBeforeAlice.data.free - nativeBalanceAfterAlice.data.free <= depositAmount) { + if (str2BigInt(nativeBalanceBeforeAlice.data.free) - str2BigInt(nativeBalanceAfterAlice.data.free) <= BigInt(depositAmount)) { failedTestcases.push("testcase 1 failed: Alice balance not match") } // balance reserve account should add deposit amount - fee which is 9,500,000,000,000 - if (nativeBalanceBeforeNativeTokenTransferReserveAccount.data.free - nativeBalanceAfterNativeTokenTransferReserveAccount.data.free === 9500000000000) { + if (str2BigInt(nativeBalanceAfterNativeTokenTransferReserveAccount.data.free) - str2BigInt(nativeBalanceBeforeNativeTokenTransferReserveAccount.data.free) !== BigInt(9500000000000)) { failedTestcases.push("testcase 1 failed: NativeTokenTransferReserveAccount balance not match") } - // fee account should add 9,500,000,000,000 - if (nativeBalanceAfterFeeAccount.data.free - nativeBalanceBeforeFeeAccount.data.free === 9500000000000) { + // fee account should add 500,000,000,000 + if (str2BigInt(nativeBalanceAfterFeeAccount.data.free) - str2BigInt(nativeBalanceBeforeFeeAccount.data.free) !== BigInt(500000000000)) { failedTestcases.push("testcase 1 failed: FeeAccount balance not match") } } -// testcase 2: Foreign token deposit on Bridge hub, dest on relayer +// testcase 2: Foreign token TTT deposit on Bridge hub, dest on relayer async function testcase2(bridgeHubApi, sudo, failedTestcases) { - const usdcBalanceBeforeAlice = await queryAssetBalance(bridgeHubApi, usdcAssetID, sudo.address); - console.log('Alice USDC asset balance before: ', usdcBalanceBeforeAlice.balance); + let tttBalanceBeforeAlice = await queryAssetBalance(bridgeHubApi, tttAssetID, sudo.address); + console.log('Alice TTT asset balance before: ', tttBalanceBeforeAlice.balance); - const usdcBalanceBeforeOtherTokenTransferReserveAccount = await queryAssetBalance(bridgeHubApi, usdcAssetID, OtherTokenTransferReserveAccount); - console.log('OtherTokenTransferReserveAccount USDC asset balance before: ', usdcBalanceBeforeOtherTokenTransferReserveAccount.balance); + const tttBalanceBeforeOtherTokenTransferReserveAccount = await queryAssetBalance(bridgeHubApi, tttAssetID, OtherTokenTransferReserveAccount); + console.log('OtherTokenTransferReserveAccount TTT asset balance before: ', tttBalanceBeforeOtherTokenTransferReserveAccount.balance); - const usdcBalanceBeforeFeeReserveAccount = await queryAssetBalance(bridgeHubApi, usdcAssetID, FeeReserveAccount); - console.log('FeeReserveAccountAddress USDC asset balance before: ', usdcBalanceBeforeFeeReserveAccount.balance); + const tttBalanceBeforeFeeReserveAccount = await queryAssetBalance(bridgeHubApi, tttAssetID, FeeReserveAccount); + console.log('FeeReserveAccountAddress TTT asset balance before: ', tttBalanceBeforeFeeReserveAccount.balance); - const usdcDepositAmount = 10000000000000; - await deposit(bridgeHubApi, getUSDCMultiAsset(bridgeHubApi, usdcDepositAmount), getAssetDepositDest(bridgeHubApi), true, sudo) + const tttDepositAmount = 10000000000000; // 10 tokens + await deposit(bridgeHubApi, getTTTMultiAsset(bridgeHubApi, tttDepositAmount), getAssetDepositDest(bridgeHubApi), true, sudo) - const usdcBalanceAfterAlice = await queryAssetBalance(bridgeHubApi, usdcAssetID, sudo.address); - console.log('Alice USDC asset balance after: ', usdcBalanceAfterAlice.balance); + let tttBalanceAfterAlice = await queryAssetBalance(bridgeHubApi, tttAssetID, sudo.address); + console.log('Alice TTT asset balance after: ', tttBalanceAfterAlice.balance); - const usdcBalanceAfterOtherTokenTransferReserveAccount = await queryAssetBalance(bridgeHubApi, usdcAssetID, OtherTokenTransferReserveAccount); - console.log('OtherTokenTransferReserveAccount USDC asset balance after: ', usdcBalanceAfterOtherTokenTransferReserveAccount.balance); + const tttBalanceAfterOtherTokenTransferReserveAccount = await queryAssetBalance(bridgeHubApi, tttAssetID, OtherTokenTransferReserveAccount); + console.log('OtherTokenTransferReserveAccount TTT asset balance after: ', tttBalanceAfterOtherTokenTransferReserveAccount.balance); - const usdcBalanceAfterFeeReserveAccount = await queryAssetBalance(bridgeHubApi, usdcAssetID, FeeReserveAccount); - console.log('FeeReserveAccountAddress USDC asset balance after: ', usdcBalanceAfterFeeReserveAccount.balance); + const tttBalanceAfterFeeReserveAccount = await queryAssetBalance(bridgeHubApi, tttAssetID, FeeReserveAccount); + console.log('FeeReserveAccountAddress TTT asset balance after: ', tttBalanceAfterFeeReserveAccount.balance); - // Alice should be deducted by 10 USDC tokens - if (usdcBalanceBeforeAlice.balance - usdcBalanceAfterAlice.balance === usdcDepositAmount) { - failedTestcases.push("testcase 2 failed: Alice USDC token balance not match") + // Alice should be deducted by 10 TTT tokens + if (str2BigInt(tttBalanceBeforeAlice.balance) - str2BigInt(tttBalanceAfterAlice.balance) !== BigInt(tttDepositAmount)) { + failedTestcases.push("testcase 2 failed: Alice TTT token balance not match") } - // OtherTokenTransferReserveAccount should add deposit amount - fee - if (usdcBalanceBeforeOtherTokenTransferReserveAccount.balance - usdcBalanceAfterOtherTokenTransferReserveAccount.balance === usdcDepositAmount - 500000000000) { - failedTestcases.push("testcase 2 failed: OtherTokenTransferReserveAccount USDC token balance not match") + // OtherTokenTransferReserveAccount should add 0 because TTT is a non-reserve token on Bridge hub + if (str2BigInt(tttBalanceBeforeOtherTokenTransferReserveAccount.balance) !== str2BigInt(tttBalanceAfterOtherTokenTransferReserveAccount.balance)) { + failedTestcases.push("testcase 2 failed: OtherTokenTransferReserveAccount TTT token balance not match") } // FeeReserveAccount should add fee which is 500000000000 - if (usdcBalanceAfterFeeReserveAccount.balance - usdcBalanceBeforeFeeReserveAccount.balance === 500000000000) { - failedTestcases.push("testcase 2 failed: FeeReserveAccount USDC token balance not match") + if (str2BigInt(tttBalanceAfterFeeReserveAccount.balance) - str2BigInt(tttBalanceBeforeFeeReserveAccount.balance) !== BigInt(500000000000)) { + failedTestcases.push("testcase 2 failed: FeeReserveAccount TTT token balance not match") } } // testcase 3: Foreign token(USDC) deposit on Asset hub, dest on Bridge hub -async function testcase3(assetHubApi, sudo, failedTestcases) { - // const nativeBalanceBeforeAlice = await queryBalance(bridgeHubApi, sudo.address); - // console.log('Alice native asset balance before: ', nativeBalanceBeforeAlice.data.free); - // - // const nativeBalanceBeforeNativeTokenTransferReserveAccount = await queryBalance(bridgeHubApi, NativeTokenTransferReserveAccount); - // console.log('token reserve account native asset balance before: ', nativeBalanceBeforeNativeTokenTransferReserveAccount.data.free); - // - // const nativeBalanceBeforeFeeAccount = await queryBalance(bridgeHubApi, FeeReserveAccount); - // console.log('fee account native asset balance before: ', nativeBalanceBeforeFeeAccount.data.free); - - const depositAmount = 10000000000000; - await deposit(assetHubApi, getNativeMultiAsset(assetHubApi, depositAmount), getAssetDepositDest(assetHubApi), true, sudo) - - // const nativeBalanceAfterAlice = await queryBalance(bridgeHubApi, sudo.address); - // console.log('Alice native asset balance after: ', nativeBalanceAfterAlice.data.free); - // - // const nativeBalanceAfterNativeTokenTransferReserveAccount = await queryBalance(bridgeHubApi, NativeTokenTransferReserveAccount); - // console.log('token reserve account native asset balance after: ', nativeBalanceAfterNativeTokenTransferReserveAccount.data.free); - // - // const nativeBalanceAfterFeeAccount = await queryBalance(bridgeHubApi, FeeReserveAccount); - // console.log('fee account native asset balance after: ', nativeBalanceAfterFeeAccount.data.free); - // - // // Alice balance should be deducted by 10 + tx fee, so the before - after should be greater than 10 tokens - // if (nativeBalanceBeforeAlice.data.free - nativeBalanceAfterAlice.data.free <= depositAmount) { - // failedTestcases.push("testcase 1 failed: Alice balance not match") - // } - // // balance reserve account should add deposit amount - fee which is 9,500,000,000,000 - // if (nativeBalanceBeforeNativeTokenTransferReserveAccount.data.free - nativeBalanceAfterNativeTokenTransferReserveAccount.data.free === 9500000000000) { - // failedTestcases.push("testcase 1 failed: NativeTokenTransferReserveAccount balance not match") - // } - // // fee account should add 9,500,000,000,000 - // if (nativeBalanceAfterFeeAccount.data.free - nativeBalanceBeforeFeeAccount.data.free === 9500000000000) { - // failedTestcases.push("testcase 1 failed: FeeAccount balance not match") - // } +async function testcase3(assetHubApi, bridgeHubApi, sudo, failedTestcases) { + const usdcBalanceBeforeAliceAssethub = await queryAssetBalance(assetHubApi, usdcAssetID, sudo.address); + console.log('Alice USDC asset balance on Asset hub before: ', usdcBalanceBeforeAliceAssethub.balance); + + const usdcBalanceBeforeAliceBridgehub = await queryAssetBalance(bridgeHubApi, usdcAssetID, sudo.address); + console.log('Alice USDC asset balance on Bridge hub before: ', usdcBalanceBeforeAliceBridgehub.balance); + + const depositAmount = 10000000000000; // 10 tokens + const beneficiary = "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d" // Alice + await teleportTokenViaXCM( + assetHubApi, + { + dest: getAssetHubTeleportDest(assetHubApi), + beneficiary: getAssetHubTeleportBeneficiary(assetHubApi, beneficiary), + assets: getAssetHubTeleportAsset(assetHubApi, getUSDCMultiAssetX2(assetHubApi, depositAmount)), + feeAssetItem: 0, + weightLimit: getAssetHubTeleportWeightLimit(assetHubApi), + fromParachainID: 1000, + toParachainID: 1013 + }, + true, sudo) + + const usdcBalanceAfterAliceAssethub = await queryAssetBalance(assetHubApi, usdcAssetID, sudo.address); + console.log('Alice USDC asset balance on Asset hub after: ', usdcBalanceAfterAliceAssethub.balance); + + const usdcBalanceAfterAliceBridgehub = await queryAssetBalance(bridgeHubApi, usdcAssetID, sudo.address); + console.log('Alice USDC asset balance on Bridge hub after: ', usdcBalanceAfterAliceBridgehub.balance); + + // Alice USDC balance should be deducted by 10, so before - after should be equal to 10 tokens on asset hub + if (str2BigInt(usdcBalanceBeforeAliceAssethub.balance) - str2BigInt(usdcBalanceAfterAliceAssethub.balance) !== BigInt(depositAmount)) { + failedTestcases.push("testcase 3 failed: Alice USDC balance on Asset hub not match") + } + + // Alice USDC balance should be added by 10 - tx fee, so after - before should be less than 10 tokens + if (str2BigInt(usdcBalanceAfterAliceBridgehub.balance) - str2BigInt(usdcBalanceBeforeAliceBridgehub.balance) >= BigInt(depositAmount)) { + failedTestcases.push("testcase 3 failed: Alice USDC balance on Bridge hub not match") + } +} + +// testcase 4: Native token deposit on Asset hub, dest on Bridge hub +async function testcase4(assetHubApi, bridgeHubApi, sudo, failedTestcases) { + const beneficiaryAddressOnBridgehub = "5GYrSdyt7wydaQiqsnrvq11neaC2eTUBXCnXhSJKpUPT3hXP"; + const beneficiary = "0xc668b505f6a7012a50dca169757c629651bfd6cefbfc24301dea2d2cc0ab2732" // Alice_extension + + const nativeBalanceBeforeAliceAssethub = await queryBalance(assetHubApi, sudo.address); + console.log('Alice native asset balance on Asset hub before: ', nativeBalanceBeforeAliceAssethub.data.free); + + const nativeBalanceBeforeAliceBridgehub = await queryAssetBalance(bridgeHubApi, ahnAssetID, beneficiaryAddressOnBridgehub); + console.log('Alice assethub\'s native asset balance on Bridge hub before: ', nativeBalanceBeforeAliceBridgehub.balance); + + const depositAmount = 2000000000000; // 2 tokens + await teleportTokenViaXCM( + assetHubApi, + { + dest: getAssetHubTeleportDest(assetHubApi), + beneficiary: getAssetHubTeleportBeneficiary(assetHubApi, beneficiary), + assets: getAssetHubTeleportAsset(assetHubApi, getNativeMultiAsset(assetHubApi, depositAmount)), + feeAssetItem: 0, + weightLimit: getAssetHubTeleportWeightLimit(assetHubApi), + fromParachainID: 1000, + toParachainID: 1013 + }, + true, sudo) + + const nativeBalanceAfterAliceAssethub = await queryBalance(assetHubApi, sudo.address); + console.log('Alice native asset balance on Asset hub after: ', nativeBalanceAfterAliceAssethub.data.free); + + const nativeBalanceAfterAliceBridgehub = await queryAssetBalance(bridgeHubApi, ahnAssetID, beneficiaryAddressOnBridgehub); + console.log('Alice assethub\'s native asset balance on Bridge hub after: ', nativeBalanceAfterAliceBridgehub.balance); + + // Alice native token balance should be deducted by 2 and some tx fee, so before - after should be greater than 2 tokens on asset hub + if (str2BigInt(nativeBalanceBeforeAliceAssethub.data.free) - str2BigInt(nativeBalanceAfterAliceAssethub.data.free) <= BigInt(depositAmount)) { + failedTestcases.push("testcase 4 failed: Alice native asset balance on Asset hub not match") + } + + // Alice native asset token balance should be added by 2 - tx fee on bridge hub, so after - before should be less than 2 tokens + if (str2BigInt(nativeBalanceAfterAliceBridgehub.balance) - str2BigInt(nativeBalanceBeforeAliceBridgehub.balance) >= BigInt(depositAmount)) { + failedTestcases.push("testcase 4 failed: Alice native asset token balance on Bridge hub not match") + } +} + +// testcase 5: Foreign token(USDC) deposit on Asset hub, dest on sygma via Bridge hub +async function testcase5(assetHubApi, bridgeHubApi, sudo, failedTestcases) { + const events = []; + await subEvents(bridgeHubApi, events); + + const usdcBalanceBeforeAliceAssethub = await queryAssetBalance(assetHubApi, usdcAssetID, sudo.address); + console.log('Alice USDC asset balance on Asset hub before: ', usdcBalanceBeforeAliceAssethub.balance); + + const usdcBalanceBeforeOtherTokenTransferReserveAccount = await queryAssetBalance(bridgeHubApi, usdcAssetID, OtherTokenTransferReserveAccount); + console.log('OtherTokenTransferReserveAccount USDC asset balance on Bridge hub before: ', usdcBalanceBeforeOtherTokenTransferReserveAccount.balance); + + const depositAmount = 5000000000000; // 5 tokens + await teleportTokenViaXCM( + assetHubApi, + { + dest: getAssetHubTeleportDest(assetHubApi), + beneficiary: getAssetHubTeleportBeneficiaryToSygma(assetHubApi), // EVM address: 0x1abd6948e422a1b6ced1ba28ba72ca562333df01 + assets: getAssetHubTeleportAsset(assetHubApi, getUSDCMultiAssetX2(assetHubApi, depositAmount)), + feeAssetItem: 0, + weightLimit: getAssetHubTeleportWeightLimit(assetHubApi), + fromParachainID: 1000, + toParachainID: 1013 + }, + true, sudo) + + const usdcBalanceAfterAliceAssethub = await queryAssetBalance(assetHubApi, usdcAssetID, sudo.address); + console.log('Alice USDC asset balance on Asset hub after: ', usdcBalanceAfterAliceAssethub.balance); + + const usdcBalanceAfterOtherTokenTransferReserveAccount = await queryAssetBalance(bridgeHubApi, usdcAssetID, OtherTokenTransferReserveAccount); + console.log('OtherTokenTransferReserveAccount USDC asset balance on Bridge hub after: ', usdcBalanceAfterOtherTokenTransferReserveAccount.balance); + + // Alice USDC balance should be deducted by 5, so before - after should be equal to 10 tokens on asset hub + if (str2BigInt(usdcBalanceBeforeAliceAssethub.balance) - str2BigInt(usdcBalanceAfterAliceAssethub.balance) !== BigInt(depositAmount)) { + failedTestcases.push("testcase 5 failed: Alice USDC balance on Asset hub not match") + } + + // OtherTokenTransferReserveAccount USDC balance should be added by 5 - tx fee, so after - before should be less than 5 tokens + if (str2BigInt(usdcBalanceAfterOtherTokenTransferReserveAccount.balance) - str2BigInt(usdcBalanceBeforeOtherTokenTransferReserveAccount.balance) >= BigInt(depositAmount)) { + failedTestcases.push("testcase 5 failed: OtherTokenTransferReserveAccount USDC balance on Bridge hub not match") + } + + // checking if any sygma events emitted + for (const e of events) { + console.log('sygma pallets event emitted: \x1b[32m%s\x1b[0m\n', e); + } + if (events.length === 0) { + failedTestcases.push("testcase 5 failed: sygma pallets event not emitted"); + } } +// testcase 6: Native token deposit on Asset hub, dest on sygma via Bridge hub +async function testcase6(assetHubApi, bridgeHubApi, sudo, failedTestcases) { + const events = []; + await subEvents(bridgeHubApi, events); + + const nativeBalanceBeforeAliceAssethub = await queryBalance(assetHubApi, sudo.address); + console.log('Alice native asset balance on Asset hub before: ', nativeBalanceBeforeAliceAssethub.data.free); + + const nativeBalanceBeforeOtherTokenTransferReserveAccount = await queryAssetBalance(bridgeHubApi, ahnAssetID, OtherTokenTransferReserveAccount); + console.log('OtherTokenTransferReserveAccount AHN balance on Bridge hub before: ', nativeBalanceBeforeOtherTokenTransferReserveAccount.balance); + + const depositAmount = 2000000000000; // 5 tokens + await teleportTokenViaXCM( + assetHubApi, + { + dest: getAssetHubTeleportDest(assetHubApi), + beneficiary: getAssetHubTeleportBeneficiaryToSygma(assetHubApi), // EVM address: 0x1abd6948e422a1b6ced1ba28ba72ca562333df01 + assets: getAssetHubTeleportAsset(assetHubApi, getNativeMultiAsset(assetHubApi, depositAmount)), + feeAssetItem: 0, + weightLimit: getAssetHubTeleportWeightLimit(assetHubApi), + fromParachainID: 1000, + toParachainID: 1013 + }, + true, sudo) + + const nativeBalanceAfterAliceAssethub = await queryBalance(assetHubApi, sudo.address); + console.log('Alice native asset balance on Asset hub before: ', nativeBalanceAfterAliceAssethub.data.free); + + const nativeBalanceAfterOtherTokenTransferReserveAccount = await queryAssetBalance(bridgeHubApi, ahnAssetID, OtherTokenTransferReserveAccount); + console.log('OtherTokenTransferReserveAccount AHN balance on Bridge hub after: ', nativeBalanceAfterOtherTokenTransferReserveAccount.balance); + + // Alice native token balance should be deducted by 2 and some tx fee, so before - after should be greater than 2 tokens on asset hub + if (str2BigInt(nativeBalanceBeforeAliceAssethub.data.free) - str2BigInt(nativeBalanceAfterAliceAssethub.data.free) <= BigInt(depositAmount)) { + failedTestcases.push("testcase 6 failed: Alice native asset balance on Asset hub not match") + } + + // OtherTokenTransferReserveAccount AHN balance should be added by 2 - tx fee, so after - before should be less than 2 tokens + if (str2BigInt(nativeBalanceAfterOtherTokenTransferReserveAccount.balance) - str2BigInt(nativeBalanceBeforeOtherTokenTransferReserveAccount.balance) >= BigInt(depositAmount)) { + failedTestcases.push("testcase 6 failed: OtherTokenTransferReserveAccount AHN balance on Bridge hub not match") + } + + // checking if any sygma events emitted + for (const e of events) { + console.log('sygma pallets event emitted: \x1b[32m%s\x1b[0m\n', e); + } + if (events.length === 0) { + failedTestcases.push("testcase 6 failed: sygma pallets event not emitted"); + } +} + + main().catch(console.error).finally(() => process.exit()); diff --git a/scripts/xcm/setup.js b/scripts/xcm/setup.js index 5449751..80fa5a9 100644 --- a/scripts/xcm/setup.js +++ b/scripts/xcm/setup.js @@ -20,6 +20,10 @@ const { setAssetMetadata, mintAsset, getUSDCAssetId, + getAHNAssetId, + getAHNMultiAsset, + getTTTMultiAsset, + getTTTAssetId, queryBridgePauseStatus, FeeReserveAccount, NativeTokenTransferReserveAccount, @@ -33,6 +37,16 @@ const { usdcName, usdcSymbol, usdcDecimal, + ahnAssetID, + ahnMinBalance, + ahnName, + ahnSymbol, + ahnDecimal, + tttAssetID, + tttMinBalance, + tttName, + tttSymbol, + tttDecimal, } = require("./util"); const BN = require('bn.js'); @@ -80,13 +94,13 @@ async function main() { // sovereignaccount of parachain 1013 on relaychain: const sovereignAccount1013 = "5Ec4AhPcMD9pfD1dC3vbyKXoZdZjigWthS9nEwGqaSJksLJv"; // transfer native asset to parachain sovereignaccount on relay chain - await transferBalance(relayChainApi, sovereignAccount1000, bn1e12.mul(new BN(100)), true, sudo); // set balance to 100 native asset - await transferBalance(relayChainApi, sovereignAccount1013, bn1e12.mul(new BN(100)), true, sudo); // set balance to 100 native asset + await transferBalance(relayChainApi, sovereignAccount1000, bn1e12.mul(new BN(10)), true, sudo); // set balance to 10 native asset + await transferBalance(relayChainApi, sovereignAccount1013, bn1e12.mul(new BN(10)), true, sudo); // set balance to 10 native asset console.log('======= Relaychain setup is done ======='); // checking if parachains start to producing blocks - console.log("wait for the parachain stars producting blocks...") + console.log("wait for the parachain stars producing blocks...") let currentBlockNumber = 0; while (currentBlockNumber < 1) { const signedBlock = await assetHubApi.rpc.chain.getBlock(); @@ -98,6 +112,10 @@ async function main() { console.log('======= Parchain setup begin ======='); // USDC token admin const usdcAdmin = sudo.address; + // AHN token admin + const ahnAdmin = sudo.address; + // TTT token admin + const tttAdmin = sudo.address; // create USDC test asset (foreign asset) on asset hub await createAsset(assetHubApi, usdcAssetID, usdcAdmin, usdcMinBalance, true, sudo); @@ -109,6 +127,18 @@ async function main() { await setAssetMetadata(bridgeHubApi, usdcAssetID, usdcName, usdcSymbol, usdcDecimal, true, sudo); await mintAsset(bridgeHubApi, usdcAssetID, usdcAdmin, bn1e12.mul(new BN(100)), true, sudo); // mint 100 USDC to Alice + // create Asset Hub Native(AHN) test asset (foreign asset) on bridge hub + // this is for mapping the Asset Hub Native asset on Bridge hub + await createAsset(bridgeHubApi, ahnAssetID, ahnAdmin, ahnMinBalance, true, sudo); + await setAssetMetadata(bridgeHubApi, ahnAssetID, ahnName, ahnSymbol, ahnDecimal, true, sudo); + await mintAsset(bridgeHubApi, ahnAssetID, ahnAdmin, bn1e12.mul(new BN(100)), true, sudo); // mint 100 AHN to Alice + + // create TTT test asset (foreign asset) on bridge hub + // this is for mapping the local foreign token on Bridge hub + await createAsset(bridgeHubApi, tttAssetID, tttAdmin, tttMinBalance, true, sudo); + await setAssetMetadata(bridgeHubApi, tttAssetID, tttName, tttSymbol, tttDecimal, true, sudo); + await mintAsset(bridgeHubApi, tttAssetID, tttAdmin, bn1e12.mul(new BN(100)), true, sudo); // mint 100 TTT to Alice + // make sure access segregator is set up for Alice before setting up all sygma pallet! // sygma config const basicFeeAmount = bn1e12.mul(new BN(1)); // 1 * 10 ** 12 @@ -129,19 +159,27 @@ async function main() { } // set fee for tokens with domains on bridge hub for (const domain of supportedDestDomains) { - await setFeeHandler(bridgeHubApi, domain.domainID, getUSDCAssetId(bridgeHubApi), feeHandlerType.PercentageFeeHandler, true, sudo) - await setFeeRate(bridgeHubApi, domain.domainID, getUSDCAssetId(bridgeHubApi), percentageFeeRate, feeRateLowerBound, feeRateUpperBound, true, sudo); + // set fee for TTT token for local token tests on bridge hub + await setFeeHandler(bridgeHubApi, domain.domainID, getTTTAssetId(bridgeHubApi), feeHandlerType.PercentageFeeHandler, true, sudo) + await setFeeRate(bridgeHubApi, domain.domainID, getTTTAssetId(bridgeHubApi), percentageFeeRate, feeRateLowerBound, feeRateUpperBound,true, sudo); + + // USDC as a foreign token is used for xcm related testcases + await setFeeHandler(bridgeHubApi, domain.domainID, getUSDCAssetId(bridgeHubApi), feeHandlerType.BasicFeeHandler, true, sudo) + await setFee(bridgeHubApi, domain.domainID, getUSDCAssetId(bridgeHubApi), basicFeeAmount,true, sudo); + + // AHN as a Native token of asset hub is used for xcm related testcases + await setFeeHandler(bridgeHubApi, domain.domainID, getAHNAssetId(bridgeHubApi), feeHandlerType.BasicFeeHandler, true, sudo) + await setFee(bridgeHubApi, domain.domainID, getAHNAssetId(bridgeHubApi), basicFeeAmount,true, sudo); } // transfer some native asset to FeeReserveAccount and TransferReserveAccount as Existential Deposit(aka ED) on bridge hub - await transferBalance(bridgeHubApi, FeeReserveAccount, bn1e12.mul(new BN(10000)), true, sudo); // set balance to 10000 native asset - await transferBalance(bridgeHubApi, NativeTokenTransferReserveAccount, bn1e12.mul(new BN(10000)), true, sudo); // set balance to 10000 native asset reserved account - await transferBalance(bridgeHubApi, OtherTokenTransferReserveAccount, bn1e12.mul(new BN(10000)), true, sudo); // set balance to 10000 other asset reserved account + await transferBalance(bridgeHubApi, FeeReserveAccount, bn1e12.mul(new BN(10)), true, sudo); // set balance to 10 native asset + await transferBalance(bridgeHubApi, NativeTokenTransferReserveAccount, bn1e12.mul(new BN(10)), true, sudo); // set balance to 10 native asset reserved account + await transferBalance(bridgeHubApi, OtherTokenTransferReserveAccount, bn1e12.mul(new BN(10)), true, sudo); // set balance to 10 other asset reserved account - // TODO: create another token on bridge hub for testcase 1 and 2, USDC will be used for xcm transfer testcase - // mint 1 USDC to reserve and fee account so that in the testcase they will not have null as balance - await mintAsset(bridgeHubApi, usdcAssetID, OtherTokenTransferReserveAccount, bn1e12.mul(new BN(1)), true, sudo); // mint 1 USDC to OtherTokenTransferReserveAccount - await mintAsset(bridgeHubApi, usdcAssetID, FeeReserveAccount, bn1e12.mul(new BN(1)), true, sudo); // mint 1 USDC to FeeReserveAccount + // mint 1 TTT to reserve and fee account so that in the testcase they will not have null as balance + await mintAsset(bridgeHubApi, tttAssetID, OtherTokenTransferReserveAccount, bn1e12.mul(new BN(1)), true, sudo); // mint 1 TTT to OtherTokenTransferReserveAccount + await mintAsset(bridgeHubApi, tttAssetID, FeeReserveAccount, bn1e12.mul(new BN(1)), true, sudo); // mint 1 TTT to FeeReserveAccount // set up MPC address(will also unpause all registered domains) on bridge hub if (mpcAddr) { @@ -152,11 +190,25 @@ async function main() { if (!await queryBridgePauseStatus(bridgeHubApi, domain.domainID)) console.log(`DestDomainID: ${domain.domainID} is readyβœ…`); } } - console.log('πŸš€ Sygma substrate pallet setup is done! πŸš€'); - // transfer native asset to parachain sovereignaccount on each parachain + // transfer native asset to the sovereignaccount of the other await transferBalance(assetHubApi, sovereignAccount1013, bn1e12.mul(new BN(1)), true, sudo); // set balance to 1 native asset await transferBalance(bridgeHubApi, sovereignAccount1000, bn1e12.mul(new BN(1)), true, sudo); // set balance to 1 native asset + + // transfer native asset to extension alice account on bridge hub + // this is for teleport native asset of Asset hub(AHN) -> Bridge hub testcase + const extensionAliceAccount = "5GYrSdyt7wydaQiqsnrvq11neaC2eTUBXCnXhSJKpUPT3hXP" + await transferBalance(bridgeHubApi, extensionAliceAccount, bn1e12.mul(new BN(1)), true, sudo); // set balance to 1 native asset + + // transfer native asset to FungiblesTransactor CheckingAccount on both parachains + // this is part of the parachain launching setup, ideally should be done by parachain team after launching, but in our testing env we are using brand new chain, so we need to set this up. + const CheckingAccount = "5EYCAe5ijiYgWYWi1fs8Xz1td1djEtJVVnNfzvDRP4VtLL7Y"; + await transferBalance(assetHubApi, CheckingAccount, bn1e12.mul(new BN(1)), true, sudo); // set balance to 1 native asset + // await transferBalance(bridgeHubApi, CheckingAccount, bn1e12.mul(new BN(1)), true, sudo); // set balance to 1 native asset + + // not sure what this account is, but it needs to exist as well + const sygmaXCMTransactorAccount = "5ExVnaLuWGe8WqCpaY4jg65kMz5hefx5A2covME3RhE4Y1m1"; + await transferBalance(bridgeHubApi, sygmaXCMTransactorAccount, bn1e12.mul(new BN(1)), true, sudo); // set balance to 1 native asset console.log('======= Parachain setup is done ======='); console.log('======= HRMP channel setup begin ======='); @@ -181,13 +233,9 @@ async function main() { const acceptHRMPChannelRequestEncodedData1013To1000 = "0x3c01f5030000"; await hrmpChannelRequest(assetHubApi, getHRMPChannelDest(assetHubApi), getHRMPChannelMessage(assetHubApi, acceptHRMPChannelRequestEncodedData1013To1000, 1000), 1013, 1000, true, sudo); - // transfer native asset to FungiblesTransactor CheckingAccount on both parachains - const CheckingAccount = "5EYCAe5ijiYgWYWi1fs8Xz1td1djEtJVVnNfzvDRP4VtLL7Y"; - await transferBalance(assetHubApi, CheckingAccount, bn1e12.mul(new BN(1)), true, sudo); // set balance to 1 native asset - await transferBalance(bridgeHubApi, CheckingAccount, bn1e12.mul(new BN(1)), true, sudo); // set balance to 1 native asset + console.log('======= HRMP Channel setup is done! ======='); - console.log('πŸš€ HRMP Channel setup is done! πŸš€'); + console.log('πŸš€ setup is done! πŸš€'); } - main().catch(console.error).finally(() => process.exit()); diff --git a/scripts/xcm/util.js b/scripts/xcm/util.js index 02f69da..e7eb4c3 100644 --- a/scripts/xcm/util.js +++ b/scripts/xcm/util.js @@ -17,6 +17,18 @@ const usdcName = "USDC test asset"; const usdcSymbol = "USDC"; const usdcDecimal = 12; +const ahnAssetID = 2001; +const ahnMinBalance = 100; +const ahnName = "Asset Hub Native"; +const ahnSymbol = "AHN"; +const ahnDecimal = 12; + +const tttAssetID = 2002; +const tttMinBalance = 100; +const tttName = "Test Token Tub"; +const tttSymbol = "TTT"; +const tttDecimal = 12; + // relay chain const relayChainProvider = new WsProvider(process.env.RELAYCHAINENDPOINT || 'ws://127.0.0.1:9942'); // asset hub parachain @@ -194,11 +206,6 @@ async function setMpcAddress(api, mpcAddr, finalization, sudo) { }); } -async function queryBridgePauseStatus(api, domainID) { - let result = await api.query.sygmaBridge.isPaused(domainID); - return result.toJSON() -} - async function createAsset(api, id, admin, minBalance, finalization, sudo) { return new Promise(async (resolve, reject) => { const nonce = Number((await api.query.system.account(sudo.address)).nonce); @@ -353,18 +360,145 @@ function getNativeMultiAsset(api, amount) { }) }) } +function getAHNAssetId(api) { + return api.createType('StagingXcmV3MultiassetAssetId', { + Concrete: api.createType('StagingXcmV3MultiLocation', { + parents: 1, + interior: api.createType('StagingXcmV3Junctions', { + X1: + api.createType('StagingXcmV3Junction', { + Parachain: api.createType('Compact', 1000) + }) + }) + }) + }) +} + +function getAHNMultiAsset(api, amount) { + return api.createType('StagingXcmV3MultiAsset', { + id: getAHNAssetId(api), + fun: api.createType('StagingXcmV3MultiassetFungibility', { + Fungible: api.createType('Compact', amount) + }) + }) +} + +// return USDC assetID with parachain(full X3) +function getUSDCAssetId(api) { + return api.createType('StagingXcmV3MultiassetAssetId', { + Concrete: api.createType('StagingXcmV3MultiLocation', { + parents: 1, + interior: api.createType('StagingXcmV3Junctions', { + X3: [ + api.createType('StagingXcmV3Junction', { + Parachain: api.createType('Compact', 1000) + }), + api.createType('StagingXcmV3Junction', { + PalletInstance: 50 + }), + api.createType('StagingXcmV3Junction', { + GeneralIndex: 2000 + }), + ] + }) + }) + }) +} + +// return USDC assetID but without parachain(only X2) +function getUSDCAssetIdX2(api) { + return api.createType('StagingXcmV3MultiassetAssetId', { + Concrete: api.createType('StagingXcmV3MultiLocation', { + parents: 0, + interior: api.createType('StagingXcmV3Junctions', { + X2: [ + api.createType('StagingXcmV3Junction', { + PalletInstance: 50 + }), + api.createType('StagingXcmV3Junction', { + GeneralIndex: 2000 + }), + ] + }) + }) + }) +} + +function getUSDCMultiAsset(api, amount) { + return api.createType('StagingXcmV3MultiAsset', { + id: getUSDCAssetId(api), + fun: api.createType('StagingXcmV3MultiassetFungibility', { + Fungible: api.createType('Compact', amount) + }) + }) +} + +function getUSDCMultiAssetX2(api, amount) { + return api.createType('StagingXcmV3MultiAsset', { + id: getUSDCAssetIdX2(api), + fun: api.createType('StagingXcmV3MultiassetFungibility', { + Fungible: api.createType('Compact', amount) + }) + }) +} + + +// TTT is used for foriegn token on Bridge hub test +function getTTTMultiAsset(api, amount) { + return api.createType('StagingXcmV3MultiAsset', { + id: getTTTAssetId(api), + fun: api.createType('StagingXcmV3MultiassetFungibility', { + Fungible: api.createType('Compact', amount) + }) + }) +} + +function getTTTAssetId(api) { + return api.createType('StagingXcmV3MultiassetAssetId', { + Concrete: api.createType('StagingXcmV3MultiLocation', { + parents: 1, + interior: api.createType('StagingXcmV3Junctions', { + X3: [ + api.createType('StagingXcmV3Junction', { + Parachain: api.createType('Compact', 1013) + }), + api.createType('StagingXcmV3Junction', { + // 0x7379676d61 is general key of sygma + GeneralKey: { + length: 5, + data: '0x7379676d61000000000000000000000000000000000000000000000000000000' + } + }), + api.createType('StagingXcmV3Junction', { + // 0x545454 is general key of TTT + GeneralKey: { + length: 3, + data: '0x5454540000000000000000000000000000000000000000000000000000000000' + } + }), + ] + }) + }) + }) +} function getAssetDepositDest(api) { return api.createType('StagingXcmV3MultiLocation', { parents: 0, interior: api.createType('StagingXcmV3Junctions', { - X3: [ + X4: [ api.createType('StagingXcmV3Junction', { GeneralKey: { length: 5, data: '0x7379676d61000000000000000000000000000000000000000000000000000000' } }), + api.createType('StagingXcmV3Junction', { + GeneralKey: { + length: 12, + data: '0x7379676d612d6272696467650000000000000000000000000000000000000000' + } + }), api.createType('StagingXcmV3Junction', { GeneralIndex: '1' }), @@ -379,46 +513,58 @@ function getAssetDepositDest(api) { }) } -function getUSDCAssetId(api) { - return api.createType('StagingXcmV3MultiassetAssetId', { - Concrete: api.createType('StagingXcmV3MultiLocation', { - parents: 1, +// The dest of teleport tokens from Asset hub +function getAssetHubTeleportDest(api) { + return api.createType('StagingXcmVersionedMultiLocation', { + V3: api.createType('StagingXcmV3MultiLocation', { + parents: 1, + interior: api.createType('StagingXcmV3Junctions', { + X1: api.createType('StagingXcmV3Junction', { + Parachain: api.createType('Compact', 1013) + }), + }) + }) + }) +} + +// The Beneficiary of teleport tokens from Asset hub to Bridge hub +function getAssetHubTeleportBeneficiary(api, beneficiary) { + return api.createType('StagingXcmVersionedMultiLocation', { + V3: api.createType('StagingXcmV3MultiLocation', { + parents: 0, interior: api.createType('StagingXcmV3Junctions', { - X3: [ - api.createType('StagingXcmV3Junction', { - Parachain: api.createType('Compact', 2005) - }), - api.createType('StagingXcmV3Junction', { - // 0x7379676d61 is general key of "sygma" defined in sygma substrate pallet runtime for testing - // see UsdcLocation definition in runtime.rs - GeneralKey: { - length: 5, - data: '0x7379676d61000000000000000000000000000000000000000000000000000000' - } - }), - api.createType('StagingXcmV3Junction', { - // 0x75736463 is general key of "usdc" defined in sygma substrate pallet runtime for testing - // see UsdcLocation definition in runtime.rs - GeneralKey: { - length: 4, - data: '0x7573646300000000000000000000000000000000000000000000000000000000' - } - }), - ] + X1: api.createType('StagingXcmV3Junction', { + AccountId32: { + network: api.createType('Option', 'rococo'), + id: beneficiary, + } + }), }) }) }) } -function getUSDCMultiAsset(api, amount) { - return api.createType('StagingXcmV3MultiAsset', { - id: getUSDCAssetId(api), - fun: api.createType('StagingXcmV3MultiassetFungibility', { - Fungible: api.createType('Compact', amount) - }) +// The Beneficiary of teleport tokens from Asset hub to Sygma via Bridge hub +function getAssetHubTeleportBeneficiaryToSygma(api) { + return api.createType('StagingXcmVersionedMultiLocation', { + V3: api.createType('StagingXcmV3MultiLocation', getAssetDepositDest(api)) + }) +} + +// The asset of teleport tokens from Asset hub to Bridge hub +function getAssetHubTeleportAsset(api, asset) { + return api.createType('StagingXcmVersionedMultiAssets', { + V3: api.createType('StagingXcmV3MultiassetMultiAssets', [ + asset + ]) }) } +// The weight limit of teleport tokens from Asset hub +function getAssetHubTeleportWeightLimit(api) { + return api.createType('StagingXcmV3WeightLimit', "Unlimited") +} + async function executeProposal(api, proposalList, signature, finalization, sudo) { return new Promise(async (resolve, reject) => { const nonce = Number((await api.query.system.account(sudo.address)).nonce); @@ -597,6 +743,77 @@ async function hrmpChannelRequest(api, dest, message, fromParachainID, toParacha }); } +async function teleportTokenViaXCM(api, {dest, beneficiary, assets, feeAssetItem, weightLimit, fromParachainID, toParachainID}, finalization, sudo) { + return new Promise(async (resolve, reject) => { + const nonce = Number((await api.query.system.account(sudo.address)).nonce); + + console.log( + `--- Submitting Teleport Token call from ${fromParachainID} to ${toParachainID}. (nonce: ${nonce}) ---` + ); + const unsub = await api.tx.polkadotXcm.limitedTeleportAssets(dest, beneficiary, assets, feeAssetItem, weightLimit) + .signAndSend(sudo, {nonce: nonce, era: 0}, ({ events = [], status, isError }) => { + console.log(`Current status is ${status}`); + if (status.isInBlock) { + console.log( + `Transaction included at blockHash ${status.asInBlock}` + ); + if (finalization) { + console.log('Waiting for finalization...'); + } else { + unsub(); + resolve(); + } + + console.log('Events:'); + events.forEach(({ event: { data, method, section }, phase }) => { + console.log('\t', phase.toString(), `: ${section}.${method}`, data.toString()); + }); + } else if (status.isFinalized) { + console.log( + `Transaction finalized at blockHash ${status.asFinalized}` + ); + unsub(); + resolve(); + } else if (isError) { + console.log(`Transaction Error`); + reject(`Transaction Error`); + } + }); + }); +} + +// Subscribe to system events via storage +async function subEvents (api, eventsList) { + api.query.system.events((events) => { + console.log(`\nReceived ${events.length} events:`); + + // Loop through the Vec + events.forEach((record) => { + // Extract the phase, event and the event types + const { event, phase } = record; + const types = event.typeDef; + + // Show what we are busy with + // console.log(`\t${event.section}:${event.method}:: (phase=${phase.toString()})`); + // console.log(`\t\t${event.meta}`); + console.log(`${event.section}:${event.method}`); + if (event.section.startsWith("sygmaBridge") || event.section.startsWith("sygmaBridgeForwarder")) { + eventsList.push(event.section); + } + + // Loop through each of the parameters, displaying the type and data + // event.data.forEach((data, index) => { + // console.log(`\t\t\t${types[index].type}: ${data.toString()}`); + // }); + }); + }); +} + +async function queryBridgePauseStatus(api, domainID) { + let result = await api.query.sygmaBridge.isPaused(domainID); + return result.toJSON() +} + async function queryAssetBalance(api, assetID, account) { let result = await api.query.assets.account(assetID, account); return result.toHuman() @@ -622,27 +839,40 @@ module.exports = { bridgeHubProvider, getNativeAssetId, getNativeMultiAsset, - getAssetDepositDest, getUSDCAssetId, + getUSDCMultiAssetX2, getUSDCMultiAsset, + getUSDCAssetIdX2, + getAHNAssetId, + getAHNMultiAsset, + getTTTMultiAsset, + getTTTAssetId, + getAssetDepositDest, deposit, registerDomain, - mintAsset, - setAssetMetadata, - createAsset, - queryBridgePauseStatus, setMpcAddress, setFee, setFeeRate, setFeeHandler, - transferBalance, executeProposal, + transferBalance, + createAsset, + setAssetMetadata, + mintAsset, + subEvents, + queryBridgePauseStatus, queryAssetBalance, queryBalance, queryMPCAddress, hrmpChannelRequest, getHRMPChannelMessage, getHRMPChannelDest, + teleportTokenViaXCM, + getAssetHubTeleportDest, + getAssetHubTeleportBeneficiary, + getAssetHubTeleportBeneficiaryToSygma, + getAssetHubTeleportAsset, + getAssetHubTeleportWeightLimit, delay, FeeReserveAccount, NativeTokenTransferReserveAccount, @@ -652,4 +882,14 @@ module.exports = { usdcName, usdcSymbol, usdcDecimal, + ahnAssetID, + ahnMinBalance, + ahnName, + ahnSymbol, + ahnDecimal, + tttAssetID, + tttMinBalance, + tttName, + tttSymbol, + tttDecimal, } From 5a425effedd8d18f0ada728e992d04fde7b86cbb Mon Sep 17 00:00:00 2001 From: Freddy Li Date: Tue, 2 Apr 2024 12:18:31 -0400 Subject: [PATCH 6/9] add comments --- scripts/xcm/e2e_tests.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/scripts/xcm/e2e_tests.js b/scripts/xcm/e2e_tests.js index 980dc8d..58f3ae9 100644 --- a/scripts/xcm/e2e_tests.js +++ b/scripts/xcm/e2e_tests.js @@ -58,13 +58,19 @@ async function main() { let failedTestcases = []; // run testcases + + // bridge hub parachain local test await testcase1(bridgeHubApi, sudo, failedTestcases); await testcase2(bridgeHubApi, sudo, failedTestcases); + + // asset hub to bridge hub and then to sygma relayer test await testcase3(assetHubApi, bridgeHubApi, sudo, failedTestcases); await testcase4(assetHubApi, bridgeHubApi, sudo, failedTestcases); await testcase5(assetHubApi, bridgeHubApi, sudo, failedTestcases); await testcase6(assetHubApi, bridgeHubApi, sudo, failedTestcases); + // sygma relayer to bridge hub and then to asset hub test + // checking if any testcase failed for (const item of failedTestcases) { console.error('\x1b[31m%s\x1b[0m\n', item); @@ -79,6 +85,8 @@ function str2BigInt(a) { // testcase 1: Native token deposit on Bridge hub, dest on relayer async function testcase1(bridgeHubApi, sudo, failedTestcases) { + console.log('testcase 1 ...'); + const nativeBalanceBeforeAlice = await queryBalance(bridgeHubApi, sudo.address); console.log('Alice native asset balance before: ', nativeBalanceBeforeAlice.data.free); @@ -116,6 +124,8 @@ async function testcase1(bridgeHubApi, sudo, failedTestcases) { // testcase 2: Foreign token TTT deposit on Bridge hub, dest on relayer async function testcase2(bridgeHubApi, sudo, failedTestcases) { + console.log('testcase 2 ...'); + let tttBalanceBeforeAlice = await queryAssetBalance(bridgeHubApi, tttAssetID, sudo.address); console.log('Alice TTT asset balance before: ', tttBalanceBeforeAlice.balance); @@ -153,6 +163,8 @@ async function testcase2(bridgeHubApi, sudo, failedTestcases) { // testcase 3: Foreign token(USDC) deposit on Asset hub, dest on Bridge hub async function testcase3(assetHubApi, bridgeHubApi, sudo, failedTestcases) { + console.log('testcase 3 ...'); + const usdcBalanceBeforeAliceAssethub = await queryAssetBalance(assetHubApi, usdcAssetID, sudo.address); console.log('Alice USDC asset balance on Asset hub before: ', usdcBalanceBeforeAliceAssethub.balance); @@ -193,6 +205,8 @@ async function testcase3(assetHubApi, bridgeHubApi, sudo, failedTestcases) { // testcase 4: Native token deposit on Asset hub, dest on Bridge hub async function testcase4(assetHubApi, bridgeHubApi, sudo, failedTestcases) { + console.log('testcase 4 ...'); + const beneficiaryAddressOnBridgehub = "5GYrSdyt7wydaQiqsnrvq11neaC2eTUBXCnXhSJKpUPT3hXP"; const beneficiary = "0xc668b505f6a7012a50dca169757c629651bfd6cefbfc24301dea2d2cc0ab2732" // Alice_extension @@ -235,6 +249,8 @@ async function testcase4(assetHubApi, bridgeHubApi, sudo, failedTestcases) { // testcase 5: Foreign token(USDC) deposit on Asset hub, dest on sygma via Bridge hub async function testcase5(assetHubApi, bridgeHubApi, sudo, failedTestcases) { + console.log('testcase 5 ...'); + const events = []; await subEvents(bridgeHubApi, events); @@ -285,6 +301,8 @@ async function testcase5(assetHubApi, bridgeHubApi, sudo, failedTestcases) { // testcase 6: Native token deposit on Asset hub, dest on sygma via Bridge hub async function testcase6(assetHubApi, bridgeHubApi, sudo, failedTestcases) { + console.log('testcase 6 ...'); + const events = []; await subEvents(bridgeHubApi, events); From d60f4306ddff5f40d6b3569bebbb1cf9d4ba9817 Mon Sep 17 00:00:00 2001 From: Freddy Li Date: Mon, 8 Apr 2024 13:14:19 -0400 Subject: [PATCH 7/9] finish xcm e2e testcases --- bridge-forwarder/src/lib.rs | 2 +- bridge-forwarder/src/xcm_asset_transactor.rs | 4 +- scripts/e2e_setup.sh | 2 +- scripts/xcm/e2e_tests.js | 197 +++++++++++++++++- scripts/xcm/package.json | 4 +- scripts/xcm/setup.js | 50 ++++- scripts/xcm/util.js | 20 +- substrate-node/parachain/runtime/src/lib.rs | 20 +- .../parachain/runtime/src/xcm_config.rs | 18 +- xcm-bridge/src/lib.rs | 2 +- .../bridge_hub_rococo_local_network.toml | 44 ++-- 11 files changed, 295 insertions(+), 68 deletions(-) diff --git a/bridge-forwarder/src/lib.rs b/bridge-forwarder/src/lib.rs index f446b6a..52cda55 100644 --- a/bridge-forwarder/src/lib.rs +++ b/bridge-forwarder/src/lib.rs @@ -43,7 +43,7 @@ pub mod pallet { what: MultiAsset, dest: MultiLocation, ) -> DispatchResult { - let cap_weight: Weight = Weight::from_parts(6_000_000_000u64, 2_000_000u64); + let cap_weight: Weight = Weight::from_all(u64::MAX); T::XCMBridge::transfer(origin, what.clone(), dest, Some(cap_weight))?; let origin_location: MultiLocation = diff --git a/bridge-forwarder/src/xcm_asset_transactor.rs b/bridge-forwarder/src/xcm_asset_transactor.rs index 3ec294e..ab70bcd 100644 --- a/bridge-forwarder/src/xcm_asset_transactor.rs +++ b/bridge-forwarder/src/xcm_asset_transactor.rs @@ -27,9 +27,9 @@ impl< // 2. recipient is on non-substrate chain(evm, cosmos, etc.) // 3. recipient is on the remote parachain fn deposit_asset(what: &MultiAsset, who: &MultiLocation, context: &XcmContext) -> XcmResult { - match (who.parents, who.first_interior()) { + match (who.parents, who.interior) { // 1. recipient is on the local parachain - (0, Some(AccountId32 { .. })) | (0, Some(AccountKey20 { .. })) => { + (0, X1(AccountId32 { .. })) | (0, X1(AccountKey20 { .. })) | (1, X1(Parachain(_))) => { // check if the asset is native, and call the corresponding deposit_asset() if AssetTypeChecker::is_native_asset(what) { CurrencyTransactor::deposit_asset(what, who, context)?; diff --git a/scripts/e2e_setup.sh b/scripts/e2e_setup.sh index 66c007e..5a25eb1 100755 --- a/scripts/e2e_setup.sh +++ b/scripts/e2e_setup.sh @@ -19,7 +19,7 @@ CHAINSPECFILE="chain-spec.json" # Run setup script echo "run scripts to set up pallets..." npm i --prefix $SETUP_SCRIPTS_DIR/scripts/js -node $SETUP_SCRIPTS_DIR/scripts/js/setup.js +node $SETUP_SCRIPTS_DIR/scripts/standalone/setup.js sleep 10 diff --git a/scripts/xcm/e2e_tests.js b/scripts/xcm/e2e_tests.js index 58f3ae9..d6973d0 100644 --- a/scripts/xcm/e2e_tests.js +++ b/scripts/xcm/e2e_tests.js @@ -3,6 +3,9 @@ require('dotenv').config(); +const {decodeAddress} = require('@polkadot/util-crypto'); +const {utils} = require('ethers'); + const {ApiPromise, WsProvider, Keyring} = require('@polkadot/api'); const {cryptoWaitReady} = require('@polkadot/util-crypto'); const { @@ -22,6 +25,7 @@ const { getAssetHubTeleportAsset, getAssetHubTeleportWeightLimit, deposit, + executeProposal, subEvents, queryBalance, queryAssetBalance, @@ -38,7 +42,7 @@ const { tttMinBalance, tttName, tttSymbol, - tttDecimal, + tttDecimal, bhnAssetID, } = require("./util"); async function main() { @@ -70,11 +74,16 @@ async function main() { await testcase6(assetHubApi, bridgeHubApi, sudo, failedTestcases); // sygma relayer to bridge hub and then to asset hub test + await testcase7(bridgeHubApi, sudo, failedTestcases); + await testcase8(bridgeHubApi, sudo, failedTestcases); + await testcase9(bridgeHubApi, assetHubApi, sudo, failedTestcases); + await testcase10(bridgeHubApi, assetHubApi, sudo, failedTestcases); // checking if any testcase failed for (const item of failedTestcases) { - console.error('\x1b[31m%s\x1b[0m\n', item); - return + // console.error('\x1b[31m%s\x1b[0m\n', item); + // return + throw Error(`\x1b[31m${item}\x1b[0m`); } console.log('\x1b[32m%s\x1b[0m', "All testcases pass"); } @@ -351,6 +360,188 @@ async function testcase6(assetHubApi, bridgeHubApi, sudo, failedTestcases) { } } +// testcase 7: Foreign token(USDC) send from sygma relayer to Bridge hub +async function testcase7(bridgeHubApi, sudo, failedTestcases) { + console.log('testcase 7 ...'); + + const events = []; + await subEvents(bridgeHubApi, events); + + // transfer 0.0001 USDC from sygma relayer to Alice on bridge hub + const proposal_usdc = { + origin_domain_id: 1, + deposit_nonce: 111, + resource_id: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0], + data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 243, 16, 122, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 0, 1, 1, 0, 212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44, 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125] + } + // signature is not used in the integration demo, this is just a placeholder + const signature = [180, 250, 104, 54, 47, 69, 174, 209, 145, 226, 25, 32, 184, 96, 142, 125, 103, 53, 60, 180, 107, 207, 80, 188, 9, 138, 218, 97, 50, 132, 193, 10, 6, 15, 186, 139, 6, 21, 63, 39, 157, 144, 81, 12, 81, 165, 215, 213, 200, 105, 198, 105, 115, 193, 42, 183, 145, 118, 52, 47, 45, 198, 165, 5, 28]; + + const usdcBalanceBefore = await queryAssetBalance(bridgeHubApi, usdcAssetID, sudo.address); + console.log('usdc asset balance before: ', usdcBalanceBefore.balance); + + await executeProposal(bridgeHubApi, [proposal_usdc], signature, true, sudo); + + const usdcbalanceAfter = await queryAssetBalance(bridgeHubApi, usdcAssetID, sudo.address); + console.log('usdc asset balance after: ', usdcbalanceAfter.balance); + + // USDC balance of Alice on Bridge hub should not equal + // USDC is a configured as a reserved token + if (str2BigInt(usdcbalanceAfter.balance) !== str2BigInt(usdcBalanceBefore.balance) + BigInt(100000000)) { + failedTestcases.push('testcase 7 failed: USDC balance not match') + } + + // checking if any sygma events emitted + for (const e of events) { + console.log('sygma pallets event emitted: \x1b[32m%s\x1b[0m\n', e); + } + if (events.length === 0) { + failedTestcases.push("testcase 7 failed: sygma pallets event not emitted"); + } +} + +// testcase 8: Native token of bridge hub send from sygma relayer to Bridge hub +async function testcase8(bridgeHubApi, sudo, failedTestcases) { + console.log('testcase 8 ...'); + + const events = []; + await subEvents(bridgeHubApi, events); + + // transfer 0.0001 native from sygma relayer to Alice on bridge hub + const proposal_native = { + origin_domain_id: 1, + deposit_nonce: 222, + resource_id: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], + data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 243, 16, 122, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 0, 1, 1, 0, 212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44, 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125] + } + // signature is not used in the integration demo, this is just a placeholder + const signature = [180, 250, 104, 54, 47, 69, 174, 209, 145, 226, 25, 32, 184, 96, 142, 125, 103, 53, 60, 180, 107, 207, 80, 188, 9, 138, 218, 97, 50, 132, 193, 10, 6, 15, 186, 139, 6, 21, 63, 39, 157, 144, 81, 12, 81, 165, 215, 213, 200, 105, 198, 105, 115, 193, 42, 183, 145, 118, 52, 47, 45, 198, 165, 5, 28]; + + const nativeBalanceBefore = await queryBalance(bridgeHubApi, sudo.address); + console.log('native asset balance before: ', nativeBalanceBefore.data.free); + + await executeProposal(bridgeHubApi, [proposal_native], signature, true, sudo); + + const nativeBalanceAfter = await queryBalance(bridgeHubApi, sudo.address); + console.log('native asset balance after: ', nativeBalanceAfter.data.free); + + const before_num = BigInt(nativeBalanceBefore.data.free.replaceAll(',', '')); + const after_num = BigInt(nativeBalanceAfter.data.free.replaceAll(',', '')); + + if (after_num <= before_num) { + failedTestcases.push('testcase 8 failed: Native asset not match') + } + + // checking if any sygma events emitted + for (const e of events) { + console.log('sygma pallets event emitted: \x1b[32m%s\x1b[0m\n', e); + } + if (events.length === 0) { + failedTestcases.push("testcase 8 failed: sygma pallets event not emitted"); + } +} + +// testcase 9: Foreign token(USDC) send from sygma relayer to Bridge hub then to Asset hub via XCM +async function testcase9(bridgeHubApi, assetHubApi, sudo, failedTestcases) { + console.log('testcase 9 ...'); + + const events = []; + await subEvents(bridgeHubApi, events); + + // transfer 1 USDC token from sygma relayer to Alice on Asset hub via Bridge hub + // data: [0, 32] => amount, [33, 64] => recipient length, [64 - end] => recipient address(Alice) + const proposal_usdc = { + origin_domain_id: 1, + deposit_nonce: 333, + resource_id: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0], + data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 224, 182, 179, 167, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 1, 2, 0, 161, 15, 1, 0, 212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44, 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125], + } + // signature is not used in the integration demo, this is just a placeholder + const signature = [180, 250, 104, 54, 47, 69, 174, 209, 145, 226, 25, 32, 184, 96, 142, 125, 103, 53, 60, 180, 107, 207, 80, 188, 9, 138, 218, 97, 50, 132, 193, 10, 6, 15, 186, 139, 6, 21, 63, 39, 157, 144, 81, 12, 81, 165, 215, 213, 200, 105, 198, 105, 115, 193, 42, 183, 145, 118, 52, 47, 45, 198, 165, 5, 28]; + + const usdcBalanceBeforeBridgehub = await queryAssetBalance(bridgeHubApi, usdcAssetID, OtherTokenTransferReserveAccount); + console.log('usdc asset balance before on Bridge hub: ', usdcBalanceBeforeBridgehub.balance); + + const usdcBalanceBeforeAssethub = await queryAssetBalance(assetHubApi, usdcAssetID, sudo.address); + console.log('usdc asset balance before on Asset hub: ', usdcBalanceBeforeAssethub.balance); + + await executeProposal(bridgeHubApi, [proposal_usdc], signature, true, sudo); + + const usdcBalanceAfterBridgehub = await queryAssetBalance(bridgeHubApi, usdcAssetID, OtherTokenTransferReserveAccount); + console.log('usdc asset balance after on Bridge hub: ', usdcBalanceAfterBridgehub.balance); + + const usdcBalanceAfterAssethub = await queryAssetBalance(assetHubApi, usdcAssetID, sudo.address); + console.log('usdc asset balance after on Asset hub: ', usdcBalanceAfterAssethub.balance); + + // OtherTokenTransferReserveAccount as the USDC token reserved account on Bridge hub, should be deducted by 1 USDC token + if (str2BigInt(usdcBalanceBeforeBridgehub.balance) - str2BigInt(usdcBalanceAfterBridgehub.balance) !== BigInt(1000000000000)) { + failedTestcases.push('testcase 9 failed: USDC asset not match in OtherTokenTransferReserveAccount') + } + + // the recipient on Asset hub(Alice) should receive less than 1 USDC token bcs a small port of fee is charged + if (str2BigInt(usdcBalanceAfterAssethub.balance) - str2BigInt(usdcBalanceBeforeAssethub.balance) >= BigInt(1000000000000)) { + failedTestcases.push('testcase 9 failed: USDC asset not match in recipient account on Asset hub') + } + + // checking if any sygma events emitted + for (const e of events) { + console.log('sygma pallets event emitted: \x1b[32m%s\x1b[0m\n', e); + } + if (events.length === 0) { + failedTestcases.push("testcase 9 failed: sygma pallets event not emitted"); + } +} + +// testcase 10: Native token of bridge hub send from sygma relayer to Bridge hub then to Asset hub via XCM +async function testcase10(bridgeHubApi, assetHubApi, sudo, failedTestcases) { + console.log('testcase 10 ...'); + + const events = []; + await subEvents(bridgeHubApi, events); + + // transfer 1 native from sygma relayer to Alice on Asset hub via Bridge hub + // data: [0, 32] => amount, [33, 64] => recipient length, [64 - end] => recipient address(Alice) + const proposal_native = { + origin_domain_id: 1, + deposit_nonce: 444, + resource_id: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], + data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 224, 182, 179, 167, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 1, 2, 0, 161, 15, 1, 0, 212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44, 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125], + } + // signature is not used in the integration demo, this is just a placeholder + const signature = [180, 250, 104, 54, 47, 69, 174, 209, 145, 226, 25, 32, 184, 96, 142, 125, 103, 53, 60, 180, 107, 207, 80, 188, 9, 138, 218, 97, 50, 132, 193, 10, 6, 15, 186, 139, 6, 21, 63, 39, 157, 144, 81, 12, 81, 165, 215, 213, 200, 105, 198, 105, 115, 193, 42, 183, 145, 118, 52, 47, 45, 198, 165, 5, 28]; + + const nativeBalanceBeforeBridgehub = await queryBalance(bridgeHubApi, NativeTokenTransferReserveAccount); + console.log('native asset balance before on Bridge hub: ', nativeBalanceBeforeBridgehub.data.free); + + const nativeBalanceBeforeAssethub = await queryAssetBalance(assetHubApi, bhnAssetID, sudo.address); + console.log('native asset balance before on Asset hub: ', nativeBalanceBeforeAssethub.balance); + + await executeProposal(bridgeHubApi, [proposal_native], signature, true, sudo); + + const nativeBalanceAfterBridgehub = await queryBalance(bridgeHubApi, NativeTokenTransferReserveAccount); + console.log('native asset balance after on Bridge hub: ', nativeBalanceAfterBridgehub.data.free); + + const nativeBalanceAfterAssethub = await queryAssetBalance(assetHubApi, bhnAssetID, sudo.address); + console.log('native asset balance after on Asset hub: ', nativeBalanceAfterAssethub.balance); + + // NativeTokenTransferReserveAccount as the native token reserved account on Bridge hub, should be deducted by 1 token + if (str2BigInt(nativeBalanceBeforeBridgehub.data.free) - str2BigInt(nativeBalanceAfterBridgehub.data.free) !== BigInt(1000000000000)) { + failedTestcases.push('testcase 10 failed: native asset not match in NativeTokenTransferReserveAccount on Bridge hub') + } + + // the recipient on Asset hub(Alice) should receive less than 1 BHN token bcs a small port of fee is charged + if (str2BigInt(nativeBalanceAfterAssethub.balance) - str2BigInt(nativeBalanceBeforeAssethub.balance) >= BigInt(1000000000000)) { + failedTestcases.push('testcase 10 failed: native asset not match in recipient account on Asset hub') + } + + // checking if any sygma events emitted + for (const e of events) { + console.log('sygma pallets event emitted: \x1b[32m%s\x1b[0m\n', e); + } + if (events.length === 0) { + failedTestcases.push("testcase 10 failed: sygma pallets event not emitted"); + } +} main().catch(console.error).finally(() => process.exit()); diff --git a/scripts/xcm/package.json b/scripts/xcm/package.json index 965d934..75e3464 100644 --- a/scripts/xcm/package.json +++ b/scripts/xcm/package.json @@ -11,7 +11,7 @@ "@polkadot/util": "^12.6.2", "@polkadot/util-crypto": "^12.6.2", "bn.js": "^5.2.1", - "dotenv": "^16.0.3" + "dotenv": "^16.0.3", + "ethers": "5.4.5" } } - diff --git a/scripts/xcm/setup.js b/scripts/xcm/setup.js index 80fa5a9..0ec2712 100644 --- a/scripts/xcm/setup.js +++ b/scripts/xcm/setup.js @@ -20,8 +20,8 @@ const { setAssetMetadata, mintAsset, getUSDCAssetId, - getAHNAssetId, getAHNMultiAsset, + getAHNAssetId, getTTTMultiAsset, getTTTAssetId, queryBridgePauseStatus, @@ -47,6 +47,11 @@ const { tttName, tttSymbol, tttDecimal, + bhnAssetID, + bhnMinBalance, + bhnName, + bhnSymbol, + bhnDecimal, } = require("./util"); const BN = require('bn.js'); @@ -114,9 +119,13 @@ async function main() { const usdcAdmin = sudo.address; // AHN token admin const ahnAdmin = sudo.address; + // BHN token admin + const bhnAdmin = sudo.address; // TTT token admin const tttAdmin = sudo.address; + const extensionAliceAccount = "5GYrSdyt7wydaQiqsnrvq11neaC2eTUBXCnXhSJKpUPT3hXP" + // create USDC test asset (foreign asset) on asset hub await createAsset(assetHubApi, usdcAssetID, usdcAdmin, usdcMinBalance, true, sudo); await setAssetMetadata(assetHubApi, usdcAssetID, usdcName, usdcSymbol, usdcDecimal, true, sudo); @@ -133,6 +142,12 @@ async function main() { await setAssetMetadata(bridgeHubApi, ahnAssetID, ahnName, ahnSymbol, ahnDecimal, true, sudo); await mintAsset(bridgeHubApi, ahnAssetID, ahnAdmin, bn1e12.mul(new BN(100)), true, sudo); // mint 100 AHN to Alice + // create Bridge Hub Native(BHN) test asset (foreign asset) on Asset hub + // this is for mapping the Bridge Hub Native asset on Asset hub + await createAsset(assetHubApi, bhnAssetID, bhnAdmin, bhnMinBalance, true, sudo); + await setAssetMetadata(assetHubApi, bhnAssetID, bhnName, bhnSymbol, bhnDecimal, true, sudo); + await mintAsset(assetHubApi, bhnAssetID, bhnAdmin, bn1e12.mul(new BN(100)), true, sudo); // mint 100 BHN to Alice + // create TTT test asset (foreign asset) on bridge hub // this is for mapping the local foreign token on Bridge hub await createAsset(bridgeHubApi, tttAssetID, tttAdmin, tttMinBalance, true, sudo); @@ -181,6 +196,15 @@ async function main() { await mintAsset(bridgeHubApi, tttAssetID, OtherTokenTransferReserveAccount, bn1e12.mul(new BN(1)), true, sudo); // mint 1 TTT to OtherTokenTransferReserveAccount await mintAsset(bridgeHubApi, tttAssetID, FeeReserveAccount, bn1e12.mul(new BN(1)), true, sudo); // mint 1 TTT to FeeReserveAccount + // mint 100 USDC to reserve account so that the testcase 7 will have some init funds + await mintAsset(bridgeHubApi, usdcAssetID, OtherTokenTransferReserveAccount, bn1e12.mul(new BN(100)), true, sudo); // mint 100 USDC to OtherTokenTransferReserveAccount + + // mint 10 USDC to the sibling sovereignaccount of 1013 on asset hub + // USDC is reserved on asset hub, so in testcase 9, the reserved token from siblingSovereignAccount1013 will be transferred to the recipient on Asset hub + const siblingSovereignAccount1013= "5Eg2fntRRwLinojmk3sh5xscp7F3S6Zzm5oDVtoLTALKiypR"; + await transferBalance(assetHubApi, siblingSovereignAccount1013, bn1e12.mul(new BN(10)), true, sudo); // make sure the sibling sovereignaccount of 1013 on asset hub exists + await mintAsset(assetHubApi, usdcAssetID, siblingSovereignAccount1013, bn1e12.mul(new BN(10)), true, sudo); + // set up MPC address(will also unpause all registered domains) on bridge hub if (mpcAddr) { console.log(`set up mpc address: ${mpcAddr}`); @@ -197,18 +221,26 @@ async function main() { // transfer native asset to extension alice account on bridge hub // this is for teleport native asset of Asset hub(AHN) -> Bridge hub testcase - const extensionAliceAccount = "5GYrSdyt7wydaQiqsnrvq11neaC2eTUBXCnXhSJKpUPT3hXP" await transferBalance(bridgeHubApi, extensionAliceAccount, bn1e12.mul(new BN(1)), true, sudo); // set balance to 1 native asset + // mint 10 AHN to extensionAliceAccount, used in testcase 4 + await mintAsset(bridgeHubApi, ahnAssetID, extensionAliceAccount, bn1e12.mul(new BN(10)), true, sudo); + // mint 10 AHN to OtherTokenTransferReserveAccount, used in testcase 6 + await mintAsset(bridgeHubApi, ahnAssetID, OtherTokenTransferReserveAccount, bn1e12.mul(new BN(10)), true, sudo); // mint 10 AHN to OtherTokenTransferReserveAccount + + // transfer native asset to FungiblesTransactor CheckingAccount on both parachains - // this is part of the parachain launching setup, ideally should be done by parachain team after launching, but in our testing env we are using brand new chain, so we need to set this up. + // this is part of the parachain launching setup, ideally should be done by parachain team after launching, but in our testing env we are using brand-new chain, so we need to set this up. const CheckingAccount = "5EYCAe5ijiYgWYWi1fs8Xz1td1djEtJVVnNfzvDRP4VtLL7Y"; await transferBalance(assetHubApi, CheckingAccount, bn1e12.mul(new BN(1)), true, sudo); // set balance to 1 native asset - // await transferBalance(bridgeHubApi, CheckingAccount, bn1e12.mul(new BN(1)), true, sudo); // set balance to 1 native asset + await transferBalance(bridgeHubApi, CheckingAccount, bn1e12.mul(new BN(1)), true, sudo); // set balance to 1 native asset - // not sure what this account is, but it needs to exist as well + // some other addresses need to exist as well, they are tmp accounts in pallets const sygmaXCMTransactorAccount = "5ExVnaLuWGe8WqCpaY4jg65kMz5hefx5A2covME3RhE4Y1m1"; - await transferBalance(bridgeHubApi, sygmaXCMTransactorAccount, bn1e12.mul(new BN(1)), true, sudo); // set balance to 1 native asset + const sygmaXCMTransactorAccount2 = "5D6gSNWpCcRowidpVC2k3FzmrfJjHX1Wu2NuBgsi717qtL5Y"; // when transfer BHN from sygma relayer to asset hub via bridge hub, this account received the BHN from NativeReservedAcoount on bridge hub + await transferBalance(bridgeHubApi, sygmaXCMTransactorAccount, bn1e12.mul(new BN(10)), true, sudo); // set balance to 10 native asset + await transferBalance(bridgeHubApi, sygmaXCMTransactorAccount2, bn1e12.mul(new BN(10)), true, sudo); // set balance to 10 native asset + console.log('======= Parachain setup is done ======='); console.log('======= HRMP channel setup begin ======='); @@ -217,18 +249,18 @@ async function main() { const openHRMPChannelRequestEncodedData1000To1013 = "0x3c00f50300000800000000001000"; await hrmpChannelRequest(assetHubApi, getHRMPChannelDest(assetHubApi), getHRMPChannelMessage(assetHubApi, openHRMPChannelRequestEncodedData1000To1013, 1000), 1000, 1013, true, sudo); console.log("wait processing on the relay chain...") - await delay(15000); + await delay(10000); // accept HRMP channel open request on 1013 const acceptHRMPChannelRequestEncodedData1000To1013 = "0x3c01e8030000"; await hrmpChannelRequest(bridgeHubApi, getHRMPChannelDest(bridgeHubApi), getHRMPChannelMessage(bridgeHubApi, acceptHRMPChannelRequestEncodedData1000To1013, 1013), 1000, 1013, true, sudo); - await delay(15000); + await delay(5000); // init HRMP channel open request from 1013 to 1000 const openHRMPChannelRequestEncodedData1013To1000 = "0x3c00e80300000800000000001000"; await hrmpChannelRequest(bridgeHubApi, getHRMPChannelDest(bridgeHubApi), getHRMPChannelMessage(bridgeHubApi, openHRMPChannelRequestEncodedData1013To1000, 1013), 1013, 1000, true, sudo); console.log("wait processing on the relay chain...") - await delay(15000); + await delay(10000); // accept HRMP channel open request on 1000 const acceptHRMPChannelRequestEncodedData1013To1000 = "0x3c01f5030000"; await hrmpChannelRequest(assetHubApi, getHRMPChannelDest(assetHubApi), getHRMPChannelMessage(assetHubApi, acceptHRMPChannelRequestEncodedData1013To1000, 1000), 1013, 1000, true, sudo); diff --git a/scripts/xcm/util.js b/scripts/xcm/util.js index e7eb4c3..0af059c 100644 --- a/scripts/xcm/util.js +++ b/scripts/xcm/util.js @@ -11,24 +11,35 @@ const NativeTokenTransferReserveAccount = "5EYCAe5jLbHcAAMKvLFSXgCTbPrLgBJusvPwf const OtherTokenTransferReserveAccount = "5EYCAe5jLbHcAAMKvLFiGhk3htXY8jQncbLTDGJQnpnPMAVp"; // UsdcAssetId: AssetId defined in runtime.rs +// USDC: Foreign token created on both Asset hub and Bridge hub const usdcAssetID = 2000; const usdcMinBalance = 100; const usdcName = "USDC test asset"; const usdcSymbol = "USDC"; const usdcDecimal = 12; -const ahnAssetID = 2001; +// AHN: Asset hub native token on Bridge hub +const + ahnAssetID = 2001; const ahnMinBalance = 100; const ahnName = "Asset Hub Native"; const ahnSymbol = "AHN"; const ahnDecimal = 12; +// TTT: Foreign token created on Bridge hub const tttAssetID = 2002; const tttMinBalance = 100; const tttName = "Test Token Tub"; const tttSymbol = "TTT"; const tttDecimal = 12; +// BHN: Bridge hub native token on Asset hub +const bhnAssetID = 2003; +const bhnMinBalance = 100; +const bhnName = "Bridge Hub Native"; +const bhnSymbol = "BHN"; +const bhnDecimal = 12; + // relay chain const relayChainProvider = new WsProvider(process.env.RELAYCHAINENDPOINT || 'ws://127.0.0.1:9942'); // asset hub parachain @@ -798,7 +809,7 @@ async function subEvents (api, eventsList) { // console.log(`\t\t${event.meta}`); console.log(`${event.section}:${event.method}`); if (event.section.startsWith("sygmaBridge") || event.section.startsWith("sygmaBridgeForwarder")) { - eventsList.push(event.section); + eventsList.push(`${event.section}:${event.method}`); } // Loop through each of the parameters, displaying the type and data @@ -892,4 +903,9 @@ module.exports = { tttName, tttSymbol, tttDecimal, + bhnAssetID, + bhnMinBalance, + bhnName, + bhnSymbol, + bhnDecimal, } diff --git a/substrate-node/parachain/runtime/src/lib.rs b/substrate-node/parachain/runtime/src/lib.rs index 9ad48f9..e4a7e59 100644 --- a/substrate-node/parachain/runtime/src/lib.rs +++ b/substrate-node/parachain/runtime/src/lib.rs @@ -884,24 +884,18 @@ impl ExtractDestinationData for DestinationDataParser { ( 0, Junctions::X3( - GeneralKey { - length: path_len, - data: sygma_path, - }, + GeneralKey { length: path_len, data: sygma_path }, GeneralIndex(dest_domain_id), - GeneralKey { - length: recipient_len, - data: recipient, - }, + GeneralKey { length: recipient_len, data: recipient }, ), ) => { - if &sygma_path[..*path_len as usize] == &[0x73, 0x79, 0x67, 0x6d, 0x61] { - return TryInto::::try_into(*dest_domain_id).ok().map( - |domain_id| (recipient[..*recipient_len as usize].to_vec(), domain_id), - ); + if sygma_path[..*path_len as usize] == [0x73, 0x79, 0x67, 0x6d, 0x61] { + return TryInto::::try_into(*dest_domain_id).ok().map(|domain_id| { + (recipient[..*recipient_len as usize].to_vec(), domain_id) + }); } None - } + }, _ => None, } } diff --git a/substrate-node/parachain/runtime/src/xcm_config.rs b/substrate-node/parachain/runtime/src/xcm_config.rs index 9caa62e..38b1b77 100644 --- a/substrate-node/parachain/runtime/src/xcm_config.rs +++ b/substrate-node/parachain/runtime/src/xcm_config.rs @@ -17,6 +17,7 @@ use frame_system::EnsureRoot; use pallet_xcm::XcmPassthrough; use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::impls::ToAuthor; +use sp_runtime::traits::CheckedConversion; use sp_std::vec; use sp_std::{marker::PhantomData, vec::Vec}; use sygma_traits::AssetTypeIdentifier; @@ -25,17 +26,12 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowTopLevelPaidExecutionFrom, CurrencyAdapter, DenyReserveTransferToRelayChain, DenyThenTry, EnsureXcmOrigin, - FixedWeightBounds, NativeAsset, ParentIsPreset, RelayChainAsNative, - SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, - SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, - UsingComponents, WithComputedOrigin, WithUniqueTopic, -}; -use xcm_executor::{ - traits::{MatchesFungible}, - XcmExecutor, + FixedWeightBounds, NativeAsset, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, + SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, + WithComputedOrigin, WithUniqueTopic, }; -use sp_runtime::traits::CheckedConversion; - +use xcm_executor::{traits::MatchesFungible, XcmExecutor}; parameter_types! { pub const RelayLocation: MultiLocation = MultiLocation::parent(); @@ -232,7 +228,7 @@ impl> MatchesFungible for NativeAsse match (&a.id, &a.fun) { (Concrete(_), Fungible(ref amount)) if C::is_native_asset(a) => { CheckedConversion::checked_from(*amount) - } + }, _ => None, } } diff --git a/xcm-bridge/src/lib.rs b/xcm-bridge/src/lib.rs index 1b9e52b..d168607 100644 --- a/xcm-bridge/src/lib.rs +++ b/xcm-bridge/src/lib.rs @@ -155,7 +155,7 @@ pub mod pallet { self.origin, xcm_instructions.clone(), hash, - message_weight, + self.weight, message_weight, ) .ensure_complete() diff --git a/zombienet/bridge_hub_rococo_local_network.toml b/zombienet/bridge_hub_rococo_local_network.toml index e2042f6..bcb66de 100644 --- a/zombienet/bridge_hub_rococo_local_network.toml +++ b/zombienet/bridge_hub_rococo_local_network.toml @@ -33,11 +33,10 @@ cumulus_based = true rpc_port = 8933 ws_port = 8943 args = [ - "-lparachain=debug,runtime::bridge-hub=trace,runtime::bridge=trace,runtime::bridge-dispatch=trace,bridge=trace,runtime::bridge-messages=trace,xcm=trace", + "-lparachain=warn,runtime::bridge-hub=debug,runtime::bridge=trace,runtime::bridge-dispatch=debug,bridge=trace,runtime::bridge-messages=debug,xcm=trace", ] extra_args = [ - "--force-authoring", "--no-mdns", "--bootnodes {{'bridge-hub-bob-collator'|zombie('multiAddress')}}", - "-- --port 41333 --rpc-port 48933 --ws-port 48943 --no-mdns", "--bootnodes {{'alice-validator'|zombie('multiAddress')}}" + "--force-authoring", "--no-mdns", "--bootnodes {{'bridge-hub-bob-collator'|zombie('multiAddress')}}", "--bootnodes {{'alice-validator'|zombie('multiAddress')}}" ] # run bob as parachain collator @@ -48,11 +47,10 @@ cumulus_based = true rpc_port = 8934 ws_port = 8944 args = [ - "-lparachain=trace,runtime::bridge-hub=trace,runtime::bridge=trace,runtime::bridge-dispatch=trace,bridge=trace,runtime::bridge-messages=trace,xcm=trace", + "-lparachain=warn,runtime::bridge-hub=debug,runtime::bridge=trace,runtime::bridge-dispatch=debug,bridge=trace,runtime::bridge-messages=debug,xcm=trace", ] extra_args = [ - "--force-authoring", "--no-mdns", "--bootnodes {{'bridge-hub-alice-collator'|zombie('multiAddress')}}", - "-- --port 41334 --rpc-port 48934 --ws-port 48944 --no-mdns", "--bootnodes {{'bob-validator'|zombie('multiAddress')}}" + "--force-authoring", "--no-mdns", "--bootnodes {{'bridge-hub-alice-collator'|zombie('multiAddress')}}", "--bootnodes {{'bob-validator'|zombie('multiAddress')}}" ] [[parachains]] @@ -66,11 +64,10 @@ cumulus_based = true ws_port = 9910 command = "./polkadot-parachain" args = [ - "-lparachain=debug,xcm=trace,runtime::bridge-transfer=trace", + "-lparachain=warn,runtime::bridge-hub=debug,runtime::bridge=trace,runtime::bridge-dispatch=debug,bridge=trace,runtime::bridge-messages=debug,xcm=trace", ] extra_args = [ - "--no-mdns", "--bootnodes {{'asset-hub-bob-collator'|zombie('multiAddress')}}", - "-- --port 51333 --rpc-port 58933 --ws-port 58943 --no-mdns", "--bootnodes {{'alice-validator'|zombie('multiAddress')}}" + "--no-mdns", "--bootnodes {{'asset-hub-bob-collator'|zombie('multiAddress')}}", "--bootnodes {{'alice-validator'|zombie('multiAddress')}}" ] [[parachains.collators]] @@ -79,21 +76,22 @@ cumulus_based = true ws_port = 9810 command = "./polkadot-parachain" args = [ - "-lparachain=debug,xcm=trace,runtime::bridge-transfer=trace", + "-lparachain=warn,runtime::bridge-hub=debug,runtime::bridge=trace,runtime::bridge-dispatch=debug,bridge=trace,runtime::bridge-messages=debug,xcm=trace", ] extra_args = [ - "--no-mdns", "--bootnodes {{'asset-hub-alice-collator'|zombie('multiAddress')}}", - "-- --port 51433 --rpc-port 58833 --ws-port 58843 --no-mdns", "--bootnodes {{'alice-validator'|zombie('multiAddress')}}" + "--no-mdns", "--bootnodes {{'asset-hub-alice-collator'|zombie('multiAddress')}}", "--bootnodes {{'alice-validator'|zombie('multiAddress')}}" ] -[[hrmp_channels]] -sender = 1000 -recipient = 1013 -max_capacity = 4 -max_message_size = 524288 - -[[hrmp_channels]] -sender = 1013 -recipient = 1000 -max_capacity = 4 -max_message_size = 524288 +# TODO: open an issue on zombienet repo +# for now, using setup scripts to build HRMP channels +#[[hrmp_channels]] +#sender = 1000 +#recipient = 1013 +#max_capacity = 8 +#max_message_size = 1048576 +# +#[[hrmp_channels]] +#sender = 1013 +#recipient = 1000 +#max_capacity = 8 +#max_message_size = 1048576 From 971a1bacaefc2ada1fda621ac38f113143fda093 Mon Sep 17 00:00:00 2001 From: Freddy Li Date: Mon, 8 Apr 2024 13:15:45 -0400 Subject: [PATCH 8/9] rename js to standalone in script folder --- scripts/{js => standalone}/execute_proposal_test.js | 0 scripts/{js => standalone}/package.json | 0 scripts/{js => standalone}/setup.js | 0 scripts/{js => standalone}/util.js | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename scripts/{js => standalone}/execute_proposal_test.js (100%) rename scripts/{js => standalone}/package.json (100%) rename scripts/{js => standalone}/setup.js (100%) rename scripts/{js => standalone}/util.js (100%) diff --git a/scripts/js/execute_proposal_test.js b/scripts/standalone/execute_proposal_test.js similarity index 100% rename from scripts/js/execute_proposal_test.js rename to scripts/standalone/execute_proposal_test.js diff --git a/scripts/js/package.json b/scripts/standalone/package.json similarity index 100% rename from scripts/js/package.json rename to scripts/standalone/package.json diff --git a/scripts/js/setup.js b/scripts/standalone/setup.js similarity index 100% rename from scripts/js/setup.js rename to scripts/standalone/setup.js diff --git a/scripts/js/util.js b/scripts/standalone/util.js similarity index 100% rename from scripts/js/util.js rename to scripts/standalone/util.js From ea9638206586e9c22cc8ff83dbc9a0317ccb4e49 Mon Sep 17 00:00:00 2001 From: Freddy Li Date: Mon, 15 Apr 2024 09:44:56 -0400 Subject: [PATCH 9/9] comment on the sovereignaccount transfer reason --- scripts/xcm/setup.js | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/xcm/setup.js b/scripts/xcm/setup.js index 0ec2712..58a6ac7 100644 --- a/scripts/xcm/setup.js +++ b/scripts/xcm/setup.js @@ -99,6 +99,7 @@ async function main() { // sovereignaccount of parachain 1013 on relaychain: const sovereignAccount1013 = "5Ec4AhPcMD9pfD1dC3vbyKXoZdZjigWthS9nEwGqaSJksLJv"; // transfer native asset to parachain sovereignaccount on relay chain + // this is for creating the HRMP channels bcs sovereignaccount will be the sender of the OpenHRMPChannel and AcceptHRMPChannel call on the relaychain await transferBalance(relayChainApi, sovereignAccount1000, bn1e12.mul(new BN(10)), true, sudo); // set balance to 10 native asset await transferBalance(relayChainApi, sovereignAccount1013, bn1e12.mul(new BN(10)), true, sudo); // set balance to 10 native asset