1234567891011121314151617import chai from 'chai';18import chaiAsPromised from 'chai-as-promised';1920import {Keyring, WsProvider} from '@polkadot/api';21import {ApiOptions} from '@polkadot/api/types';22import {IKeyringPair} from '@polkadot/types/types';23import usingApi, {submitTransactionAsync} from './substrate/substrate-api';24import {getGenericResult, generateKeyringPair} from './util/helpers';25import {MultiLocation} from '@polkadot/types/interfaces';26import {blake2AsHex} from '@polkadot/util-crypto';27import getBalance from './substrate/get-balance';28import waitNewBlocks from './substrate/wait-new-blocks';2930chai.use(chaiAsPromised);31const expect = chai.expect;323334let UNIQUE_CHAIN = 0;35let MOONBEAM_CHAIN = 0;363738process.argv.forEach((val) => {3940 const ai = val.indexOf('moonbeamId=');41 const ui = val.indexOf('uniqueId=');42 if (ai != -1)43 {44 MOONBEAM_CHAIN = Number(val.substring('moonbeamId='.length));45 }46 if (ui != -1)47 {48 UNIQUE_CHAIN = Number(val.substring('uniqueId='.length));49 }50});5152const UNIQUE_PORT = '9944';53const MOONBEAM_PORT = '9946';54const TRANSFER_AMOUNT = 2000000000000000000000000n;5556describe('Integration test: Exchanging UNQ with Moonbeam', () => {5758 59 let uniqueAlice: IKeyringPair;60 let uniqueAssetLocation;6162 let randomAccountUnique: IKeyringPair;63 let randomAccountMoonbeam: IKeyringPair;6465 66 let assetId: Uint8Array;6768 const moonbeamKeyring = new Keyring({type: 'ethereum'});69 const alithPrivateKey = '0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133';70 const baltatharPrivateKey = '0x8075991ce870b93a8870eca0c0f91913d12f47948ca0fd25b49c6fa7cdbeee8b';71 const dorothyPrivateKey = '0x39539ab1876910bbf3a223d84a29e28f1cb4e2e456503e7e91ed39b2e7223d68';7273 const alithAccount = moonbeamKeyring.addFromUri(alithPrivateKey, undefined, 'ethereum');74 const baltatharAccount = moonbeamKeyring.addFromUri(baltatharPrivateKey, undefined, 'ethereum');75 const dorothyAccount = moonbeamKeyring.addFromUri(dorothyPrivateKey, undefined, 'ethereum');7677 const councilVotingThreshold = 2;78 const technicalCommitteeThreshold = 2;79 const votingPeriod = 3;80 const delayPeriod = 0;8182 const uniqueAssetMetadata = {83 name: 'xcUnique',84 symbol: 'xcUNQ',85 decimals: 18,86 isFrozen: false,87 minimalBalance: 1,88 };8990 91 9293 94 95 96 97 98 99 100101 let balanceUniqueTokenBefore: bigint;102 let balanceUniqueTokenAfter: bigint;103 let balanceUniqueTokenFinal: bigint;104 let balanceGlmrTokenBefore: bigint;105 let balanceGlmrTokenAfter: bigint;106 let balanceGlmrTokenFinal: bigint;107108 before(async () => {109 await usingApi(async (api, privateKeyWrapper) => {110 uniqueAlice = privateKeyWrapper('//Alice');111 randomAccountUnique = generateKeyringPair();112 randomAccountMoonbeam = generateKeyringPair('ethereum');113 });114115 const moonbeamApiOptions: ApiOptions = {116 provider: new WsProvider('ws://127.0.0.1:' + MOONBEAM_PORT),117 };118119 await usingApi(120 async (api) => {121122 123 const tx0 = api.tx.balances.transfer(dorothyAccount.address, 11_000_000_000_000_000_000n);124 const events0 = await submitTransactionAsync(alithAccount, tx0);125 const result0 = getGenericResult(events0);126 expect(result0.success).to.be.true;127 128129 const sourceLocation: MultiLocation = api.createType(130 'MultiLocation',131 {132 parents: 1,133 interior: {X1: {Parachain: UNIQUE_CHAIN}},134 },135 );136137 assetId = api.registry.hash(sourceLocation.toU8a()).slice(0, 16).reverse();138 console.log('Internal asset ID is %s', assetId);139 uniqueAssetLocation = {XCM: sourceLocation};140 const existentialDeposit = 1;141 const isSufficient = true;142 const unitsPerSecond = '1';143 const numAssetsWeightHint = 0;144145 const registerTx = api.tx.assetManager.registerForeignAsset(146 uniqueAssetLocation,147 uniqueAssetMetadata,148 existentialDeposit,149 isSufficient,150 );151 console.log('Encoded proposal for registerAsset is %s', registerTx.method.toHex() || '');152153 const setUnitsTx = api.tx.assetManager.setAssetUnitsPerSecond(154 uniqueAssetLocation,155 unitsPerSecond,156 numAssetsWeightHint,157 );158 console.log('Encoded proposal for setAssetUnitsPerSecond is %s', setUnitsTx.method.toHex() || '');159160 const batchCall = api.tx.utility.batchAll([registerTx, setUnitsTx]);161 console.log('Encoded proposal for batchCall is %s', batchCall.method.toHex() || '');162163 164 const encodedProposal = batchCall?.method.toHex() || '';165 const proposalHash = blake2AsHex(encodedProposal);166 console.log('Encoded proposal for batch utility after schedule is %s', encodedProposal);167 console.log('Encoded proposal hash for batch utility after schedule is %s', proposalHash);168 console.log('Encoded length %d', encodedProposal.length);169170 const tx1 = api.tx.democracy.notePreimage(encodedProposal);171 const events1 = await submitTransactionAsync(baltatharAccount, tx1);172 const result1 = getGenericResult(events1);173 expect(result1.success).to.be.true;174 175176 177 const externalMotion = api.tx.democracy.externalProposeMajority(proposalHash);178 const tx2 = api.tx.councilCollective.propose(179 councilVotingThreshold,180 externalMotion,181 externalMotion.encodedLength,182 );183 const events2 = await submitTransactionAsync(baltatharAccount, tx2);184 const result2 = getGenericResult(events2);185 expect(result2.success).to.be.true;186187 const encodedMotion = externalMotion?.method.toHex() || '';188 const motionHash = blake2AsHex(encodedMotion);189 console.log('Motion hash is %s', motionHash);190191 const tx3 = api.tx.councilCollective.vote(motionHash, 0, true);192 {193 const events3 = await submitTransactionAsync(dorothyAccount, tx3);194 const result3 = getGenericResult(events3);195 expect(result3.success).to.be.true;196 }197 {198 const events3 = await submitTransactionAsync(baltatharAccount, tx3);199 const result3 = getGenericResult(events3);200 expect(result3.success).to.be.true;201 }202203 const tx4 = api.tx.councilCollective.close(motionHash, 0, 1_000_000_000, externalMotion.encodedLength);204 const events4 = await submitTransactionAsync(dorothyAccount, tx4);205 const result4 = getGenericResult(events4);206 expect(result4.success).to.be.true;207 208209 210 const fastTrack = api.tx.democracy.fastTrack(proposalHash, votingPeriod, delayPeriod);211 const tx5 = api.tx.techCommitteeCollective.propose(212 technicalCommitteeThreshold,213 fastTrack,214 fastTrack.encodedLength,215 );216 const events5 = await submitTransactionAsync(alithAccount, tx5);217 const result5 = getGenericResult(events5);218 expect(result5.success).to.be.true;219220 const encodedFastTrack = fastTrack?.method.toHex() || '';221 const fastTrackHash = blake2AsHex(encodedFastTrack);222 console.log('FastTrack hash is %s', fastTrackHash);223224 const proposalIdx = Number(await api.query.techCommitteeCollective.proposalCount()) - 1;225 const tx6 = api.tx.techCommitteeCollective.vote(fastTrackHash, proposalIdx, true);226 {227 const events6 = await submitTransactionAsync(baltatharAccount, tx6);228 const result6 = getGenericResult(events6);229 expect(result6.success).to.be.true;230 }231 {232 const events6 = await submitTransactionAsync(alithAccount, tx6);233 const result6 = getGenericResult(events6);234 expect(result6.success).to.be.true;235 }236237 const tx7 = api.tx.techCommitteeCollective238 .close(fastTrackHash, proposalIdx, 1_000_000_000, fastTrack.encodedLength);239 const events7 = await submitTransactionAsync(baltatharAccount, tx7);240 const result7 = getGenericResult(events7);241 expect(result7.success).to.be.true;242 243244 245 const tx8 = api.tx.democracy.vote(246 0,247 {Standard: {balance: 10_000_000_000_000_000_000n, vote: {aye: true, conviction: 1}}},248 );249 const events8 = await submitTransactionAsync(dorothyAccount, tx8);250 const result8 = getGenericResult(events8);251 expect(result8.success).to.be.true;252 253254 255 const tx9 = api.tx.balances.transfer(randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);256 const events9 = await submitTransactionAsync(baltatharAccount, tx9);257 const result9 = getGenericResult(events9);258 expect(result9.success).to.be.true;259 260261 [balanceGlmrTokenBefore] = await getBalance(api, [randomAccountMoonbeam.address]);262 },263 moonbeamApiOptions,264 );265266 await usingApi(async (api) => {267 const tx0 = api.tx.balances.transfer(randomAccountUnique.address, 10n * TRANSFER_AMOUNT);268 const events0 = await submitTransactionAsync(uniqueAlice, tx0);269 const result0 = getGenericResult(events0);270 expect(result0.success).to.be.true;271272 [balanceUniqueTokenBefore] = await getBalance(api, [randomAccountUnique.address]);273 });274 });275276 it('Should connect and send UNQ to Moonbeam', async () => {277 await usingApi(async (api) => {278 const currencyId = {279 NativeAssetId: 'Here',280 };281 const dest = {282 V1: {283 parents: 1,284 interior: {285 X2: [286 {Parachain: MOONBEAM_CHAIN},287 {AccountKey20: {network: 'Any', key: randomAccountMoonbeam.address}},288 ],289 },290 },291 };292 const amount = TRANSFER_AMOUNT;293 const destWeight = 50000000;294295 const tx = api.tx.xTokens.transfer(currencyId, amount, dest, destWeight);296 const events = await submitTransactionAsync(uniqueAlice, tx);297 const result = getGenericResult(events);298 expect(result.success).to.be.true;299300 [balanceUniqueTokenAfter] = await getBalance(api, [randomAccountUnique.address]);301 expect(balanceUniqueTokenAfter < balanceUniqueTokenBefore).to.be.true;302303 const transactionFees = balanceUniqueTokenBefore - balanceUniqueTokenAfter - TRANSFER_AMOUNT;304 console.log('Unique to Moonbeam transaction fees on Unique: %s UNQ', transactionFees);305 expect(transactionFees > 0).to.be.true;306 });307308 await usingApi(309 async (api) => {310 311 await waitNewBlocks(api, 3);312313 [balanceGlmrTokenAfter] = await getBalance(api, [randomAccountMoonbeam.address]);314315 const glmrFees = balanceGlmrTokenBefore - balanceGlmrTokenAfter;316 console.log('Unique to Moonbeam transaction fees on Moonbeam: %s GLMR', glmrFees);317 expect(glmrFees == 0n).to.be.true;318 },319 {provider: new WsProvider('ws://127.0.0.1:' + MOONBEAM_PORT)},320 );321 });322323 it('Should connect to Moonbeam and send UNQ back', async () => {324 await usingApi(325 async (api) => {326 const amount = TRANSFER_AMOUNT / 2n;327 const asset = {328 V1: {329 id: {330 Concrete: {331 parents: 1,332 interior: {333 X1: {Parachain: UNIQUE_CHAIN},334 },335 },336 },337 fun: {338 Fungible: amount,339 },340 },341 };342 const destination = {343 V1: {344 parents: 1,345 interior: {346 X2: [347 {Parachain: UNIQUE_CHAIN},348 {AccountId32: {network: 'Any', id: randomAccountUnique.addressRaw}},349 ],350 },351 },352 };353 const destWeight = 50000000;354355 const tx = api.tx.xTokens.transferMultiasset(asset, destination, destWeight);356 const events = await submitTransactionAsync(randomAccountMoonbeam, tx);357 const result = getGenericResult(events);358 expect(result.success).to.be.true;359360 [balanceGlmrTokenFinal] = await getBalance(api, [randomAccountMoonbeam.address]);361362 const glmrFees = balanceGlmrTokenAfter - balanceGlmrTokenFinal;363 console.log('Moonbeam to Unique transaction fees on Moonbeam: %s GLMR', glmrFees);364 expect(glmrFees > 0).to.be.true;365 },366 {provider: new WsProvider('ws://127.0.0.1:' + MOONBEAM_PORT)},367 );368369 await usingApi(async (api) => {370 371 await waitNewBlocks(api, 3);372373 [balanceUniqueTokenFinal] = await getBalance(api, [randomAccountUnique.address]);374 const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenAfter;375 expect(actuallyDelivered > 0).to.be.true;376377 const unqFees = TRANSFER_AMOUNT - actuallyDelivered;378 console.log('Moonbeam to Unique transaction fees on Unique: %s UNQ', unqFees);379 expect(unqFees > 0).to.be.true;380 });381 });382});