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 });534535 await usingPlaygrounds(async (helper) => {536 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);537 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);538 });539 });540541 itSub('Should connect and send UNQ to Acala', async ({helper}) => {542543 const destination = {544 V2: {545 parents: 1,546 interior: {547 X1: {548 Parachain: ACALA_CHAIN,549 },550 },551 },552 };553554 const beneficiary = {555 V2: {556 parents: 0,557 interior: {558 X1: {559 AccountId32: {560 network: 'Any',561 id: randomAccount.addressRaw,562 },563 },564 },565 },566 };567568 const assets = {569 V2: [570 {571 id: {572 Concrete: {573 parents: 0,574 interior: 'Here',575 },576 },577 fun: {578 Fungible: TRANSFER_AMOUNT,579 },580 },581 ],582 };583584 const feeAssetItem = 0;585586 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');587 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);588589 const unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;590 console.log('[Unique -> Acala] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));591 expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;592593 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {594 await helper.wait.newBlocks(3);595596 balanceUniqueForeignTokenMiddle = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});597 balanceAcalaTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);598599 const acaFees = balanceAcalaTokenInit - balanceAcalaTokenMiddle;600 const unqIncomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenInit;601 acalaBackwardTransferAmount = unqIncomeTransfer;602603 const acaUnqFees = TRANSFER_AMOUNT - unqIncomeTransfer;604605 console.log(606 '[Unique -> Acala] transaction fees on Acala: %s ACA',607 helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),608 );609 console.log(610 '[Unique -> Acala] transaction fees on Acala: %s UNQ',611 helper.util.bigIntToDecimals(acaUnqFees),612 );613 console.log('[Unique -> Acala] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));614 expect(acaFees == 0n).to.be.true;615616 const bigintAbs = (n: bigint) => (n < 0n) ? -n : n;617618 expect(619 bigintAbs(acaUnqFees - expectedAcalaIncomeFee) < acalaEps,620 'Acala took different income fee, check the Acala foreign asset config',621 ).to.be.true;622 });623 });624625 itSub('Should connect to Acala and send UNQ back', async ({helper}) => {626 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {627 const destination = {628 V2: {629 parents: 1,630 interior: {631 X2: [632 {Parachain: UNIQUE_CHAIN},633 {634 AccountId32: {635 network: 'Any',636 id: randomAccount.addressRaw,637 },638 },639 ],640 },641 },642 };643644 const id = {645 ForeignAsset: 0,646 };647648 await helper.xTokens.transfer(randomAccount, id, acalaBackwardTransferAmount, destination, 'Unlimited');649 balanceAcalaTokenFinal = await helper.balance.getSubstrate(randomAccount.address);650 balanceUniqueForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);651652 const acaFees = balanceAcalaTokenMiddle - balanceAcalaTokenFinal;653 const unqOutcomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenFinal;654655 console.log(656 '[Acala -> Unique] transaction fees on Acala: %s ACA',657 helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),658 );659 console.log('[Acala -> Unique] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));660661 expect(acaFees > 0, 'Negative fees ACA, looks like nothing was transferred').to.be.true;662 expect(unqOutcomeTransfer == acalaBackwardTransferAmount).to.be.true;663 });664665 await helper.wait.newBlocks(3);666667 balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address);668 const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;669 expect(actuallyDelivered > 0).to.be.true;670671 console.log('[Acala -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));672673 const unqFees = acalaBackwardTransferAmount - actuallyDelivered;674 console.log('[Acala -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));675 expect(unqFees == 0n).to.be.true;676 });677678 itSub('Acala can send only up to its balance', async ({helper}) => {679 680 const acalaBalance = 10000n * (10n ** UNQ_DECIMALS);681 const acalaSovereignAccount = helper.address.paraSiblingSovereignAccount(ACALA_CHAIN);682 await helper.getSudo().balance.setBalanceSubstrate(alice, acalaSovereignAccount, acalaBalance);683684 const moreThanAcalaHas = acalaBalance * 2n;685686 let targetAccountBalance = 0n;687 const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);688689 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(690 targetAccount.addressRaw,691 {692 Concrete: {693 parents: 0,694 interior: 'Here',695 },696 },697 moreThanAcalaHas,698 );699700 let maliciousXcmProgramSent: any;701 const maxWaitBlocks = 3;702703 704 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {705 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);706707 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);708 });709710 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash711 && event.outcome.isFailedToTransactAsset);712713 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);714 expect(targetAccountBalance).to.be.equal(0n);715716 717 const validTransferAmount = acalaBalance / 2n;718 const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(719 targetAccount.addressRaw,720 {721 Concrete: {722 parents: 0,723 interior: 'Here',724 },725 },726 validTransferAmount,727 );728729 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {730 await helper.getSudo().xcm.send(alice, uniqueMultilocation, validXcmProgram);731 });732733 await helper.wait.newBlocks(maxWaitBlocks);734735 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);736 expect(targetAccountBalance).to.be.equal(validTransferAmount);737 });738739 itSub('Should not accept reserve transfer of UNQ from Acala', async ({helper}) => {740 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);741 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);742743 const uniqueMultilocation = {744 V2: {745 parents: 1,746 interior: {747 X1: {748 Parachain: UNIQUE_CHAIN,749 },750 },751 },752 };753754 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(755 targetAccount.addressRaw,756 {757 Concrete: {758 parents: 1,759 interior: {760 X1: {761 Parachain: UNIQUE_CHAIN,762 },763 },764 },765 },766 testAmount,767 );768769 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(770 targetAccount.addressRaw,771 {772 Concrete: {773 parents: 0,774 interior: 'Here',775 },776 },777 testAmount,778 );779780 let maliciousXcmProgramFullIdSent: any;781 let maliciousXcmProgramHereIdSent: any;782 const maxWaitBlocks = 3;783784 785 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {786 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);787788 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);789 });790791 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash792 && event.outcome.isUntrustedReserveLocation);793794 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);795 expect(accountBalance).to.be.equal(0n);796797 798 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {799 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);800801 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);802 });803804 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash805 && event.outcome.isUntrustedReserveLocation);806807 accountBalance = await helper.balance.getSubstrate(targetAccount.address);808 expect(accountBalance).to.be.equal(0n);809 });810});811812describeXCM('[XCM] Integration test: Exchanging tokens with Polkadex', () => {813 let alice: IKeyringPair;814 let randomAccount: IKeyringPair;815 let unqFees: bigint;816 let balanceUniqueTokenInit: bigint;817 let balanceUniqueTokenMiddle: bigint;818 let balanceUniqueTokenFinal: bigint;819 const maxWaitBlocks = 6;820821 const uniqueAssetId = {822 Concrete: {823 parents: 1,824 interior: {825 X1: {826 Parachain: UNIQUE_CHAIN,827 },828 },829 },830 };831832 before(async () => {833 await usingPlaygrounds(async (helper, privateKey) => {834 alice = await privateKey('//Alice');835 [randomAccount] = await helper.arrange.createAccounts([0n], alice);836837 838 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);839 });840841 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {842 const isWhitelisted = ((await helper.callRpc('api.query.xcmHelper.whitelistedTokens', []))843 .toJSON() as [])844 .map(nToBigInt).length != 0;845 846847848849850851 if(!isWhitelisted) {852 await helper.getSudo().xcmHelper.whitelistToken(alice, uniqueAssetId);853 }854855 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);856 });857858 await usingPlaygrounds(async (helper) => {859 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);860 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);861 });862 });863864 itSub('Should connect and send UNQ to Polkadex', async ({helper}) => {865866 const destination = {867 V2: {868 parents: 1,869 interior: {870 X1: {871 Parachain: POLKADEX_CHAIN,872 },873 },874 },875 };876877 const beneficiary = {878 V2: {879 parents: 0,880 interior: {881 X1: {882 AccountId32: {883 network: 'Any',884 id: randomAccount.addressRaw,885 },886 },887 },888 },889 };890891 const assets = {892 V2: [893 {894 id: {895 Concrete: {896 parents: 0,897 interior: 'Here',898 },899 },900 fun: {901 Fungible: TRANSFER_AMOUNT,902 },903 },904 ],905 };906907 const feeAssetItem = 0;908909 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');910 const messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);911 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);912913 unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;914 console.log('[Unique -> Polkadex] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));915 expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;916917 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {918 919920921922923924925926927928 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash);929 });930 });931932933 itSub('Should connect to Polkadex and send UNQ back', async ({helper}) => {934935 const xcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(936 randomAccount.addressRaw,937 {938 Concrete: {939 parents: 1,940 interior: {941 X1: {Parachain: UNIQUE_CHAIN},942 },943 },944 },945 TRANSFER_AMOUNT,946 );947948 let xcmProgramSent: any;949950951 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {952 await helper.getSudo().xcm.send(alice, uniqueMultilocation, xcmProgram);953954 xcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);955 });956957 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Success, event => event.messageHash == xcmProgramSent.messageHash);958959 balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address);960961 expect(balanceUniqueTokenFinal).to.be.equal(balanceUniqueTokenInit - unqFees);962 });963964 itSub('Polkadex can send only up to its balance', async ({helper}) => {965 const polkadexBalance = 10000n * (10n ** UNQ_DECIMALS);966 const polkadexSovereignAccount = helper.address.paraSiblingSovereignAccount(POLKADEX_CHAIN);967 await helper.getSudo().balance.setBalanceSubstrate(alice, polkadexSovereignAccount, polkadexBalance);968 const moreThanPolkadexHas = 2n * polkadexBalance;969970 const targetAccount = helper.arrange.createEmptyAccount();971972 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(973 targetAccount.addressRaw,974 {975 Concrete: {976 parents: 0,977 interior: 'Here',978 },979 },980 moreThanPolkadexHas,981 );982983 let maliciousXcmProgramSent: any;984985986 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {987 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);988989 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);990 });991992 await expectFailedToTransact(helper, maliciousXcmProgramSent);993994 const targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);995 expect(targetAccountBalance).to.be.equal(0n);996 });997998 itSub('Should not accept reserve transfer of UNQ from Polkadex', async ({helper}) => {999 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1000 const targetAccount = helper.arrange.createEmptyAccount();10011002 const uniqueMultilocation = {1003 V2: {1004 parents: 1,1005 interior: {1006 X1: {1007 Parachain: UNIQUE_CHAIN,1008 },1009 },1010 },1011 };10121013 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1014 targetAccount.addressRaw,1015 {1016 Concrete: {1017 parents: 1,1018 interior: {1019 X1: {1020 Parachain: UNIQUE_CHAIN,1021 },1022 },1023 },1024 },1025 testAmount,1026 );10271028 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1029 targetAccount.addressRaw,1030 {1031 Concrete: {1032 parents: 0,1033 interior: 'Here',1034 },1035 },1036 testAmount,1037 );10381039 let maliciousXcmProgramFullIdSent: any;1040 let maliciousXcmProgramHereIdSent: any;1041 const maxWaitBlocks = 3;10421043 1044 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {1045 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);10461047 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1048 });10491050 await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramFullIdSent);10511052 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1053 expect(accountBalance).to.be.equal(0n);10541055 1056 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {1057 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);10581059 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1060 });10611062 await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramHereIdSent);10631064 accountBalance = await helper.balance.getSubstrate(targetAccount.address);1065 expect(accountBalance).to.be.equal(0n);1066 });1067});1068106910701071describeXCM('[XCM] Integration test: Unique rejects non-native tokens', () => {1072 let alice: IKeyringPair;1073 let alith: IKeyringPair;10741075 const testAmount = 100_000_000_000n;1076 let uniqueParachainJunction;1077 let uniqueAccountJunction;10781079 let uniqueParachainMultilocation: any;1080 let uniqueAccountMultilocation: any;1081 let uniqueCombinedMultilocation: any;1082 let uniqueCombinedMultilocationAcala: any; 10831084 let messageSent: any;10851086 const maxWaitBlocks = 3;10871088 before(async () => {1089 await usingPlaygrounds(async (helper, privateKey) => {1090 alice = await privateKey('//Alice');10911092 uniqueParachainJunction = {Parachain: UNIQUE_CHAIN};1093 uniqueAccountJunction = {1094 AccountId32: {1095 network: 'Any',1096 id: alice.addressRaw,1097 },1098 };10991100 uniqueParachainMultilocation = {1101 V2: {1102 parents: 1,1103 interior: {1104 X1: uniqueParachainJunction,1105 },1106 },1107 };11081109 uniqueAccountMultilocation = {1110 V2: {1111 parents: 0,1112 interior: {1113 X1: uniqueAccountJunction,1114 },1115 },1116 };11171118 uniqueCombinedMultilocation = {1119 V2: {1120 parents: 1,1121 interior: {1122 X2: [uniqueParachainJunction, uniqueAccountJunction],1123 },1124 },1125 };11261127 uniqueCombinedMultilocationAcala = {1128 V2: {1129 parents: 1,1130 interior: {1131 X2: [uniqueParachainJunction, uniqueAccountJunction],1132 },1133 },1134 };11351136 1137 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1138 });11391140 1141 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1142 alith = helper.account.alithAccount();1143 });1144 });1145114611471148 itSub('Unique rejects ACA tokens from Acala', async ({helper}) => {1149 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {1150 const id = {1151 Token: 'ACA',1152 };1153 const destination = uniqueCombinedMultilocationAcala;1154 await helper.xTokens.transfer(alice, id, testAmount, destination, 'Unlimited');11551156 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1157 });11581159 await expectFailedToTransact(helper, messageSent);1160 });11611162 itSub('Unique rejects GLMR tokens from Moonbeam', async ({helper}) => {1163 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1164 const id = 'SelfReserve';1165 const destination = uniqueCombinedMultilocation;1166 await helper.xTokens.transfer(alith, id, testAmount, destination, 'Unlimited');11671168 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1169 });11701171 await expectFailedToTransact(helper, messageSent);1172 });11731174 itSub('Unique rejects ASTR tokens from Astar', async ({helper}) => {1175 await usingAstarPlaygrounds(astarUrl, async (helper) => {1176 const destinationParachain = uniqueParachainMultilocation;1177 const beneficiary = uniqueAccountMultilocation;1178 const assets = {1179 V2: [{1180 id: {1181 Concrete: {1182 parents: 0,1183 interior: 'Here',1184 },1185 },1186 fun: {1187 Fungible: testAmount,1188 },1189 }],1190 };1191 const feeAssetItem = 0;11921193 await helper.executeExtrinsic(alice, 'api.tx.polkadotXcm.reserveWithdrawAssets', [1194 destinationParachain,1195 beneficiary,1196 assets,1197 feeAssetItem,1198 ]);11991200 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1201 });12021203 await expectFailedToTransact(helper, messageSent);1204 });12051206 itSub('Unique rejects PDX tokens from Polkadex', async ({helper}) => {12071208 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1209 helper.arrange.createEmptyAccount().addressRaw,1210 {1211 Concrete: {1212 parents: 1,1213 interior: {1214 X1: {1215 Parachain: POLKADEX_CHAIN,1216 },1217 },1218 },1219 },1220 testAmount,1221 );12221223 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {1224 await helper.getSudo().xcm.send(alice, uniqueParachainMultilocation, maliciousXcmProgramFullId);1225 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1226 });12271228 await expectFailedToTransact(helper, messageSent);1229 });1230});12311232describeXCM('[XCM] Integration test: Exchanging UNQ with Moonbeam', () => {1233 1234 let alice: IKeyringPair;1235 let uniqueAssetLocation;12361237 let randomAccountUnique: IKeyringPair;1238 let randomAccountMoonbeam: IKeyringPair;12391240 1241 let assetId: string;12421243 const uniqueAssetMetadata = {1244 name: 'xcUnique',1245 symbol: 'xcUNQ',1246 decimals: 18,1247 isFrozen: false,1248 minimalBalance: 1n,1249 };12501251 let balanceUniqueTokenInit: bigint;1252 let balanceUniqueTokenMiddle: bigint;1253 let balanceUniqueTokenFinal: bigint;1254 let balanceForeignUnqTokenInit: bigint;1255 let balanceForeignUnqTokenMiddle: bigint;1256 let balanceForeignUnqTokenFinal: bigint;1257 let balanceGlmrTokenInit: bigint;1258 let balanceGlmrTokenMiddle: bigint;1259 let balanceGlmrTokenFinal: bigint;12601261 before(async () => {1262 await usingPlaygrounds(async (helper, privateKey) => {1263 alice = await privateKey('//Alice');1264 [randomAccountUnique] = await helper.arrange.createAccounts([0n], alice);12651266 balanceForeignUnqTokenInit = 0n;12671268 1269 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1270 });12711272 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1273 const alithAccount = helper.account.alithAccount();1274 const baltatharAccount = helper.account.baltatharAccount();1275 const dorothyAccount = helper.account.dorothyAccount();12761277 randomAccountMoonbeam = helper.account.create();12781279 1280 console.log('Sponsoring Dorothy.......');1281 await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);1282 console.log('Sponsoring Dorothy.......DONE');1283 12841285 uniqueAssetLocation = {1286 XCM: {1287 parents: 1,1288 interior: {X1: {Parachain: UNIQUE_CHAIN}},1289 },1290 };1291 const existentialDeposit = 1n;1292 const isSufficient = true;1293 const unitsPerSecond = 1n;1294 const numAssetsWeightHint = 0;12951296 if((await helper.assetManager.assetTypeId(uniqueAssetLocation)).toJSON) {1297 console.log('Unique asset is already registered on MoonBeam');1298 } else {1299 const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({1300 location: uniqueAssetLocation,1301 metadata: uniqueAssetMetadata,1302 existentialDeposit,1303 isSufficient,1304 unitsPerSecond,1305 numAssetsWeightHint,1306 });13071308 console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);13091310 await helper.fastDemocracy.executeProposal('register UNQ foreign asset', encodedProposal);13111312 1313 console.log('Acquire Unique AssetId Info on Moonbeam.......');13141315 assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString();13161317 console.log('UNQ asset ID is %s', assetId);1318 console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');1319 }1320 13211322 1323 console.log('Sponsoring random Account.......');1324 await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);1325 console.log('Sponsoring random Account.......DONE');1326 13271328 balanceGlmrTokenInit = await helper.balance.getEthereum(randomAccountMoonbeam.address);1329 });13301331 await usingPlaygrounds(async (helper) => {1332 await helper.balance.transferToSubstrate(alice, randomAccountUnique.address, 10n * TRANSFER_AMOUNT);1333 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccountUnique.address);1334 });1335 });13361337 itSub('Should connect and send UNQ to Moonbeam', async ({helper}) => {1338 const currencyId = {1339 NativeAssetId: 'Here',1340 };1341 const dest = {1342 V2: {1343 parents: 1,1344 interior: {1345 X2: [1346 {Parachain: MOONBEAM_CHAIN},1347 {AccountKey20: {network: 'Any', key: randomAccountMoonbeam.address}},1348 ],1349 },1350 },1351 };1352 const amount = TRANSFER_AMOUNT;13531354 await helper.xTokens.transfer(randomAccountUnique, currencyId, amount, dest, 'Unlimited');13551356 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccountUnique.address);1357 expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true;13581359 const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;1360 console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(transactionFees));1361 expect(transactionFees > 0, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;13621363 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1364 await helper.wait.newBlocks(3);13651366 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});