1234567891011121314151617import type {IKeyringPair} from '@polkadot/types/types';18import config from '../config.js';19import {itSub, expect, describeXCM, usingPlaygrounds, usingWestmintPlaygrounds, usingRelayPlaygrounds} from '../util/index.js';20import {XcmTestHelper} from './xcm.types';2122const STATEMINE_CHAIN = +(process.env.RELAY_WESTMINT_ID || 1000);23const UNIQUE_CHAIN = +(process.env.RELAY_OPAL_ID || 2095);2425const relayUrl = config.relayUrl;26const westmintUrl = config.westmintUrl;2728const STATEMINE_PALLET_INSTANCE = 50;29const USDT_ASSET_ID = 100;30const USDT_ASSET_METADATA_DECIMALS = 18;31const USDT_ASSET_METADATA_NAME = 'USDT';32const USDT_ASSET_METADATA_DESCRIPTION = 'USDT';33const USDT_ASSET_METADATA_MINIMAL_BALANCE = 1n;3435const RELAY_DECIMALS = 12;36const WESTMINT_DECIMALS = 12;3738const TRANSFER_AMOUNT = 1_000_000_000_000_000_000n;394041const USDT_ASSET_AMOUNT = 1_000_000_000_000_000_000_000n;4243const testHelper = new XcmTestHelper('opal');4445describeXCM('[XCM] Integration test: Exchanging USDT with Westmint', () => {46 let alice: IKeyringPair;47 let bob: IKeyringPair;4849 let balanceStmnBefore: bigint;50 let balanceStmnAfter: bigint;5152 let balanceOpalBefore: bigint;53 let balanceOpalAfter: bigint;54 let balanceOpalFinal: bigint;5556 let balanceBobBefore: bigint;57 let balanceBobAfter: bigint;58 let balanceBobFinal: bigint;5960 let balanceBobRelayTokenBefore: bigint;61 let balanceBobRelayTokenAfter: bigint;6263 let usdtCollectionId: number;64 let relayCollectionId: number;6566 before(async () => {67 await usingPlaygrounds(async (_helper, privateKey) => {68 alice = await privateKey('//Alice');69 bob = await privateKey('//Bob'); 7071 relayCollectionId = await testHelper.registerRelayNativeTokenOnUnique(alice);72 });7374 await usingWestmintPlaygrounds(westmintUrl, async (helper) => {75 const assetInfo = await helper.assets.assetInfo(USDT_ASSET_ID);76 if(assetInfo == null) {77 await helper.assets.create(78 alice,79 USDT_ASSET_ID,80 alice.address,81 USDT_ASSET_METADATA_MINIMAL_BALANCE,82 );83 await helper.assets.setMetadata(84 alice,85 USDT_ASSET_ID,86 USDT_ASSET_METADATA_NAME,87 USDT_ASSET_METADATA_DESCRIPTION,88 USDT_ASSET_METADATA_DECIMALS,89 );90 } else {91 console.log('The USDT asset is already registered on AssetHub');92 }9394 await helper.assets.mint(95 alice,96 USDT_ASSET_ID,97 alice.address,98 USDT_ASSET_AMOUNT,99 );100101 const sovereignFundingAmount = 3_500_000_000n;102103 104 const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(UNIQUE_CHAIN);105 await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, sovereignFundingAmount);106 });107108 await usingPlaygrounds(async (helper) => {109 const location = {110 parents: 1,111 interior: {X3: [112 {113 Parachain: STATEMINE_CHAIN,114 },115 {116 PalletInstance: STATEMINE_PALLET_INSTANCE,117 },118 {119 GeneralIndex: USDT_ASSET_ID,120 },121 ]},122 };123124 if(await helper.foreignAssets.foreignCollectionId(location) == null) {125 const tokenPrefix = USDT_ASSET_METADATA_NAME;126 await helper.getSudo().foreignAssets.register(alice, location, USDT_ASSET_METADATA_NAME, tokenPrefix, {Fungible: USDT_ASSET_METADATA_DECIMALS});127 } else {128 console.log('Foreign collection is already registered on Opal');129 }130131 balanceOpalBefore = await helper.balance.getSubstrate(alice.address);132 usdtCollectionId = await helper.foreignAssets.foreignCollectionId(location);133 });134135 136 await usingRelayPlaygrounds(relayUrl, async (helper) => {137 const destination = {138 V2: {139 parents: 0,140 interior: {X1: {141 Parachain: UNIQUE_CHAIN,142 },143 },144 }};145146 const beneficiary = {147 V2: {148 parents: 0,149 interior: {X1: {150 AccountId32: {151 network: 'Any',152 id: alice.addressRaw,153 },154 }},155 },156 };157158 const assets = {159 V2: [160 {161 id: {162 Concrete: {163 parents: 0,164 interior: 'Here',165 },166 },167 fun: {168 Fungible: 50_000_000_000_000_000n,169 },170 },171 ],172 };173174 const feeAssetItem = 0;175176 await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, 'Unlimited');177 });178179 });180181 itSub('Should connect and send USDT from Westmint to Opal', async ({helper}) => {182 await usingWestmintPlaygrounds(westmintUrl, async (helper) => {183 const dest = {184 V2: {185 parents: 1,186 interior: {X1: {187 Parachain: UNIQUE_CHAIN,188 },189 },190 }};191192 const beneficiary = {193 V2: {194 parents: 0,195 interior: {X1: {196 AccountId32: {197 network: 'Any',198 id: alice.addressRaw,199 },200 }},201 },202 };203204 const assets = {205 V2: [206 {207 id: {208 Concrete: {209 parents: 0,210 interior: {211 X2: [212 {213 PalletInstance: STATEMINE_PALLET_INSTANCE,214 },215 {216 GeneralIndex: USDT_ASSET_ID,217 },218 ]},219 },220 },221 fun: {222 Fungible: TRANSFER_AMOUNT,223 },224 },225 ],226 };227228 const feeAssetItem = 0;229230 balanceStmnBefore = await helper.balance.getSubstrate(alice.address);231 await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, 'Unlimited');232233 balanceStmnAfter = await helper.balance.getSubstrate(alice.address);234235 236 console.log(237 '[Westmint -> Opal] transaction fees on Westmint: %s WND',238 helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, WESTMINT_DECIMALS),239 );240 expect(balanceStmnBefore > balanceStmnAfter).to.be.true;241242 });243244 245 await helper.wait.newBlocks(3);246247 const free = await helper.ft.getBalance(usdtCollectionId, {Substrate: alice.address});248249 balanceOpalAfter = await helper.balance.getSubstrate(alice.address);250251 console.log(252 '[Westmint -> Opal] transaction fees on Opal: %s USDT',253 helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, USDT_ASSET_METADATA_DECIMALS),254 );255 console.log(256 '[Westmint -> Opal] transaction fees on Opal: %s OPL',257 helper.util.bigIntToDecimals(balanceOpalAfter - balanceOpalBefore),258 );259260 261 expect(free == TRANSFER_AMOUNT).to.be.true;262 263 expect(balanceOpalAfter == balanceOpalBefore).to.be.true;264 });265266 itSub('Should connect and send USDT from Unique to Statemine back', async ({helper}) => {267 const destination = {268 V2: {269 parents: 1,270 interior: {X2: [271 {272 Parachain: STATEMINE_CHAIN,273 },274 {275 AccountId32: {276 network: 'Any',277 id: alice.addressRaw,278 },279 },280 ]},281 },282 };283284 const currencies: [any, bigint][] = [285 [286 usdtCollectionId,287 TRANSFER_AMOUNT,288 ],289 [290 relayCollectionId,291 400_000_000_000_000n,292 ],293 ];294295 const feeItem = 1;296297 await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, 'Unlimited');298299 300 balanceOpalFinal = await helper.balance.getSubstrate(alice.address);301 expect(balanceOpalAfter > balanceOpalFinal).to.be.true;302303 await usingWestmintPlaygrounds(westmintUrl, async (helper) => {304 await helper.wait.newBlocks(3);305306 307 308 expect((await helper.assets.account(USDT_ASSET_ID, alice.address))! == USDT_ASSET_AMOUNT).to.be.true;309 });310 });311312 itSub('Should connect and send Relay token to Unique', async ({helper}) => {313 const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n;314315 balanceBobBefore = await helper.balance.getSubstrate(bob.address);316 balanceBobRelayTokenBefore = await helper.ft.getBalance(relayCollectionId, {Substrate: bob.address});317318 319 await usingRelayPlaygrounds(relayUrl, async (helper) => {320 const destination = {321 V2: {322 parents: 0,323 interior: {X1: {324 Parachain: UNIQUE_CHAIN,325 },326 },327 }};328329 const beneficiary = {330 V2: {331 parents: 0,332 interior: {X1: {333 AccountId32: {334 network: 'Any',335 id: bob.addressRaw,336 },337 }},338 },339 };340341 const assets = {342 V2: [343 {344 id: {345 Concrete: {346 parents: 0,347 interior: 'Here',348 },349 },350 fun: {351 Fungible: TRANSFER_AMOUNT_RELAY,352 },353 },354 ],355 };356357 const feeAssetItem = 0;358359 await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, 'Unlimited');360 });361362 await helper.wait.newBlocks(3);363364 balanceBobAfter = await helper.balance.getSubstrate(bob.address);365 balanceBobRelayTokenAfter = await helper.ft.getBalance(relayCollectionId, {Substrate: bob.address});366367 const wndFee = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;368 console.log(369 'Relay (Westend) to Opal transaction fees: %s OPL',370 helper.util.bigIntToDecimals(balanceBobAfter - balanceBobBefore),371 );372 console.log(373 'Relay (Westend) to Opal transaction fees: %s WND',374 helper.util.bigIntToDecimals(wndFee, WESTMINT_DECIMALS),375 );376 expect(balanceBobBefore == balanceBobAfter).to.be.true;377 expect(balanceBobRelayTokenBefore < balanceBobRelayTokenAfter).to.be.true;378 });379380 itSub('Should connect and send Relay token back', async ({helper}) => {381 let relayTokenBalanceBefore: bigint;382 let relayTokenBalanceAfter: bigint;383 await usingRelayPlaygrounds(relayUrl, async (helper) => {384 relayTokenBalanceBefore = await helper.balance.getSubstrate(bob.address);385 });386387 const destination = {388 V2: {389 parents: 1,390 interior: {391 X1:{392 AccountId32: {393 network: 'Any',394 id: bob.addressRaw,395 },396 },397 },398 },399 };400401 const currencies: any = [402 [403 relayCollectionId,404 50_000_000_000_000_000n,405 ],406 ];407408 const feeItem = 0;409410 await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, 'Unlimited');411412 balanceBobFinal = await helper.balance.getSubstrate(bob.address);413 console.log('[Opal -> Relay (Westend)] transaction fees: %s OPL', helper.util.bigIntToDecimals(balanceBobAfter - balanceBobFinal));414415 await usingRelayPlaygrounds(relayUrl, async (helper) => {416 await helper.wait.newBlocks(10);417 relayTokenBalanceAfter = await helper.balance.getSubstrate(bob.address);418419 const diff = relayTokenBalanceAfter - relayTokenBalanceBefore;420 console.log('[Opal -> Relay (Westend)] actually delivered: %s WND', helper.util.bigIntToDecimals(diff, RELAY_DECIMALS));421 expect(diff > 0, 'Relay tokens was not delivered back').to.be.true;422 });423 });424});