1234567891011121314151617import {IKeyringPair} from '@polkadot/types/types';18import config from '../config';19import {itSub, expect, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingRelayPlaygrounds, usingMoonbeamPlaygrounds, usingStatemintPlaygrounds, usingAstarPlaygrounds, usingPolkadexPlaygrounds} from '../util';20import {DevUniqueHelper, Event} from '../util/playgrounds/unique.dev';21import {hexToString, nToBigInt} from '@polkadot/util';2223const UNIQUE_CHAIN = +(process.env.RELAY_UNIQUE_ID || 2037);24const STATEMINT_CHAIN = +(process.env.RELAY_STATEMINT_ID || 1000);25const ACALA_CHAIN = +(process.env.RELAY_ACALA_ID || 2000);26const MOONBEAM_CHAIN = +(process.env.RELAY_MOONBEAM_ID || 2004);27const ASTAR_CHAIN = +(process.env.RELAY_ASTAR_ID || 2006);28const POLKADEX_CHAIN = +(process.env.RELAY_POLKADEX_ID || 2040);2930const STATEMINT_PALLET_INSTANCE = 50;3132const relayUrl = config.relayUrl;33const statemintUrl = config.statemintUrl;34const acalaUrl = config.acalaUrl;35const moonbeamUrl = config.moonbeamUrl;36const astarUrl = config.astarUrl;37const polkadexUrl = config.polkadexUrl;3839const RELAY_DECIMALS = 12;40const STATEMINT_DECIMALS = 12;41const ACALA_DECIMALS = 12;42const ASTAR_DECIMALS = 18n;43const UNQ_DECIMALS = 18n;4445const TRANSFER_AMOUNT = 2000000000000000000000000n;4647const FUNDING_AMOUNT = 3_500_000_0000_000_000n;4849const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n;5051const USDT_ASSET_ID = 100;52const USDT_ASSET_METADATA_DECIMALS = 18;53const USDT_ASSET_METADATA_NAME = 'USDT';54const USDT_ASSET_METADATA_DESCRIPTION = 'USDT';55const USDT_ASSET_METADATA_MINIMAL_BALANCE = 1n;56const USDT_ASSET_AMOUNT = 10_000_000_000_000_000_000_000_000n;5758const SAFE_XCM_VERSION = 2;59const maxWaitBlocks = 6;6061const uniqueMultilocation = {62 V2: {63 parents: 1,64 interior: {65 X1: {66 Parachain: UNIQUE_CHAIN,67 },68 },69 },70};7172const expectFailedToTransact = async (helper: DevUniqueHelper, messageSent: any) => {73 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash74 && event.outcome.isFailedToTransactAsset);75};76const expectUntrustedReserveLocationFail = async (helper: DevUniqueHelper, messageSent: any) => {77 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash78 && event.outcome.isUntrustedReserveLocation);79};80describeXCM('[XCM] Integration test: Exchanging USDT with Statemint', () => {81 let alice: IKeyringPair;82 let bob: IKeyringPair;8384 let balanceStmnBefore: bigint;85 let balanceStmnAfter: bigint;8687 let balanceUniqueBefore: bigint;88 let balanceUniqueAfter: bigint;89 let balanceUniqueFinal: bigint;9091 let balanceBobBefore: bigint;92 let balanceBobAfter: bigint;93 let balanceBobFinal: bigint;9495 let balanceBobRelayTokenBefore: bigint;96 let balanceBobRelayTokenAfter: bigint;979899 before(async () => {100 await usingPlaygrounds(async (helper, privateKey) => {101 alice = await privateKey('//Alice');102 bob = await privateKey('//Bob'); 103104 105 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);106 });107108 await usingRelayPlaygrounds(relayUrl, async (helper) => {109 110 await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, alice.addressRaw, FUNDING_AMOUNT);111 await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, bob.addressRaw, FUNDING_AMOUNT);112 });113114 await usingStatemintPlaygrounds(statemintUrl, async (helper) => {115 const sovereignFundingAmount = 3_500_000_000n;116117 await helper.assets.create(118 alice,119 USDT_ASSET_ID,120 alice.address,121 USDT_ASSET_METADATA_MINIMAL_BALANCE,122 );123 await helper.assets.setMetadata(124 alice,125 USDT_ASSET_ID,126 USDT_ASSET_METADATA_NAME,127 USDT_ASSET_METADATA_DESCRIPTION,128 USDT_ASSET_METADATA_DECIMALS,129 );130 await helper.assets.mint(131 alice,132 USDT_ASSET_ID,133 alice.address,134 USDT_ASSET_AMOUNT,135 );136137 138 139 140 const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(UNIQUE_CHAIN);141 await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, sovereignFundingAmount);142 });143144145 await usingPlaygrounds(async (helper) => {146 const location = {147 V2: {148 parents: 1,149 interior: {X3: [150 {151 Parachain: STATEMINT_CHAIN,152 },153 {154 PalletInstance: STATEMINT_PALLET_INSTANCE,155 },156 {157 GeneralIndex: USDT_ASSET_ID,158 },159 ]},160 },161 };162163 const metadata =164 {165 name: USDT_ASSET_ID,166 symbol: USDT_ASSET_METADATA_NAME,167 decimals: USDT_ASSET_METADATA_DECIMALS,168 minimalBalance: USDT_ASSET_METADATA_MINIMAL_BALANCE,169 };170 await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata);171 balanceUniqueBefore = await helper.balance.getSubstrate(alice.address);172 });173174175 176 177 await usingRelayPlaygrounds(relayUrl, async (helper) => {178 const destination = {179 V2: {180 parents: 0,181 interior: {X1: {182 Parachain: UNIQUE_CHAIN,183 },184 },185 }};186187 const beneficiary = {188 V2: {189 parents: 0,190 interior: {X1: {191 AccountId32: {192 network: 'Any',193 id: alice.addressRaw,194 },195 }},196 },197 };198199 const assets = {200 V2: [201 {202 id: {203 Concrete: {204 parents: 0,205 interior: 'Here',206 },207 },208 fun: {209 Fungible: TRANSFER_AMOUNT_RELAY,210 },211 },212 ],213 };214215 const feeAssetItem = 0;216217 await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, 'Unlimited');218 });219220 });221222 itSub('Should connect and send USDT from Statemint to Unique', async ({helper}) => {223 await usingStatemintPlaygrounds(statemintUrl, async (helper) => {224 const dest = {225 V2: {226 parents: 1,227 interior: {X1: {228 Parachain: UNIQUE_CHAIN,229 },230 },231 }};232233 const beneficiary = {234 V2: {235 parents: 0,236 interior: {X1: {237 AccountId32: {238 network: 'Any',239 id: alice.addressRaw,240 },241 }},242 },243 };244245 const assets = {246 V2: [247 {248 id: {249 Concrete: {250 parents: 0,251 interior: {252 X2: [253 {254 PalletInstance: STATEMINT_PALLET_INSTANCE,255 },256 {257 GeneralIndex: USDT_ASSET_ID,258 },259 ]},260 },261 },262 fun: {263 Fungible: TRANSFER_AMOUNT,264 },265 },266 ],267 };268269 const feeAssetItem = 0;270271 balanceStmnBefore = await helper.balance.getSubstrate(alice.address);272 await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, 'Unlimited');273274 balanceStmnAfter = await helper.balance.getSubstrate(alice.address);275276 277 console.log(278 '[Statemint -> Unique] transaction fees on Statemint: %s WND',279 helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, STATEMINT_DECIMALS),280 );281 expect(balanceStmnBefore > balanceStmnAfter).to.be.true;282283 });284285286 287 await helper.wait.newBlocks(3);288289 290 const free = await helper.ft.getBalance(1, {Substrate: alice.address});291292 balanceUniqueAfter = await helper.balance.getSubstrate(alice.address);293294 console.log(295 '[Statemint -> Unique] transaction fees on Unique: %s USDT',296 helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, USDT_ASSET_METADATA_DECIMALS),297 );298 console.log(299 '[Statemint -> Unique] transaction fees on Unique: %s UNQ',300 helper.util.bigIntToDecimals(balanceUniqueAfter - balanceUniqueBefore),301 );302 303 expect(free).to.be.equal(TRANSFER_AMOUNT);304 305 expect(balanceUniqueAfter == balanceUniqueBefore).to.be.true;306 });307308 itSub('Should connect and send USDT from Unique to Statemint back', async ({helper}) => {309 const destination = {310 V2: {311 parents: 1,312 interior: {X2: [313 {314 Parachain: STATEMINT_CHAIN,315 },316 {317 AccountId32: {318 network: 'Any',319 id: alice.addressRaw,320 },321 },322 ]},323 },324 };325326 const relayFee = 400_000_000_000_000n;327 const currencies: [any, bigint][] = [328 [329 {330 ForeignAssetId: 0,331 },332 TRANSFER_AMOUNT,333 ],334 [335 {336 NativeAssetId: 'Parent',337 },338 relayFee,339 ],340 ];341342 const feeItem = 1;343344 await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, 'Unlimited');345346 347 balanceUniqueFinal = await helper.balance.getSubstrate(alice.address);348 console.log('[Unique -> Statemint] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(balanceUniqueAfter - balanceUniqueFinal));349 expect(balanceUniqueAfter > balanceUniqueFinal).to.be.true;350351 await usingStatemintPlaygrounds(statemintUrl, async (helper) => {352 await helper.wait.newBlocks(3);353354 355 356 expect((await helper.assets.account(USDT_ASSET_ID, alice.address))! == USDT_ASSET_AMOUNT).to.be.true;357 });358 });359360 itSub('Should connect and send Relay token to Unique', async ({helper}) => {361 balanceBobBefore = await helper.balance.getSubstrate(bob.address);362 balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});363364 await usingRelayPlaygrounds(relayUrl, async (helper) => {365 const destination = {366 V2: {367 parents: 0,368 interior: {X1: {369 Parachain: UNIQUE_CHAIN,370 },371 },372 }};373374 const beneficiary = {375 V2: {376 parents: 0,377 interior: {X1: {378 AccountId32: {379 network: 'Any',380 id: bob.addressRaw,381 },382 }},383 },384 };385386 const assets = {387 V2: [388 {389 id: {390 Concrete: {391 parents: 0,392 interior: 'Here',393 },394 },395 fun: {396 Fungible: TRANSFER_AMOUNT_RELAY,397 },398 },399 ],400 };401402 const feeAssetItem = 0;403404 await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, 'Unlimited');405 });406407 await helper.wait.newBlocks(3);408409 balanceBobAfter = await helper.balance.getSubstrate(bob.address);410 balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});411412 const wndFeeOnUnique = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;413 const wndDiffOnUnique = balanceBobRelayTokenAfter - balanceBobRelayTokenBefore;414 console.log(415 '[Relay (Westend) -> Unique] transaction fees: %s UNQ',416 helper.util.bigIntToDecimals(balanceBobAfter - balanceBobBefore),417 );418 console.log(419 '[Relay (Westend) -> Unique] transaction fees: %s WND',420 helper.util.bigIntToDecimals(wndFeeOnUnique, STATEMINT_DECIMALS),421 );422 console.log('[Relay (Westend) -> Unique] actually delivered: %s WND', wndDiffOnUnique);423 expect(wndFeeOnUnique == 0n, 'No incoming WND fees should be taken').to.be.true;424 expect(balanceBobBefore == balanceBobAfter, 'No incoming UNQ fees should be taken').to.be.true;425 });426427 itSub('Should connect and send Relay token back', async ({helper}) => {428 let relayTokenBalanceBefore: bigint;429 let relayTokenBalanceAfter: bigint;430 await usingRelayPlaygrounds(relayUrl, async (helper) => {431 relayTokenBalanceBefore = await helper.balance.getSubstrate(bob.address);432 });433434 const destination = {435 V2: {436 parents: 1,437 interior: {438 X1:{439 AccountId32: {440 network: 'Any',441 id: bob.addressRaw,442 },443 },444 },445 },446 };447448 const currencies: any = [449 [450 {451 NativeAssetId: 'Parent',452 },453 TRANSFER_AMOUNT_RELAY,454 ],455 ];456457 const feeItem = 0;458459 await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, 'Unlimited');460461 balanceBobFinal = await helper.balance.getSubstrate(bob.address);462 console.log('[Unique -> Relay (Westend)] transaction fees: %s UNQ', helper.util.bigIntToDecimals(balanceBobAfter - balanceBobFinal));463464 await usingRelayPlaygrounds(relayUrl, async (helper) => {465 await helper.wait.newBlocks(10);466 relayTokenBalanceAfter = await helper.balance.getSubstrate(bob.address);467468 const diff = relayTokenBalanceAfter - relayTokenBalanceBefore;469 console.log('[Unique -> Relay (Westend)] actually delivered: %s WND', helper.util.bigIntToDecimals(diff, RELAY_DECIMALS));470 expect(diff > 0, 'Relay tokens was not delivered back').to.be.true;471 });472 });473});474475describeXCM('[XCM] Integration test: Exchanging tokens with Acala', () => {476 let alice: IKeyringPair;477 let randomAccount: IKeyringPair;478479 let balanceUniqueTokenInit: bigint;480 let balanceUniqueTokenMiddle: bigint;481 let balanceUniqueTokenFinal: bigint;482 let balanceAcalaTokenInit: bigint;483 let balanceAcalaTokenMiddle: bigint;484 let balanceAcalaTokenFinal: bigint;485 let balanceUniqueForeignTokenInit: bigint;486 let balanceUniqueForeignTokenMiddle: bigint;487 let balanceUniqueForeignTokenFinal: bigint;488489 490 491 492 const expectedAcalaIncomeFee = 2000000000000000000n - 1898970000000000000n;493 const acalaEps = 8n * 10n ** 16n;494495 let acalaBackwardTransferAmount: bigint;496497 before(async () => {498 await usingPlaygrounds(async (helper, privateKey) => {499 alice = await privateKey('//Alice');500 [randomAccount] = await helper.arrange.createAccounts([0n], alice);501502 503 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);504 });505506 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {507 const destination = {508 V2: {509 parents: 1,510 interior: {511 X1: {512 Parachain: UNIQUE_CHAIN,513 },514 },515 },516 };517518 const metadata = {519 name: 'Unique Network',520 symbol: 'UNQ',521 decimals: 18,522 minimalBalance: 1250000000000000000n,523 };524 const assets = (await (helper.callRpc('api.query.assetRegistry.assetMetadatas.entries'))).map(([_k, v] : [any, any]) =>525 hexToString(v.toJSON()['symbol'])) as string[];526527 if(!assets.includes('UNQ')) {528 await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);529 } else {530 console.log('UNQ token already registered on Acala assetRegistry pallet');531 }532 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);533 balanceAcalaTokenInit = await helper.balance.getSubstrate(randomAccount.address);534 balanceUniqueForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});535 });536537 await usingPlaygrounds(async (helper) => {538 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);539 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);540 });541 });542543 itSub('Should connect and send UNQ to Acala', async ({helper}) => {544545 const destination = {546 V2: {547 parents: 1,548 interior: {549 X1: {550 Parachain: ACALA_CHAIN,551 },552 },553 },554 };555556 const beneficiary = {557 V2: {558 parents: 0,559 interior: {560 X1: {561 AccountId32: {562 network: 'Any',563 id: randomAccount.addressRaw,564 },565 },566 },567 },568 };569570 const assets = {571 V2: [572 {573 id: {574 Concrete: {575 parents: 0,576 interior: 'Here',577 },578 },579 fun: {580 Fungible: TRANSFER_AMOUNT,581 },582 },583 ],584 };585586 const feeAssetItem = 0;587588 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');589 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);590591 const unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;592 console.log('[Unique -> Acala] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));593 expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;594595 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {596 await helper.wait.newBlocks(3);597598 balanceUniqueForeignTokenMiddle = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});599 balanceAcalaTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);600601 const acaFees = balanceAcalaTokenInit - balanceAcalaTokenMiddle;602 const unqIncomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenInit;603 acalaBackwardTransferAmount = unqIncomeTransfer;604605 const acaUnqFees = TRANSFER_AMOUNT - unqIncomeTransfer;606607 console.log(608 '[Unique -> Acala] transaction fees on Acala: %s ACA',609 helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),610 );611 console.log(612 '[Unique -> Acala] transaction fees on Acala: %s UNQ',613 helper.util.bigIntToDecimals(acaUnqFees),614 );615 console.log('[Unique -> Acala] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));616 expect(acaFees == 0n).to.be.true;617618 const bigintAbs = (n: bigint) => (n < 0n) ? -n : n;619620 expect(621 bigintAbs(acaUnqFees - expectedAcalaIncomeFee) < acalaEps,622 'Acala took different income fee, check the Acala foreign asset config',623 ).to.be.true;624 });625 });626627 itSub('Should connect to Acala and send UNQ back', async ({helper}) => {628 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {629 const destination = {630 V2: {631 parents: 1,632 interior: {633 X2: [634 {Parachain: UNIQUE_CHAIN},635 {636 AccountId32: {637 network: 'Any',638 id: randomAccount.addressRaw,639 },640 },641 ],642 },643 },644 };645646 const id = {647 ForeignAsset: 0,648 };649650 await helper.xTokens.transfer(randomAccount, id, acalaBackwardTransferAmount, destination, 'Unlimited');651 balanceAcalaTokenFinal = await helper.balance.getSubstrate(randomAccount.address);652 balanceUniqueForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);653654 const acaFees = balanceAcalaTokenMiddle - balanceAcalaTokenFinal;655 const unqOutcomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenFinal;656657 console.log(658 '[Acala -> Unique] transaction fees on Acala: %s ACA',659 helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),660 );661 console.log('[Acala -> Unique] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));662663 expect(acaFees > 0, 'Negative fees ACA, looks like nothing was transferred').to.be.true;664 expect(unqOutcomeTransfer == acalaBackwardTransferAmount).to.be.true;665 });666667 await helper.wait.newBlocks(3);668669 balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address);670 const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;671 expect(actuallyDelivered > 0).to.be.true;672673 console.log('[Acala -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));674675 const unqFees = acalaBackwardTransferAmount - actuallyDelivered;676 console.log('[Acala -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));677 expect(unqFees == 0n).to.be.true;678 });679680 itSub('Acala can send only up to its balance', async ({helper}) => {681 682 const acalaBalance = 10000n * (10n ** UNQ_DECIMALS);683 const acalaSovereignAccount = helper.address.paraSiblingSovereignAccount(ACALA_CHAIN);684 await helper.getSudo().balance.setBalanceSubstrate(alice, acalaSovereignAccount, acalaBalance);685686 const moreThanAcalaHas = acalaBalance * 2n;687688 let targetAccountBalance = 0n;689 const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);690691 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(692 targetAccount.addressRaw,693 {694 Concrete: {695 parents: 0,696 interior: 'Here',697 },698 },699 moreThanAcalaHas,700 );701702 let maliciousXcmProgramSent: any;703 const maxWaitBlocks = 3;704705 706 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {707 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);708709 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);710 });711712 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash713 && event.outcome.isFailedToTransactAsset);714715 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);716 expect(targetAccountBalance).to.be.equal(0n);717718 719 const validTransferAmount = acalaBalance / 2n;720 const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(721 targetAccount.addressRaw,722 {723 Concrete: {724 parents: 0,725 interior: 'Here',726 },727 },728 validTransferAmount,729 );730731 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {732 await helper.getSudo().xcm.send(alice, uniqueMultilocation, validXcmProgram);733 });734735 await helper.wait.newBlocks(maxWaitBlocks);736737 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);738 expect(targetAccountBalance).to.be.equal(validTransferAmount);739 });740741 itSub('Should not accept reserve transfer of UNQ from Acala', async ({helper}) => {742 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);743 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);744745 const uniqueMultilocation = {746 V2: {747 parents: 1,748 interior: {749 X1: {750 Parachain: UNIQUE_CHAIN,751 },752 },753 },754 };755756 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(757 targetAccount.addressRaw,758 {759 Concrete: {760 parents: 1,761 interior: {762 X1: {763 Parachain: UNIQUE_CHAIN,764 },765 },766 },767 },768 testAmount,769 );770771 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(772 targetAccount.addressRaw,773 {774 Concrete: {775 parents: 0,776 interior: 'Here',777 },778 },779 testAmount,780 );781782 let maliciousXcmProgramFullIdSent: any;783 let maliciousXcmProgramHereIdSent: any;784 const maxWaitBlocks = 3;785786 787 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {788 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);789790 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);791 });792793 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash794 && event.outcome.isUntrustedReserveLocation);795796 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);797 expect(accountBalance).to.be.equal(0n);798799 800 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {801 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);802803 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);804 });805806 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash807 && event.outcome.isUntrustedReserveLocation);808809 accountBalance = await helper.balance.getSubstrate(targetAccount.address);810 expect(accountBalance).to.be.equal(0n);811 });812});813814describeXCM('[XCM] Integration test: Exchanging tokens with Polkadex', () => {815 let alice: IKeyringPair;816 let randomAccount: IKeyringPair;817 let unqFees: bigint;818 let balanceUniqueTokenInit: bigint;819 let balanceUniqueTokenMiddle: bigint;820 let balanceUniqueTokenFinal: bigint;821 const maxWaitBlocks = 6;822823 const uniqueAssetId = {824 Concrete: {825 parents: 1,826 interior: {827 X1: {828 Parachain: UNIQUE_CHAIN,829 },830 },831 },832 };833834 before(async () => {835 await usingPlaygrounds(async (helper, privateKey) => {836 alice = await privateKey('//Alice');837 [randomAccount] = await helper.arrange.createAccounts([0n], alice);838839 840 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);841 });842843 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {844 const isWhitelisted = ((await helper.callRpc('api.query.xcmHelper.whitelistedTokens', []))845 .toJSON() as [])846 .map(nToBigInt).length != 0;847 848849850851852853 if(!isWhitelisted) {854 await helper.getSudo().xcmHelper.whitelistToken(alice, uniqueAssetId);855 }856857 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);858 });859860 await usingPlaygrounds(async (helper) => {861 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);862 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);863 });864 });865866 itSub('Should connect and send UNQ to Polkadex', async ({helper}) => {867868 const destination = {869 V2: {870 parents: 1,871 interior: {872 X1: {873 Parachain: POLKADEX_CHAIN,874 },875 },876 },877 };878879 const beneficiary = {880 V2: {881 parents: 0,882 interior: {883 X1: {884 AccountId32: {885 network: 'Any',886 id: randomAccount.addressRaw,887 },888 },889 },890 },891 };892893 const assets = {894 V2: [895 {896 id: {897 Concrete: {898 parents: 0,899 interior: 'Here',900 },901 },902 fun: {903 Fungible: TRANSFER_AMOUNT,904 },905 },906 ],907 };908909 const feeAssetItem = 0;910911 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');912 const messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);913 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);914915 unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;916 console.log('[Unique -> Polkadex] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));917 expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;918919 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {920 921922923924925926927928929930 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash);931 });932 });933934935 itSub('Should connect to Polkadex and send UNQ back', async ({helper}) => {936937 const xcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(938 randomAccount.addressRaw,939 {940 Concrete: {941 parents: 1,942 interior: {943 X1: {Parachain: UNIQUE_CHAIN},944 },945 },946 },947 TRANSFER_AMOUNT,948 );949950 let xcmProgramSent: any;951952953 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {954 await helper.getSudo().xcm.send(alice, uniqueMultilocation, xcmProgram);955956 xcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);957 });958959 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Success, event => event.messageHash == xcmProgramSent.messageHash);960961 balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address);962963 expect(balanceUniqueTokenFinal).to.be.equal(balanceUniqueTokenInit - unqFees);964 });965966 itSub('Polkadex can send only up to its balance', async ({helper}) => {967 const polkadexBalance = 10000n * (10n ** UNQ_DECIMALS);968 const polkadexSovereignAccount = helper.address.paraSiblingSovereignAccount(POLKADEX_CHAIN);969 await helper.getSudo().balance.setBalanceSubstrate(alice, polkadexSovereignAccount, polkadexBalance);970 const moreThanPolkadexHas = 2n * polkadexBalance;971972 const targetAccount = helper.arrange.createEmptyAccount();973974 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(975 targetAccount.addressRaw,976 {977 Concrete: {978 parents: 0,979 interior: 'Here',980 },981 },982 moreThanPolkadexHas,983 );984985 let maliciousXcmProgramSent: any;986987988 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {989 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);990991 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);992 });993994 await expectFailedToTransact(helper, maliciousXcmProgramSent);995996 const targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);997 expect(targetAccountBalance).to.be.equal(0n);998 });9991000 itSub('Should not accept reserve transfer of UNQ from Polkadex', async ({helper}) => {1001 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1002 const targetAccount = helper.arrange.createEmptyAccount();10031004 const uniqueMultilocation = {1005 V2: {1006 parents: 1,1007 interior: {1008 X1: {1009 Parachain: UNIQUE_CHAIN,1010 },1011 },1012 },1013 };10141015 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1016 targetAccount.addressRaw,1017 {1018 Concrete: {1019 parents: 1,1020 interior: {1021 X1: {1022 Parachain: UNIQUE_CHAIN,1023 },1024 },1025 },1026 },1027 testAmount,1028 );10291030 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1031 targetAccount.addressRaw,1032 {1033 Concrete: {1034 parents: 0,1035 interior: 'Here',1036 },1037 },1038 testAmount,1039 );10401041 let maliciousXcmProgramFullIdSent: any;1042 let maliciousXcmProgramHereIdSent: any;1043 const maxWaitBlocks = 3;10441045 1046 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {1047 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);10481049 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1050 });10511052 await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramFullIdSent);10531054 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1055 expect(accountBalance).to.be.equal(0n);10561057 1058 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {1059 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);10601061 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1062 });10631064 await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramHereIdSent);10651066 accountBalance = await helper.balance.getSubstrate(targetAccount.address);1067 expect(accountBalance).to.be.equal(0n);1068 });1069});1070107110721073describeXCM('[XCM] Integration test: Unique rejects non-native tokens', () => {1074 let alice: IKeyringPair;1075 let alith: IKeyringPair;10761077 const testAmount = 100_000_000_000n;1078 let uniqueParachainJunction;1079 let uniqueAccountJunction;10801081 let uniqueParachainMultilocation: any;1082 let uniqueAccountMultilocation: any;1083 let uniqueCombinedMultilocation: any;1084 let uniqueCombinedMultilocationAcala: any; 10851086 let messageSent: any;10871088 const maxWaitBlocks = 3;10891090 before(async () => {1091 await usingPlaygrounds(async (helper, privateKey) => {1092 alice = await privateKey('//Alice');10931094 uniqueParachainJunction = {Parachain: UNIQUE_CHAIN};1095 uniqueAccountJunction = {1096 AccountId32: {1097 network: 'Any',1098 id: alice.addressRaw,1099 },1100 };11011102 uniqueParachainMultilocation = {1103 V2: {1104 parents: 1,1105 interior: {1106 X1: uniqueParachainJunction,1107 },1108 },1109 };11101111 uniqueAccountMultilocation = {1112 V2: {1113 parents: 0,1114 interior: {1115 X1: uniqueAccountJunction,1116 },1117 },1118 };11191120 uniqueCombinedMultilocation = {1121 V2: {1122 parents: 1,1123 interior: {1124 X2: [uniqueParachainJunction, uniqueAccountJunction],1125 },1126 },1127 };11281129 uniqueCombinedMultilocationAcala = {1130 V2: {1131 parents: 1,1132 interior: {1133 X2: [uniqueParachainJunction, uniqueAccountJunction],1134 },1135 },1136 };11371138 1139 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1140 });11411142 1143 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1144 alith = helper.account.alithAccount();1145 });1146 });1147114811491150 itSub('Unique rejects ACA tokens from Acala', async ({helper}) => {1151 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {1152 const id = {1153 Token: 'ACA',1154 };1155 const destination = uniqueCombinedMultilocationAcala;1156 await helper.xTokens.transfer(alice, id, testAmount, destination, 'Unlimited');11571158 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1159 });11601161 await expectFailedToTransact(helper, messageSent);1162 });11631164 itSub('Unique rejects GLMR tokens from Moonbeam', async ({helper}) => {1165 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1166 const id = 'SelfReserve';1167 const destination = uniqueCombinedMultilocation;1168 await helper.xTokens.transfer(alith, id, testAmount, destination, 'Unlimited');11691170 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1171 });11721173 await expectFailedToTransact(helper, messageSent);1174 });11751176 itSub('Unique rejects ASTR tokens from Astar', async ({helper}) => {1177 await usingAstarPlaygrounds(astarUrl, async (helper) => {1178 const destinationParachain = uniqueParachainMultilocation;1179 const beneficiary = uniqueAccountMultilocation;1180 const assets = {1181 V2: [{1182 id: {1183 Concrete: {1184 parents: 0,1185 interior: 'Here',1186 },1187 },1188 fun: {1189 Fungible: testAmount,1190 },1191 }],1192 };1193 const feeAssetItem = 0;11941195 await helper.executeExtrinsic(alice, 'api.tx.polkadotXcm.reserveWithdrawAssets', [1196 destinationParachain,1197 beneficiary,1198 assets,1199 feeAssetItem,1200 ]);12011202 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1203 });12041205 await expectFailedToTransact(helper, messageSent);1206 });12071208 itSub('Unique rejects PDX tokens from Polkadex', async ({helper}) => {12091210 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1211 helper.arrange.createEmptyAccount().addressRaw,1212 {1213 Concrete: {1214 parents: 1,1215 interior: {1216 X1: {1217 Parachain: POLKADEX_CHAIN,1218 },1219 },1220 },1221 },1222 testAmount,1223 );12241225 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {1226 await helper.getSudo().xcm.send(alice, uniqueParachainMultilocation, maliciousXcmProgramFullId);1227 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1228 });12291230 await expectFailedToTransact(helper, messageSent);1231 });1232});12331234describeXCM('[XCM] Integration test: Exchanging UNQ with Moonbeam', () => {1235 1236 let alice: IKeyringPair;1237 let uniqueAssetLocation;12381239 let randomAccountUnique: IKeyringPair;1240 let randomAccountMoonbeam: IKeyringPair;12411242 1243 let assetId: string;12441245 const uniqueAssetMetadata = {1246 name: 'xcUnique',1247 symbol: 'xcUNQ',1248 decimals: 18,1249 isFrozen: false,1250 minimalBalance: 1n,1251 };12521253 let balanceUniqueTokenInit: bigint;1254 let balanceUniqueTokenMiddle: bigint;1255 let balanceUniqueTokenFinal: bigint;1256 let balanceForeignUnqTokenInit: bigint;1257 let balanceForeignUnqTokenMiddle: bigint;1258 let balanceForeignUnqTokenFinal: bigint;1259 let balanceGlmrTokenInit: bigint;1260 let balanceGlmrTokenMiddle: bigint;1261 let balanceGlmrTokenFinal: bigint;12621263 before(async () => {1264 await usingPlaygrounds(async (helper, privateKey) => {1265 alice = await privateKey('//Alice');1266 [randomAccountUnique] = await helper.arrange.createAccounts([0n], alice);12671268 balanceForeignUnqTokenInit = 0n;12691270 1271 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1272 });12731274 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1275 const alithAccount = helper.account.alithAccount();1276 const baltatharAccount = helper.account.baltatharAccount();1277 const dorothyAccount = helper.account.dorothyAccount();12781279 randomAccountMoonbeam = helper.account.create();12801281 1282 console.log('Sponsoring Dorothy.......');1283 await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);1284 console.log('Sponsoring Dorothy.......DONE');1285 12861287 uniqueAssetLocation = {1288 XCM: {1289 parents: 1,1290 interior: {X1: {Parachain: UNIQUE_CHAIN}},1291 },1292 };1293 const existentialDeposit = 1n;1294 const isSufficient = true;1295 const unitsPerSecond = 1n;1296 const numAssetsWeightHint = 0;12971298 if((await helper.assetManager.assetTypeId(uniqueAssetLocation)).toJSON()) {1299 console.log('Unique asset is already registered on MoonBeam');1300 } else {1301 const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({1302 location: uniqueAssetLocation,1303 metadata: uniqueAssetMetadata,1304 existentialDeposit,1305 isSufficient,1306 unitsPerSecond,1307 numAssetsWeightHint,1308 });13091310 console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);13111312 await helper.fastDemocracy.executeProposal('register UNQ foreign asset', encodedProposal);1313 }13141315 1316 console.log('Acquire Unique AssetId Info on Moonbeam.......');13171318 assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString();1319 console.log('UNQ asset ID is %s', assetId);1320 console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');1321 13221323 1324 console.log('Sponsoring random Account.......');1325 await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);1326 console.log('Sponsoring random Account.......DONE');1327 13281329 balanceGlmrTokenInit = await helper.balance.getEthereum(randomAccountMoonbeam.address);1330 });13311332 await usingPlaygrounds(async (helper) => {1333 await helper.balance.transferToSubstrate(alice, randomAccountUnique.address, 10n * TRANSFER_AMOUNT);1334 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccountUnique.address);1335 });1336 });13371338 itSub('Should connect and send UNQ to Moonbeam', async ({helper}) => {1339 const currencyId = {1340 NativeAssetId: 'Here',1341 };1342 const dest = {1343 V2: {1344 parents: 1,1345 interior: {1346 X2: [1347 {Parachain: MOONBEAM_CHAIN},1348 {AccountKey20: {network: 'Any', key: randomAccountMoonbeam.address}},1349 ],1350 },1351 },1352 };1353 const amount = TRANSFER_AMOUNT;13541355 await helper.xTokens.transfer(randomAccountUnique, currencyId, amount, dest, 'Unlimited');13561357 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccountUnique.address);1358 expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true;13591360 const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;1361 console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(transactionFees));1362 expect(transactionFees > 0, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;13631364 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1365 await helper.wait.newBlocks(3);1366 balanceGlmrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonbeam.address);13671368 const glmrFees = balanceGlmrTokenInit - balanceGlmrTokenMiddle;1369 console.log('[Unique -> Moonbeam] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));1370 expect(glmrFees == 0n).to.be.true;13711372 balanceForeignUnqTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonbeam.address))!;13731374 const unqIncomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenInit;1375 console.log('[Unique -> Moonbeam] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));1376 expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;1377 });1378 });13791380 itSub('Should connect to Moonbeam and send UNQ back', async ({helper}) => {1381 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1382 const asset = {1383 V2: {1384 id: {1385 Concrete: {1386 parents: 1,1387 interior: {1388 X1: {Parachain: UNIQUE_CHAIN},1389 },1390 },1391 },1392 fun: {1393 Fungible: TRANSFER_AMOUNT,1394 },1395 },1396 };1397 const destination = {1398 V2: {1399 parents: 1,1400 interior: {1401 X2: [1402 {Parachain: UNIQUE_CHAIN},1403 {AccountId32: {network: 'Any', id: randomAccountUnique.addressRaw}},1404 ],1405 },1406 },1407 };14081409 await helper.xTokens.transferMultiasset(randomAccountMoonbeam, asset, destination, 'Unlimited');14101411 balanceGlmrTokenFinal = await helper.balance.getEthereum(randomAccountMoonbeam.address);14121413 const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal;1414 console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));1415 expect(glmrFees > 0, 'Negative fees GLMR, looks like nothing was transferred').to.be.true;14161417 const unqRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonbeam.address);14181419 expect(unqRandomAccountAsset).to.be.null;14201421 balanceForeignUnqTokenFinal = 0n;14221423 const unqOutcomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenFinal;1424 console.log('[Unique -> Moonbeam] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));1425 expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;1426 });14271428 await helper.wait.newBlocks(3);14291430 balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccountUnique.address);1431 const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;1432 expect(actuallyDelivered > 0).to.be.true;14331434 console.log('[Moonbeam -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));14351436 const unqFees = TRANSFER_AMOUNT - actuallyDelivered;1437 console.log('[Moonbeam -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));1438 expect(unqFees == 0n).to.be.true;1439 });14401441 itSub('Moonbeam can send only up to its balance', async ({helper}) => {1442 1443 const moonbeamBalance = 10000n * (10n ** UNQ_DECIMALS);1444 const moonbeamSovereignAccount = helper.address.paraSiblingSovereignAccount(MOONBEAM_CHAIN);1445 await helper.getSudo().balance.setBalanceSubstrate(alice, moonbeamSovereignAccount, moonbeamBalance);14461447 const moreThanMoonbeamHas = moonbeamBalance * 2n;14481449 let targetAccountBalance = 0n;1450 const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);14511452 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1453 targetAccount.addressRaw,1454 {1455 Concrete: {1456 parents: 0,1457 interior: 'Here',1458 },1459 },1460 moreThanMoonbeamHas,1461 );14621463 let maliciousXcmProgramSent: any;1464 const maxWaitBlocks = 3;14651466 1467 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1468 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgram]);14691470 1471 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1472 await helper.fastDemocracy.executeProposal('try to spend more UNQ than Moonbeam has', batchCall);14731474 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1475 });14761477 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash1478 && event.outcome.isFailedToTransactAsset);14791480 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1481 expect(targetAccountBalance).to.be.equal(0n);14821483 1484 const validTransferAmount = moonbeamBalance / 2n;1485 const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1486 targetAccount.addressRaw,1487 {1488 Concrete: {1489 parents: 0,1490 interior: 'Here',1491 },1492 },1493 validTransferAmount,1494 );14951496 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1497 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, validXcmProgram]);14981499 1500 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1501 await helper.fastDemocracy.executeProposal('Spend the correct amount of UNQ', batchCall);1502 });15031504 await helper.wait.newBlocks(maxWaitBlocks);15051506 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1507 expect(targetAccountBalance).to.be.equal(validTransferAmount);1508 });15091510 itSub('Should not accept reserve transfer of UNQ from Moonbeam', async ({helper}) => {1511 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1512 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);15131514 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1515 targetAccount.addressRaw,1516 {1517 Concrete: {1518 parents: 0,1519 interior: {1520 X1: {1521 Parachain: UNIQUE_CHAIN,1522 },1523 },1524 },1525 },1526 testAmount,1527 );15281529 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1530 targetAccount.addressRaw,1531 {1532 Concrete: {1533 parents: 0,1534 interior: 'Here',1535 },1536 },1537 testAmount,1538 );15391540 let maliciousXcmProgramFullIdSent: any;1541 let maliciousXcmProgramHereIdSent: any;1542 const maxWaitBlocks = 3;15431544 1545 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1546 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgramFullId]);15471548 1549 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1550 await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using path asset identification', batchCall);15511552 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1553 });15541555 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash1556 && event.outcome.isUntrustedReserveLocation);15571558 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1559 expect(accountBalance).to.be.equal(0n);15601561 1562 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1563 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgramHereId]);15641565 1566 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1567 await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using "here" asset identification', batchCall);15681569 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1570 });15711572 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash1573 && event.outcome.isUntrustedReserveLocation);15741575 accountBalance = await helper.balance.getSubstrate(targetAccount.address);1576 expect(accountBalance).to.be.equal(0n);1577 });1578});15791580describeXCM('[XCM] Integration test: Exchanging tokens with Astar', () => {1581 let alice: IKeyringPair;1582 let randomAccount: IKeyringPair;15831584 const UNQ_ASSET_ID_ON_ASTAR = 1;1585 const UNQ_MINIMAL_BALANCE_ON_ASTAR = 1n;15861587 1588 const astarInitialBalance = 1n * (10n ** ASTAR_DECIMALS); 1589 const unitsPerSecond = 228_000_000_000n; 1590 const unqToAstarTransferred = 10n * (10n ** UNQ_DECIMALS); 1591 const unqToAstarArrived = 9_999_999_999_088_000_000n; 15921593 1594 const unqFromAstarTransfered = 5n * (10n ** UNQ_DECIMALS); 1595 const unqOnAstarLeft = unqToAstarArrived - unqFromAstarTransfered; 15961597 let balanceAfterUniqueToAstarXCM: bigint;15981599 before(async () => {1600 await usingPlaygrounds(async (helper, privateKey) => {1601 alice = await privateKey('//Alice');1602 [randomAccount] = await helper.arrange.createAccounts([100n], alice);1603 console.log('randomAccount', randomAccount.address);16041605 1606 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1607 });16081609 await usingAstarPlaygrounds(astarUrl, async (helper) => {1610 if(!(await helper.callRpc('api.query.assets.asset', [UNQ_ASSET_ID_ON_ASTAR])).toJSON()) {1611 console.log('1. Create foreign asset and metadata');1612 1613 await helper.assets.create(1614 alice,1615 UNQ_ASSET_ID_ON_ASTAR,1616 alice.address,1617 UNQ_MINIMAL_BALANCE_ON_ASTAR,1618 );16191620 await helper.assets.setMetadata(1621 alice,1622 UNQ_ASSET_ID_ON_ASTAR,1623 'Cross chain UNQ',1624 'xcUNQ',1625 Number(UNQ_DECIMALS),1626 );16271628 console.log('2. Register asset location on Astar');1629 const assetLocation = {1630 V2: {1631 parents: 1,1632 interior: {1633 X1: {1634 Parachain: UNIQUE_CHAIN,1635 },1636 },1637 },1638 };16391640 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, UNQ_ASSET_ID_ON_ASTAR]);16411642 console.log('3. Set UNQ payment for XCM execution on Astar');1643 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);1644 }1645 console.log('4. Transfer 1 ASTR to recipient to create the account (needed due to existential balance)');1646 await helper.balance.transferToSubstrate(alice, randomAccount.address, astarInitialBalance);1647 });1648 });16491650 itSub('Should connect and send UNQ to Astar', async ({helper}) => {1651 const destination = {1652 V2: {1653 parents: 1,1654 interior: {1655 X1: {1656 Parachain: ASTAR_CHAIN,1657 },1658 },1659 },1660 };16611662 const beneficiary = {1663 V2: {1664 parents: 0,1665 interior: {1666 X1: {1667 AccountId32: {1668 network: 'Any',1669 id: randomAccount.addressRaw,1670 },1671 },1672 },1673 },1674 };16751676 const assets = {1677 V2: [1678 {1679 id: {1680 Concrete: {1681 parents: 0,1682 interior: 'Here',1683 },1684 },1685 fun: {1686 Fungible: unqToAstarTransferred,1687 },1688 },1689 ],1690 };16911692 1693 const balanceBefore = await helper.balance.getSubstrate(randomAccount.address);1694 console.log(`Initial balance is: ${balanceBefore}`);16951696 const feeAssetItem = 0;1697 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');16981699 1700 balanceAfterUniqueToAstarXCM = await helper.balance.getSubstrate(randomAccount.address);1701 console.log(`UNQ Balance on Unique after XCM is: ${balanceAfterUniqueToAstarXCM}`);1702 console.log(`Unique's UNQ commission is: ${balanceBefore - balanceAfterUniqueToAstarXCM}`);1703 expect(balanceBefore - balanceAfterUniqueToAstarXCM > 0).to.be.true;17041705 await usingAstarPlaygrounds(astarUrl, async (helper) => {1706 await helper.wait.newBlocks(3);1707 const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);1708 const astarBalance = await helper.balance.getSubstrate(randomAccount.address);17091710 console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);1711 console.log(`Astar's UNQ commission is: ${unqToAstarTransferred - xcUNQbalance!}`);17121713 expect(xcUNQbalance).to.eq(unqToAstarArrived);1714 1715 expect(astarBalance).to.eq(astarInitialBalance);1716 });1717 });17181719 itSub('Should connect to Astar and send UNQ back', async ({helper}) => {1720 await usingAstarPlaygrounds(astarUrl, async (helper) => {1721 const destination = {1722 V2: {1723 parents: 1,1724 interior: {1725 X1: {1726 Parachain: UNIQUE_CHAIN,1727 },1728 },1729 },1730 };17311732 const beneficiary = {1733 V2: {1734 parents: 0,1735 interior: {1736 X1: {1737 AccountId32: {1738 network: 'Any',1739 id: randomAccount.addressRaw,1740 },1741 },1742 },1743 },1744 };17451746 const assets = {1747 V2: [1748 {1749 id: {1750 Concrete: {1751 parents: 1,1752 interior: {1753 X1: {1754 Parachain: UNIQUE_CHAIN,1755 },1756 },1757 },1758 },1759 fun: {1760 Fungible: unqFromAstarTransfered,1761 },1762 },1763 ],1764 };17651766 1767 const balanceASTRbefore = await helper.balance.getSubstrate(randomAccount.address);1768 console.log(`ASTR balance is: ${balanceASTRbefore}, it does not changed`);1769 expect(balanceASTRbefore).to.eq(astarInitialBalance);17701771 const feeAssetItem = 0;1772 1773 await helper.executeExtrinsic(randomAccount, 'api.tx.polkadotXcm.reserveWithdrawAssets', [destination, beneficiary, assets, feeAssetItem]);17741775 const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);1776 const balanceAstar = await helper.balance.getSubstrate(randomAccount.address);1777 console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);17781779 1780 expect(xcUNQbalance).to.eq(unqOnAstarLeft);1781 1782 expect(balanceAstar / (10n ** (ASTAR_DECIMALS - 3n))).to.eq(996n);1783 });17841785 await helper.wait.newBlocks(3);1786 const balanceUNQ = await helper.balance.getSubstrate(randomAccount.address);1787 console.log(`UNQ Balance on Unique after XCM is: ${balanceUNQ}`);1788 expect(balanceUNQ).to.eq(balanceAfterUniqueToAstarXCM + unqFromAstarTransfered);1789 });17901791 itSub('Astar can send only up to its balance', async ({helper}) => {1792 1793 const astarBalance = 10000n * (10n ** UNQ_DECIMALS);1794 const astarSovereignAccount = helper.address.paraSiblingSovereignAccount(ASTAR_CHAIN);1795 await helper.getSudo().balance.setBalanceSubstrate(alice, astarSovereignAccount, astarBalance);17961797 const moreThanAstarHas = astarBalance * 2n;17981799 let targetAccountBalance = 0n;1800 const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);18011802 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1803 targetAccount.addressRaw,1804 {1805 Concrete: {1806 parents: 0,1807 interior: 'Here',1808 },1809 },1810 moreThanAstarHas,1811 );18121813 let maliciousXcmProgramSent: any;1814 const maxWaitBlocks = 3;18151816 1817 await usingAstarPlaygrounds(astarUrl, async (helper) => {1818 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);18191820 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1821 });18221823 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash1824 && event.outcome.isFailedToTransactAsset);18251826 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1827 expect(targetAccountBalance).to.be.equal(0n);18281829 1830 const validTransferAmount = astarBalance / 2n;1831 const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1832 targetAccount.addressRaw,1833 {1834 Concrete: {1835 parents: 0,1836 interior: 'Here',1837 },1838 },1839 validTransferAmount,1840 );18411842 await usingAstarPlaygrounds(astarUrl, async (helper) => {1843 await helper.getSudo().xcm.send(alice, uniqueMultilocation, validXcmProgram);1844 });18451846 await helper.wait.newBlocks(maxWaitBlocks);18471848 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1849 expect(targetAccountBalance).to.be.equal(validTransferAmount);1850 });18511852 itSub('Should not accept reserve transfer of UNQ from Astar', async ({helper}) => {1853 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1854 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);18551856 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1857 targetAccount.addressRaw,1858 {1859 Concrete: {1860 parents: 1,1861 interior: {1862 X1: {1863 Parachain: UNIQUE_CHAIN,1864 },1865 },1866 },1867 },1868 testAmount,1869 );18701871 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1872 targetAccount.addressRaw,1873 {1874 Concrete: {1875 parents: 0,1876 interior: 'Here',1877 },1878 },1879 testAmount,1880 );18811882 let maliciousXcmProgramFullIdSent: any;1883 let maliciousXcmProgramHereIdSent: any;1884 const maxWaitBlocks = 3;18851886 1887 await usingAstarPlaygrounds(astarUrl, async (helper) => {1888 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);18891890 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1891 });18921893 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash1894 && event.outcome.isUntrustedReserveLocation);18951896 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1897 expect(accountBalance).to.be.equal(0n);18981899 1900 await usingAstarPlaygrounds(astarUrl, async (helper) => {1901 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);19021903 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1904 });19051906 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash1907 && event.outcome.isUntrustedReserveLocation);19081909 accountBalance = await helper.balance.getSubstrate(targetAccount.address);1910 expect(accountBalance).to.be.equal(0n);1911 });1912});