1234567891011121314151617import type {IKeyringPair} from '@polkadot/types/types';18import {itSub, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds, usingMoonriverPlaygrounds, usingShidenPlaygrounds} from '@unique/test-utils/util.js';19import {QUARTZ_CHAIN, QTZ_DECIMALS, SHIDEN_DECIMALS, karuraUrl, moonriverUrl, shidenUrl, SAFE_XCM_VERSION, XcmTestHelper, TRANSFER_AMOUNT, SENDER_BUDGET, relayUrl} from './xcm.types.js';20import {hexToString} from '@polkadot/util';2122const testHelper = new XcmTestHelper('quartz');2324describeXCM('[XCMLL] Integration test: Exchanging tokens with Karura', () => {25 let alice: IKeyringPair;26 let randomAccount: IKeyringPair;2728 before(async () => {29 await usingPlaygrounds(async (helper, privateKey) => {30 alice = await privateKey('//Alice');31 [randomAccount] = await helper.arrange.createAccounts([0n], alice);3233 34 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);35 });3637 await usingKaruraPlaygrounds(karuraUrl, async (helper) => {38 const destination = {39 V2: {40 parents: 1,41 interior: {42 X1: {43 Parachain: QUARTZ_CHAIN,44 },45 },46 },47 };4849 const metadata = {50 name: 'Quartz',51 symbol: 'QTZ',52 decimals: 18,53 minimalBalance: 1000000000000000000n,54 };5556 const assets = (await (helper.callRpc('api.query.assetRegistry.assetMetadatas.entries'))).map(([_k, v]: [any, any]) =>57 hexToString(v.toJSON()['symbol'])) as string[];5859 if(!assets.includes('QTZ')) {60 await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);61 } else {62 console.log('QTZ token already registered on Karura assetRegistry pallet');63 }64 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);65 });6667 await usingPlaygrounds(async (helper) => {68 await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);69 });70 });7172 itSub('Should connect and send QTZ to Karura', async () => {73 await testHelper.sendUnqTo('karura', randomAccount);74 });7576 itSub('Should connect to Karura and send QTZ back', async () => {77 await testHelper.sendUnqBack('karura', alice, randomAccount);78 });7980 itSub('Karura can send only up to its balance', async () => {81 await testHelper.sendOnlyOwnedBalance('karura', alice);82 });83});848586describeXCM('[XCMLL] Integration test: Quartz rejects non-native tokens', () => {87 let alice: IKeyringPair;888990 before(async () => {91 await usingPlaygrounds(async (helper, privateKey) => {92 alice = await privateKey('//Alice');93949596 97 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);98 });99 });100101 itSub('Quartz rejects KAR tokens from Karura', async () => {102 await testHelper.rejectNativeTokensFrom('karura', alice);103 });104105 itSub('Quartz rejects MOVR tokens from Moonriver', async () => {106 await testHelper.rejectNativeTokensFrom('moonriver', alice);107 });108109 itSub('Quartz rejects SDN tokens from Shiden', async () => {110 await testHelper.rejectNativeTokensFrom('shiden', alice);111 });112});113114describeXCM('[XCMLL] Integration test: Exchanging QTZ with Moonriver', () => {115 116 let alice: IKeyringPair;117 let quartzAssetLocation;118119 let randomAccountQuartz: IKeyringPair;120 let randomAccountMoonriver: IKeyringPair;121122 123 let assetId: string;124125 const quartzAssetMetadata = {126 name: 'xcQuartz',127 symbol: 'xcQTZ',128 decimals: 18,129 isFrozen: false,130 minimalBalance: 1n,131 };132133134 before(async () => {135 await usingPlaygrounds(async (helper, privateKey) => {136 alice = await privateKey('//Alice');137 [randomAccountQuartz] = await helper.arrange.createAccounts([0n], alice);138139140 141 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);142 });143144 await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {145 const alithAccount = helper.account.alithAccount();146 const baltatharAccount = helper.account.baltatharAccount();147 const dorothyAccount = helper.account.dorothyAccount();148149 randomAccountMoonriver = helper.account.create();150151 152 console.log('Sponsoring Dorothy.......');153 await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);154 console.log('Sponsoring Dorothy.......DONE');155 156157 quartzAssetLocation = {158 XCM: {159 parents: 1,160 interior: {X1: {Parachain: QUARTZ_CHAIN}},161 },162 };163 const existentialDeposit = 1n;164 const isSufficient = true;165 const unitsPerSecond = 1n;166 const numAssetsWeightHint = 0;167 if((await helper.assetManager.assetTypeId(quartzAssetLocation)).toJSON()) {168 console.log('Quartz asset already registered on Moonriver');169 } else {170 const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({171 location: quartzAssetLocation,172 metadata: quartzAssetMetadata,173 existentialDeposit,174 isSufficient,175 unitsPerSecond,176 numAssetsWeightHint,177 });178179 console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);180181 await helper.fastDemocracy.executeProposal('register QTZ foreign asset', encodedProposal);182 }183 184 console.log('Acquire Quartz AssetId Info on Moonriver.......');185186 assetId = (await helper.assetManager.assetTypeId(quartzAssetLocation)).toString();187188 console.log('QTZ asset ID is %s', assetId);189 console.log('Acquire Quartz AssetId Info on Moonriver.......DONE');190 191192 193 console.log('Sponsoring random Account.......');194 await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonriver.address, 11_000_000_000_000_000_000n);195 console.log('Sponsoring random Account.......DONE');196 197 });198199 await usingPlaygrounds(async (helper) => {200 await helper.balance.transferToSubstrate(alice, randomAccountQuartz.address, 10n * TRANSFER_AMOUNT);201 });202 });203204 itSub('Should connect and send QTZ to Moonriver', async () => {205 await testHelper.sendUnqTo('moonriver', randomAccountQuartz, randomAccountMoonriver);206 });207208 itSub('Should connect to Moonriver and send QTZ back', async () => {209 await testHelper.sendUnqBack('moonriver', alice, randomAccountQuartz);210 });211212 itSub('Moonriver can send only up to its balance', async () => {213 await testHelper.sendOnlyOwnedBalance('moonriver', alice);214 });215216 itSub('Should not accept reserve transfer of QTZ from Moonriver', async () => {217 await testHelper.rejectReserveTransferUNQfrom('moonriver', alice);218 });219});220221describeXCM('[XCMLL] Integration test: Exchanging tokens with Shiden', () => {222 let alice: IKeyringPair;223 let randomAccount: IKeyringPair;224225 const QTZ_ASSET_ID_ON_SHIDEN = 18_446_744_073_709_551_633n; 226 const QTZ_MINIMAL_BALANCE_ON_SHIDEN = 1n; 227228 229 const shidenInitialBalance = 1n * (10n ** SHIDEN_DECIMALS); 230 const unitsPerSecond = 500_451_000_000_000_000_000n; 231232 before(async () => {233 await usingPlaygrounds(async (helper, privateKey) => {234 alice = await privateKey('//Alice');235 randomAccount = helper.arrange.createEmptyAccount();236 await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);237 console.log('sender: ', randomAccount.address);238239 240 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);241 });242243 await usingShidenPlaygrounds(shidenUrl, async (helper) => {244 if(!(await helper.callRpc('api.query.assets.asset', [QTZ_ASSET_ID_ON_SHIDEN])).toJSON()) {245 console.log('1. Create foreign asset and metadata');246 await helper.getSudo().assets.forceCreate(247 alice,248 QTZ_ASSET_ID_ON_SHIDEN,249 alice.address,250 QTZ_MINIMAL_BALANCE_ON_SHIDEN,251 );252253 await helper.assets.setMetadata(254 alice,255 QTZ_ASSET_ID_ON_SHIDEN,256 'Quartz',257 'QTZ',258 Number(QTZ_DECIMALS),259 );260261 console.log('2. Register asset location on Shiden');262 const assetLocation = {263 V2: {264 parents: 1,265 interior: {266 X1: {267 Parachain: QUARTZ_CHAIN,268 },269 },270 },271 };272273 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, QTZ_ASSET_ID_ON_SHIDEN]);274275 console.log('3. Set QTZ payment for XCM execution on Shiden');276 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);277 } else {278 console.log('QTZ is already registered on Shiden');279 }280 console.log('4. Transfer 1 SDN to recipient to create the account (needed due to existential balance)');281 await helper.balance.transferToSubstrate(alice, randomAccount.address, shidenInitialBalance);282 });283 });284285 itSub('Should connect and send QTZ to Shiden', async () => {286 await testHelper.sendUnqTo('shiden', randomAccount);287 });288289 itSub('Should connect to Shiden and send QTZ back', async () => {290 await testHelper.sendUnqBack('shiden', alice, randomAccount);291 });292293 itSub('Shiden can send only up to its balance', async () => {294 await testHelper.sendOnlyOwnedBalance('shiden', alice);295 });296297 itSub('Should not accept reserve transfer of QTZ from Shiden', async () => {298 await testHelper.rejectReserveTransferUNQfrom('shiden', alice);299 });300});