difftreelog
Merge pull request #640 from UniqueNetwork/refactor/xcm-n-rmrk-tests-playgrnds
in: master
Refactor/xcm n rmrk tests playgrnds
36 files changed
.docker/xcm-config/launch-config-xcm-unique-rococo.jsondiffbeforeafterbothno syntactic changes
tests/src/config.tsdiffbeforeafterboth19const config = {19const config = {20 substrateUrl: process.env.substrateUrl || 'ws://127.0.0.1:9944',20 substrateUrl: process.env.substrateUrl || 'ws://127.0.0.1:9944',21 frontierUrl: process.env.frontierUrl || 'http://127.0.0.1:9933',21 frontierUrl: process.env.frontierUrl || 'http://127.0.0.1:9933',22 relayUrl: process.env.relayUrl || 'ws://127.0.0.1:9844',23 acalaUrl: process.env.acalaUrl || 'ws://127.0.0.1:9946',24 karuraUrl: process.env.acalaUrl || 'ws://127.0.0.1:9946',25 moonbeamUrl: process.env.moonbeamUrl || 'ws://127.0.0.1:9947',26 moonriverUrl: process.env.moonbeamUrl || 'ws://127.0.0.1:9947',27 westmintUrl: process.env.westmintUrl || 'ws://127.0.0.1:9948',22};28};232924export default config;30export default config;tests/src/deprecated-helpers/contracthelpers.tsdiffbeforeafterbothno changes
tests/src/deprecated-helpers/eth/helpers.d.tsdiffbeforeafterbothno changes
tests/src/deprecated-helpers/eth/helpers.tsdiffbeforeafterbothno changes
tests/src/deprecated-helpers/helpers.tsdiffbeforeafterbothno changes
tests/src/deprecated-helpers/util.tsdiffbeforeafterbothno changes
tests/src/eth/util/playgrounds/unique.dev.tsdiffbeforeafterboth361 }361 }362362363 clearApi() {363 clearApi() {364 super.clearApi();364 this.web3 = null;365 this.web3 = null;365 }366 }366367tests/src/rmrk/acceptNft.test.tsdiffbeforeafterboth7 acceptNft,7 acceptNft,8} from './util/tx';8} from './util/tx';9import {NftIdTuple} from './util/fetch';9import {NftIdTuple} from './util/fetch';10import {isNftChildOfAnother, expectTxFailure} from './util/helpers';10import {isNftChildOfAnother, expectTxFailure, requirePallets, Pallets} from './util/helpers';11import {requirePallets, Pallets} from '../deprecated-helpers/helpers';121113describe('integration test: accept NFT', () => {12describe('integration test: accept NFT', () => {14 let api: any;13 let api: any;104 expect(isChild).to.be.false;103 expect(isChild).to.be.false;105 });104 });106105107 after(() => { api.disconnect(); });106 after(async() => { await api.disconnect(); });108});107});109108tests/src/rmrk/addResource.test.tsdiffbeforeafterboth1import {expect} from 'chai';1import {expect} from 'chai';2import {getApiConnection} from '../substrate/substrate-api';2import {getApiConnection} from '../substrate/substrate-api';3import {NftIdTuple} from './util/fetch';3import {NftIdTuple} from './util/fetch';4import {expectTxFailure, getResourceById} from './util/helpers';4import {expectTxFailure, getResourceById, requirePallets, Pallets} from './util/helpers';5import {5import {6 addNftBasicResource,6 addNftBasicResource,7 acceptNftResource,7 acceptNftResource,12 addNftComposableResource,12 addNftComposableResource,13} from './util/tx';13} from './util/tx';14import {RmrkTraitsResourceResourceInfo as ResourceInfo} from '@polkadot/types/lookup';14import {RmrkTraitsResourceResourceInfo as ResourceInfo} from '@polkadot/types/lookup';15import {requirePallets, Pallets} from '../deprecated-helpers/helpers';161517describe('integration test: add NFT resource', () => {16describe('integration test: add NFT resource', () => {18 const Alice = '//Alice';17 const Alice = '//Alice';433432434433435 after(() => {434 after(() => {436 api.disconnect();435 after(async() => { await api.disconnect(); });437 });436 });438});437});439438tests/src/rmrk/addTheme.test.tsdiffbeforeafterboth1import {expect} from 'chai';1import {expect} from 'chai';2import {getApiConnection} from '../substrate/substrate-api';2import {getApiConnection} from '../substrate/substrate-api';3import {createBase, addTheme} from './util/tx';3import {createBase, addTheme} from './util/tx';4import {expectTxFailure} from './util/helpers';4import {expectTxFailure, requirePallets, Pallets} from './util/helpers';5import {getThemeNames} from './util/fetch';5import {getThemeNames} from './util/fetch';6import {requirePallets, Pallets} from '../deprecated-helpers/helpers';768describe('integration test: add Theme to Base', () => {7describe('integration test: add Theme to Base', () => {9 let api: any;8 let api: any;126 await expectTxFailure(/rmrkEquip\.PermissionError/, tx);125 await expectTxFailure(/rmrkEquip\.PermissionError/, tx);127 });126 });128127129 after(() => { api.disconnect(); });128 after(async() => { await api.disconnect(); });130});129});131130tests/src/rmrk/burnNft.test.tsdiffbeforeafterboth1import {getApiConnection} from '../substrate/substrate-api';1import {getApiConnection} from '../substrate/substrate-api';2import {expectTxFailure} from './util/helpers';2import {expectTxFailure, requirePallets, Pallets} from './util/helpers';3import {NftIdTuple, getChildren} from './util/fetch';3import {NftIdTuple, getChildren} from './util/fetch';4import {burnNft, createCollection, sendNft, mintNft} from './util/tx';4import {burnNft, createCollection, sendNft, mintNft} from './util/tx';556import chai from 'chai';6import chai from 'chai';7import chaiAsPromised from 'chai-as-promised';7import chaiAsPromised from 'chai-as-promised';8import {requirePallets, Pallets} from '../deprecated-helpers/helpers';9810chai.use(chaiAsPromised);9chai.use(chaiAsPromised);11const expect = chai.expect;10const expect = chai.expect;167 });166 });168 });167 });169168170 after(() => {169 after(async() => { await api.disconnect(); });171 api.disconnect();172 });173});170});tests/src/rmrk/changeCollectionIssuer.test.tsdiffbeforeafterboth1import {getApiConnection} from '../substrate/substrate-api';1import {getApiConnection} from '../substrate/substrate-api';2import {requirePallets, Pallets} from '../deprecated-helpers/helpers';2import {expectTxFailure, requirePallets, Pallets} from './util/helpers';3import {expectTxFailure} from './util/helpers';4import {3import {5 changeIssuer,4 changeIssuer,6 createCollection,5 createCollection,50 });49 });51 });50 });525153 after(() => {52 after(async() => { await api.disconnect(); });54 api.disconnect();55 });56});53});tests/src/rmrk/createBase.test.tsdiffbeforeafterboth1import {getApiConnection} from '../substrate/substrate-api';1import {getApiConnection} from '../substrate/substrate-api';2import {requirePallets, Pallets} from '../deprecated-helpers/helpers';2import {requirePallets, Pallets} from './util/helpers';3import {createCollection, createBase} from './util/tx';3import {createCollection, createBase} from './util/tx';445describe('integration test: create new Base', () => {5describe('integration test: create new Base', () => {84 ]);84 ]);85 });85 });868687 after(() => { api.disconnect(); });87 after(async() => { await api.disconnect(); });88});88});8989tests/src/rmrk/createCollection.test.tsdiffbeforeafterboth1import {getApiConnection} from '../substrate/substrate-api';1import {getApiConnection} from '../substrate/substrate-api';2import {requirePallets, Pallets} from '../deprecated-helpers/helpers';2import {requirePallets, Pallets} from './util/helpers';3import {createCollection} from './util/tx';3import {createCollection} from './util/tx';445describe('Integration test: create new collection', () => {5describe('Integration test: create new collection', () => {21 await createCollection(api, alice, 'no-limit-metadata', null, 'no-limit-symbol');21 await createCollection(api, alice, 'no-limit-metadata', null, 'no-limit-symbol');22 });22 });232324 after(() => { api.disconnect(); });24 after(async() => { await api.disconnect(); });25});25});2626tests/src/rmrk/deleteCollection.test.tsdiffbeforeafterboth1import {getApiConnection} from '../substrate/substrate-api';1import {getApiConnection} from '../substrate/substrate-api';2import {requirePallets, Pallets} from '../deprecated-helpers/helpers';2import {expectTxFailure, requirePallets, Pallets} from './util/helpers';3import {expectTxFailure} from './util/helpers';4import {createCollection, deleteCollection} from './util/tx';3import {createCollection, deleteCollection} from './util/tx';546describe('integration test: delete collection', () => {5describe('integration test: delete collection', () => {43 });42 });44 });43 });454446 after(() => {45 after(async() => { await api.disconnect(); });47 api.disconnect();48 });49});46});tests/src/rmrk/equipNft.test.tsdiffbeforeafterboth1import {ApiPromise} from '@polkadot/api';1import {ApiPromise} from '@polkadot/api';2import {expect} from 'chai';2import {expect} from 'chai';3import {getApiConnection} from '../substrate/substrate-api';3import {getApiConnection} from '../substrate/substrate-api';4import {requirePallets, Pallets} from '../deprecated-helpers/helpers';5import {getNft, getParts, NftIdTuple} from './util/fetch';4import {getNft, getParts, NftIdTuple} from './util/fetch';6import {expectTxFailure} from './util/helpers';5import {expectTxFailure, requirePallets, Pallets} from './util/helpers';7import {6import {8 addNftComposableResource,7 addNftComposableResource,9 addNftSlotResource,8 addNftSlotResource,337 await expectTxFailure(/rmrkEquip\.CollectionNotEquippable/, tx);336 await expectTxFailure(/rmrkEquip\.CollectionNotEquippable/, tx);338 });337 });339338340 after(() => {339 after(async() => { await api.disconnect(); });341 api.disconnect();342 });343});340});tests/src/rmrk/getOwnedNfts.test.tsdiffbeforeafterboth1import {expect} from 'chai';1import {expect} from 'chai';2import {getApiConnection} from '../substrate/substrate-api';2import {getApiConnection} from '../substrate/substrate-api';3import {requirePallets, Pallets} from '../deprecated-helpers/helpers';3import {requirePallets, Pallets} from './util/helpers';4import {getOwnedNfts} from './util/fetch';4import {getOwnedNfts} from './util/fetch';5import {mintNft, createCollection} from './util/tx';5import {mintNft, createCollection} from './util/tx';6676 });76 });77 });77 });787879 after(() => { api.disconnect(); });79 after(async() => { await api.disconnect(); });80});80});8181tests/src/rmrk/lockCollection.test.tsdiffbeforeafterboth1import {getApiConnection} from '../substrate/substrate-api';1import {getApiConnection} from '../substrate/substrate-api';2import {requirePallets, Pallets} from '../deprecated-helpers/helpers';2import {expectTxFailure, requirePallets, Pallets} from './util/helpers';3import {expectTxFailure} from './util/helpers';4import {createCollection, lockCollection, mintNft} from './util/tx';3import {createCollection, lockCollection, mintNft} from './util/tx';546describe('integration test: lock collection', () => {5describe('integration test: lock collection', () => {113 });112 });114 });113 });115114116 after(() => {115 after(async() => { await api.disconnect(); });117 api.disconnect();118 });119});116});tests/src/rmrk/mintNft.test.tsdiffbeforeafterboth1import {expect} from 'chai';1import {expect} from 'chai';2import {getApiConnection} from '../substrate/substrate-api';2import {getApiConnection} from '../substrate/substrate-api';3import {requirePallets, Pallets} from '../deprecated-helpers/helpers';4import {getNft} from './util/fetch';3import {getNft} from './util/fetch';5import {expectTxFailure} from './util/helpers';4import {expectTxFailure, requirePallets, Pallets} from './util/helpers';6import {createCollection, mintNft} from './util/tx';5import {createCollection, mintNft} from './util/tx';768describe('integration test: mint new NFT', () => {7describe('integration test: mint new NFT', () => {208 expect(nft.isSome).to.be.false;207 expect(nft.isSome).to.be.false;209 });208 });210209211 after(() => { api.disconnect(); });210 after(async() => { await api.disconnect(); });212});211});213212tests/src/rmrk/rejectNft.test.tsdiffbeforeafterboth7 rejectNft,7 rejectNft,8} from './util/tx';8} from './util/tx';9import {getChildren, NftIdTuple} from './util/fetch';9import {getChildren, NftIdTuple} from './util/fetch';10import {isNftChildOfAnother, expectTxFailure} from './util/helpers';10import {isNftChildOfAnother, expectTxFailure, requirePallets, Pallets} from './util/helpers';11import {requirePallets, Pallets} from '../deprecated-helpers/helpers';121113describe('integration test: reject NFT', () => {12describe('integration test: reject NFT', () => {14 let api: any;13 let api: any;91 await expectTxFailure(/rmrkCore\.CannotRejectNonPendingNft/, tx);90 await expectTxFailure(/rmrkCore\.CannotRejectNonPendingNft/, tx);92 });91 });939294 after(() => { api.disconnect(); });93 after(async() => { await api.disconnect(); });95});94});9695tests/src/rmrk/removeResource.test.tsdiffbeforeafterboth1import {expect} from 'chai';1import {expect} from 'chai';2import privateKey from '../substrate/privateKey';2import privateKey from '../substrate/privateKey';3import {executeTransaction, getApiConnection} from '../substrate/substrate-api';3import {executeTransaction, getApiConnection} from '../substrate/substrate-api';4import {requirePallets, Pallets} from '../deprecated-helpers/helpers';5import {getNft, NftIdTuple} from './util/fetch';4import {getNft, NftIdTuple} from './util/fetch';6import {expectTxFailure} from './util/helpers';5import {expectTxFailure, requirePallets, Pallets} from './util/helpers';7import {6import {8 acceptNft, acceptResourceRemoval, addNftBasicResource,7 acceptNft, acceptResourceRemoval, addNftBasicResource,9 createBase,8 createBase,344 await expectTxFailure(/rmrkCore\.NoPermission/, tx);343 await expectTxFailure(/rmrkCore\.NoPermission/, tx);345 });344 });346345347 after(() => {346 after(async() => { await api.disconnect(); });348 api.disconnect();349 });350});347});tests/src/rmrk/rmrkIsolation.test.tsdiffbeforeafterboth1import {expect} from 'chai';1import {executeTransaction} from '../substrate/substrate-api';2import usingApi, {executeTransaction} from '../substrate/substrate-api';3import {4 createCollectionExpectSuccess,5 createItemExpectSuccess,6 getCreateCollectionResult,7 getDetailedCollectionInfo,8 getGenericResult,9 requirePallets,10 normalizeAccountId,11 Pallets,12} from '../deprecated-helpers/helpers';13import {IKeyringPair} from '@polkadot/types/types';2import {IKeyringPair} from '@polkadot/types/types';14import {ApiPromise} from '@polkadot/api';3import {itSub, expect, usingPlaygrounds, Pallets, requirePalletsOrSkip} from '../util/playgrounds';15import {it} from 'mocha';4import {UniqueHelper} from '../util/playgrounds/unique';16517let alice: IKeyringPair;6let alice: IKeyringPair;18let bob: IKeyringPair;7let bob: IKeyringPair;19820async function createRmrkCollection(api: ApiPromise, sender: IKeyringPair): Promise<{uniqueId: number, rmrkId: number}> {9async function createRmrkCollection(helper: UniqueHelper, sender: IKeyringPair): Promise<{uniqueId: number, rmrkId: number}> {21 const tx = api.tx.rmrkCore.createCollection('metadata', null, 'symbol');10 const result = await helper.executeExtrinsic(sender, 'api.tx.rmrkCore.createCollection', ['metadata', null, 'symbol'], true);22 const events = await executeTransaction(api, sender, tx);231124 const uniqueResult = getCreateCollectionResult(events);12 const uniqueId = helper.util.extractCollectionIdFromCreationResult(result);1325 const rmrkResult = getGenericResult(events, 'rmrkCore', 'CollectionCreated', (data) => {14 let rmrkId = null;15 result.result.events.forEach(({event: {data, method, section}}) => {16 if ((section === 'rmrkCore') && (method === 'CollectionCreated')) {26 return parseInt(data[1].toString(), 10);17 rmrkId = parseInt(data[1].toString(), 10);18 }27 });19 });282021 if (rmrkId === null) {22 throw Error('No rmrkCore.CollectionCreated event was found!');23 }2429 return {25 return {30 uniqueId: uniqueResult.collectionId,26 uniqueId,31 rmrkId: rmrkResult.data!,27 rmrkId,32 };28 };33}29}343035async function createRmrkNft(api: ApiPromise, sender: IKeyringPair, collectionId: number): Promise<number> {31async function createRmrkNft(helper: UniqueHelper, sender: IKeyringPair, collectionId: number): Promise<number> {36 const tx = api.tx.rmrkCore.mintNft(32 const result = await helper.executeExtrinsic(33 sender,34 'api.tx.rmrkCore.mintNft',37 sender.address,35 [36 sender.address,38 collectionId,37 collectionId,39 sender.address,38 sender.address,40 null,39 null,41 'nft-metadata',40 'nft-metadata',41 true,42 null,43 ],42 true,44 true,43 null,44 );45 );4645 const events = await executeTransaction(api, sender, tx);47 let rmrkNftId = null;48 result.result.events.forEach(({event: {data, method, section}}) => {46 const result = getGenericResult(events, 'rmrkCore', 'NftMinted', (data) => {49 if ((section === 'rmrkCore') && (method === 'NftMinted')) {47 return parseInt(data[2].toString(), 10);50 rmrkNftId = parseInt(data[2].toString(), 10);51 }48 });52 });495350 return result.data!;54 if (rmrkNftId === null) {55 throw Error('No rmrkCore.NftMinted event was found!');51}56 }525753async function isUnique(): Promise<boolean> {58 return rmrkNftId;54 return usingApi(async api => {55 const chain = await api.rpc.system.chain();5657 return chain.eq('UNIQUE');58 });59}59}606061describe('RMRK External Integration Test', async () => {61describe('RMRK External Integration Test', async () => {62 const it_rmrk = (await isUnique() ? it : it.skip);6364 before(async function() {62 before(async function() {65 await usingApi(async (api, privateKeyWrapper) => {63 await usingPlaygrounds(async (_helper, privateKey) => {66 alice = privateKeyWrapper('//Alice');64 alice = privateKey('//Alice');67 await requirePallets(this, [Pallets.RmrkCore]);68 });65 });69 });66 });706771 it_rmrk('Creates a new RMRK collection that is mapped to a different ID and is tagged as external', async () => {68 itSub.ifWithPallets('Creates a new RMRK collection that is mapped to a different ID and is tagged as external', [Pallets.RmrkCore], async ({helper}) => {72 await usingApi(async api => {69 // throw away collection to bump last Unique collection ID to test ID mapping73 // throwaway collection to bump last Unique collection ID to test ID mapping74 await createCollectionExpectSuccess();70 await helper.nft.mintCollection(alice, {tokenPrefix: 'unqt'});757176 const collectionIds = await createRmrkCollection(api, alice);72 const collectionIds = await createRmrkCollection(helper, alice);777378 expect(collectionIds.rmrkId).to.be.lessThan(collectionIds.uniqueId, 'collection ID mapping');74 expect(collectionIds.rmrkId).to.be.lessThan(collectionIds.uniqueId, 'collection ID mapping');797580 const collection = (await getDetailedCollectionInfo(api, collectionIds.uniqueId))!;76 const collection = (await helper.nft.getCollectionObject(collectionIds.uniqueId).getData())!; // (await getDetailedCollectionInfo(api, collectionIds.uniqueId))!;81 expect(collection.readOnly.toHuman(), 'tagged external').to.be.true;77 expect(collection.raw.readOnly, 'tagged external').to.be.true;82 });83 });78 });84});79});858086describe('Negative Integration Test: External Collections, Internal Ops', async () => {81describe('Negative Integration Test: External Collections, Internal Ops', async () => {87 let uniqueCollectionId: number;82 let uniqueCollectionId: number;88 let rmrkCollectionId: number;83 let rmrkCollectionId: number;89 let rmrkNftId: number;84 let rmrkNftId: number;85 let normalizedAlice: {Substrate: string};908691 const it_rmrk = (await isUnique() ? it : it.skip);87 before(async function() {88 await usingPlaygrounds(async (helper, privateKey) => {89 alice = privateKey('//Alice');90 bob = privateKey('//Bob');91 normalizedAlice = {Substrate: helper.address.normalizeSubstrateToChainFormat(alice.address)};929293 before(async () => {93 await requirePalletsOrSkip(this, helper, [Pallets.RmrkCore]);94 await usingApi(async (api, privateKeyWrapper) => {95 alice = privateKeyWrapper('//Alice');96 bob = privateKeyWrapper('//Bob');979498 const collectionIds = await createRmrkCollection(api, alice);95 const collectionIds = await createRmrkCollection(helper, alice);99 uniqueCollectionId = collectionIds.uniqueId;96 uniqueCollectionId = collectionIds.uniqueId;100 rmrkCollectionId = collectionIds.rmrkId;97 rmrkCollectionId = collectionIds.rmrkId;10198102 rmrkNftId = await createRmrkNft(api, alice, rmrkCollectionId);99 rmrkNftId = await createRmrkNft(helper, alice, rmrkCollectionId);103 });100 });104 });101 });105102106 it_rmrk('[Negative] Forbids Unique operations with an external collection, handled by dispatch_call', async () => {103 itSub.ifWithPallets('[Negative] Forbids Unique operations with an external collection, handled by dispatch_call', [Pallets.RmrkCore], async ({helper}) => {107 await usingApi(async api => {108 // Collection item creation104 // Collection item creation109105110 const txCreateItem = api.tx.unique.createItem(uniqueCollectionId, normalizeAccountId(alice), 'NFT');106 await expect(helper.nft.mintToken(alice, {collectionId: uniqueCollectionId, owner: {Substrate: alice.address}}), 'creating item')111 await expect(executeTransaction(api, alice, txCreateItem), 'creating item')112 .to.be.rejectedWith(/common\.CollectionIsExternal/);107 .to.be.rejectedWith(/common\.CollectionIsExternal/);113108114 const txCreateMultipleItems = api.tx.unique.createMultipleItems(uniqueCollectionId, normalizeAccountId(alice), [{NFT: {}}, {NFT: {}}]);109 const txCreateMultipleItems = helper.getApi().tx.unique.createMultipleItems(uniqueCollectionId, normalizedAlice, [{NFT: {}}, {NFT: {}}]);115 await expect(executeTransaction(api, alice, txCreateMultipleItems), 'creating multiple')110 await expect(executeTransaction(helper.getApi(), alice, txCreateMultipleItems), 'creating multiple')116 .to.be.rejectedWith(/common\.CollectionIsExternal/);111 .to.be.rejectedWith(/common\.CollectionIsExternal/);112 113 await expect(helper.nft.mintMultipleTokens(alice, uniqueCollectionId, [{owner: {Substrate: alice.address}}]), 'creating multiple ex')114 .to.be.rejectedWith(/common\.CollectionIsExternal/);117115118 const txCreateMultipleItemsEx = api.tx.unique.createMultipleItemsEx(uniqueCollectionId, {NFT: [{}]});116 // Collection properties119 await expect(executeTransaction(api, alice, txCreateMultipleItemsEx), 'creating multiple ex')120 .to.be.rejectedWith(/common\.CollectionIsExternal/);121117122 // Collection properties118 await expect(helper.nft.setProperties(alice, uniqueCollectionId, [{key: 'a', value: '1'}, {key: 'b'}]), 'setting collection properties')119 .to.be.rejectedWith(/common\.CollectionIsExternal/);123120124 const txSetCollectionProperties = api.tx.unique.setCollectionProperties(uniqueCollectionId, [{key: 'a', value: '1'}, {key: 'b'}]);121 await expect(helper.nft.deleteProperties(alice, uniqueCollectionId, ['a']), 'deleting collection properties')125 await expect(executeTransaction(api, alice, txSetCollectionProperties), 'setting collection properties')126 .to.be.rejectedWith(/common\.CollectionIsExternal/);122 .to.be.rejectedWith(/common\.CollectionIsExternal/);127123128 const txDeleteCollectionProperties = api.tx.unique.deleteCollectionProperties(uniqueCollectionId, ['a']);124 await expect(helper.nft.setTokenPropertyPermissions(alice, uniqueCollectionId, [{key: 'a', permission: {mutable: true}}]), 'setting property permissions')129 await expect(executeTransaction(api, alice, txDeleteCollectionProperties), 'deleting collection properties')130 .to.be.rejectedWith(/common\.CollectionIsExternal/);125 .to.be.rejectedWith(/common\.CollectionIsExternal/);131126132 const txsetTokenPropertyPermissions = api.tx.unique.setTokenPropertyPermissions(uniqueCollectionId, [{key: 'a', permission: {mutable: true}}]);127 // NFT133 await expect(executeTransaction(api, alice, txsetTokenPropertyPermissions), 'setting property permissions')134 .to.be.rejectedWith(/common\.CollectionIsExternal/);135128136 // NFT129 await expect(helper.nft.burnToken(alice, uniqueCollectionId, rmrkNftId, 1n), 'burning')130 .to.be.rejectedWith(/common\.CollectionIsExternal/);137131138 const txBurn = api.tx.unique.burnItem(uniqueCollectionId, rmrkNftId, 1);132 await expect(helper.nft.burnTokenFrom(alice, uniqueCollectionId, rmrkNftId, {Substrate: alice.address}, 1n), 'burning-from')139 await expect(executeTransaction(api, alice, txBurn), 'burning').to.be.rejectedWith(/common\.CollectionIsExternal/);133 .to.be.rejectedWith(/common\.CollectionIsExternal/);140134141 const txBurnFrom = api.tx.unique.burnFrom(uniqueCollectionId, normalizeAccountId(alice), rmrkNftId, 1);135 await expect(helper.nft.transferToken(alice, uniqueCollectionId, rmrkNftId, {Substrate: bob.address}), 'transferring')142 await expect(executeTransaction(api, alice, txBurnFrom), 'burning-from').to.be.rejectedWith(/common\.CollectionIsExternal/);136 .to.be.rejectedWith(/common\.CollectionIsExternal/);143137144 const txTransfer = api.tx.unique.transfer(normalizeAccountId(bob), uniqueCollectionId, rmrkNftId, 1);138 await expect(helper.nft.approveToken(alice, uniqueCollectionId, rmrkNftId, {Substrate: bob.address}), 'approving')145 await expect(executeTransaction(api, alice, txTransfer), 'transferring').to.be.rejectedWith(/common\.CollectionIsExternal/);139 .to.be.rejectedWith(/common\.CollectionIsExternal/);146140147 const txApprove = api.tx.unique.approve(normalizeAccountId(bob), uniqueCollectionId, rmrkNftId, 1);141 await expect(helper.nft.transferTokenFrom(alice, uniqueCollectionId, rmrkNftId, {Substrate: alice.address}, {Substrate: bob.address}), 'transferring-from')148 await expect(executeTransaction(api, alice, txApprove), 'approving').to.be.rejectedWith(/common\.CollectionIsExternal/);142 .to.be.rejectedWith(/common\.CollectionIsExternal/);149143150 const txTransferFrom = api.tx.unique.transferFrom(normalizeAccountId(alice), normalizeAccountId(bob), uniqueCollectionId, rmrkNftId, 1);144 // NFT properties151 await expect(executeTransaction(api, alice, txTransferFrom), 'transferring-from').to.be.rejectedWith(/common\.CollectionIsExternal/);152145153 // NFT properties146 await expect(helper.nft.setTokenProperties(alice, uniqueCollectionId, rmrkNftId, [{key: 'a', value: '2'}]), 'setting token properties')147 .to.be.rejectedWith(/common\.CollectionIsExternal/);154148155 const txSetTokenProperties = api.tx.unique.setTokenProperties(uniqueCollectionId, rmrkNftId, [{key: 'a', value: '2'}]);149 await expect(helper.nft.deleteTokenProperties(alice, uniqueCollectionId, rmrkNftId, ['a']))156 await expect(executeTransaction(api, alice, txSetTokenProperties), 'setting token properties')157 .to.be.rejectedWith(/common\.CollectionIsExternal/);158159 const txDeleteTokenProperties = api.tx.unique.deleteTokenProperties(uniqueCollectionId, rmrkNftId, ['a']);160 await expect(executeTransaction(api, alice, txDeleteTokenProperties), 'deleting token properties')161 .to.be.rejectedWith(/common\.CollectionIsExternal/);150 .to.be.rejectedWith(/common\.CollectionIsExternal/);162 });163 });151 });164152165 it_rmrk('[Negative] Forbids Unique collection operations with an external collection', async () => {153 itSub.ifWithPallets('[Negative] Forbids Unique collection operations with an external collection', [Pallets.RmrkCore], async ({helper}) => {166 await usingApi(async api => {154 await expect(helper.nft.burn(alice, uniqueCollectionId), 'destroying collection')167 const txDestroyCollection = api.tx.unique.destroyCollection(uniqueCollectionId);168 await expect(executeTransaction(api, alice, txDestroyCollection), 'destroying collection')169 .to.be.rejectedWith(/common\.CollectionIsExternal/);155 .to.be.rejectedWith(/common\.CollectionIsExternal/);170156171 // Allow list157 // Allow list172158173 const txAddAllowList = api.tx.unique.addToAllowList(uniqueCollectionId, normalizeAccountId(bob));159 await expect(helper.nft.addToAllowList(alice, uniqueCollectionId, {Substrate: bob.address}), 'adding to allow list')174 await expect(executeTransaction(api, alice, txAddAllowList), 'adding to allow list')175 .to.be.rejectedWith(/common\.CollectionIsExternal/);160 .to.be.rejectedWith(/common\.CollectionIsExternal/);176161177 const txRemoveAllowList = api.tx.unique.removeFromAllowList(uniqueCollectionId, normalizeAccountId(bob));162 await expect(helper.nft.removeFromAllowList(alice, uniqueCollectionId, {Substrate: bob.address}), 'removing from allowlist')178 await expect(executeTransaction(api, alice, txRemoveAllowList), 'removing from allowlist')179 .to.be.rejectedWith(/common\.CollectionIsExternal/);163 .to.be.rejectedWith(/common\.CollectionIsExternal/);180164181 // Owner / Admin / Sponsor165 // Owner / Admin / Sponsor182166183 const txChangeOwner = api.tx.unique.changeCollectionOwner(uniqueCollectionId, bob.address);167 await expect(helper.nft.changeOwner(alice, uniqueCollectionId, bob.address), 'changing owner')184 await expect(executeTransaction(api, alice, txChangeOwner), 'changing owner')185 .to.be.rejectedWith(/common\.CollectionIsExternal/);168 .to.be.rejectedWith(/common\.CollectionIsExternal/);186169187 const txAddAdmin = api.tx.unique.addCollectionAdmin(uniqueCollectionId, normalizeAccountId(bob));170 await expect(helper.nft.addAdmin(alice, uniqueCollectionId, {Substrate: bob.address}), 'adding admin')188 await expect(executeTransaction(api, alice, txAddAdmin), 'adding admin')189 .to.be.rejectedWith(/common\.CollectionIsExternal/);171 .to.be.rejectedWith(/common\.CollectionIsExternal/);190172191 const txRemoveAdmin = api.tx.unique.removeCollectionAdmin(uniqueCollectionId, normalizeAccountId(bob));173 await expect(helper.nft.removeAdmin(alice, uniqueCollectionId, {Substrate: bob.address}), 'removing admin')192 await expect(executeTransaction(api, alice, txRemoveAdmin), 'removing admin')193 .to.be.rejectedWith(/common\.CollectionIsExternal/);174 .to.be.rejectedWith(/common\.CollectionIsExternal/);194175195 const txAddCollectionSponsor = api.tx.unique.setCollectionSponsor(uniqueCollectionId, bob.address);176 await expect(helper.nft.setSponsor(alice, uniqueCollectionId, bob.address), 'setting sponsor')196 await expect(executeTransaction(api, alice, txAddCollectionSponsor), 'setting sponsor')197 .to.be.rejectedWith(/common\.CollectionIsExternal/);177 .to.be.rejectedWith(/common\.CollectionIsExternal/);198178199 const txConfirmCollectionSponsor = api.tx.unique.confirmSponsorship(uniqueCollectionId);179 await expect(helper.nft.confirmSponsorship(alice, uniqueCollectionId), 'confirming sponsor')200 await expect(executeTransaction(api, alice, txConfirmCollectionSponsor), 'confirming sponsor')201 .to.be.rejectedWith(/common\.CollectionIsExternal/);180 .to.be.rejectedWith(/common\.CollectionIsExternal/);202181203 const txRemoveCollectionSponsor = api.tx.unique.removeCollectionSponsor(uniqueCollectionId);182 await expect(helper.nft.removeSponsor(alice, uniqueCollectionId), 'removing sponsor')204 await expect(executeTransaction(api, alice, txRemoveCollectionSponsor), 'removing sponsor')205 .to.be.rejectedWith(/common\.CollectionIsExternal/);183 .to.be.rejectedWith(/common\.CollectionIsExternal/);206 184 207 // Limits / permissions / transfers185 // Limits / permissions / transfers208186209 const txSetTransfers = api.tx.unique.setTransfersEnabledFlag(uniqueCollectionId, true);187 const txSetTransfers = helper.getApi().tx.unique.setTransfersEnabledFlag(uniqueCollectionId, true);210 await expect(executeTransaction(api, alice, txSetTransfers), 'setting transfers enabled flag')188 await expect(executeTransaction(helper.getApi(), alice, txSetTransfers), 'setting transfers enabled flag')211 .to.be.rejectedWith(/common\.CollectionIsExternal/);189 .to.be.rejectedWith(/common\.CollectionIsExternal/);212190213 const txSetLimits = api.tx.unique.setCollectionLimits(uniqueCollectionId, {transfersEnabled: false});191 await expect(helper.nft.setLimits(alice, uniqueCollectionId, {transfersEnabled: false}), 'setting collection limits')214 await expect(executeTransaction(api, alice, txSetLimits), 'setting collection limits')215 .to.be.rejectedWith(/common\.CollectionIsExternal/);192 .to.be.rejectedWith(/common\.CollectionIsExternal/);216193217 const txSetPermissions = api.tx.unique.setCollectionPermissions(uniqueCollectionId, {access: 'AllowList'});194 await expect(helper.nft.setPermissions(alice, uniqueCollectionId, {access: 'AllowList'}), 'setting collection permissions')218 await expect(executeTransaction(api, alice, txSetPermissions), 'setting collection permissions')219 .to.be.rejectedWith(/common\.CollectionIsExternal/);195 .to.be.rejectedWith(/common\.CollectionIsExternal/);220 });221 });196 });222});197});223198224describe('Negative Integration Test: Internal Collections, External Ops', async () => {199describe('Negative Integration Test: Internal Collections, External Ops', async () => {225 let collectionId: number;200 let collectionId: number;226 let nftId: number;201 let nftId: number;227202228 const it_rmrk = (await isUnique() ? it : it.skip);229230 before(async () => {203 before(async () => {231 await usingApi(async (api, privateKeyWrapper) => {204 await usingPlaygrounds(async (helper, privateKey) => {232 alice = privateKeyWrapper('//Alice');205 alice = privateKey('//Alice');233 bob = privateKeyWrapper('//Bob');206 bob = privateKey('//Bob');234207235 collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});208 const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'iceo'});236 nftId = await createItemExpectSuccess(alice, collectionId, 'NFT');209 collectionId = collection.collectionId;210 nftId = (await collection.mintToken(alice)).tokenId;237 });211 });238 });212 });239213240 it_rmrk('[Negative] Forbids RMRK operations with the internal collection and NFT (due to the lack of mapping)', async () => {214 itSub.ifWithPallets('[Negative] Forbids RMRK operations with the internal collection and NFT (due to the lack of mapping)', [Pallets.RmrkCore], async ({helper}) => {241 await usingApi(async api => {215 const api = helper.getApi();242 const txChangeOwner = api.tx.rmrkCore.changeCollectionIssuer(collectionId, bob.address);243 await expect(executeTransaction(api, alice, txChangeOwner), 'changing collection issuer')244 .to.be.rejectedWith(/rmrkCore\.CollectionUnknown/);245216246 const maxBurns = 10;217 const txChangeOwner = api.tx.rmrkCore.changeCollectionIssuer(collectionId, bob.address);218 await expect(executeTransaction(api, alice, txChangeOwner), 'changing collection issuer')219 .to.be.rejectedWith(/rmrkCore\.CollectionUnknown/);220221 const maxBurns = 10;247 const txBurnItem = api.tx.rmrkCore.burnNft(collectionId, nftId, maxBurns);222 const txBurnItem = api.tx.rmrkCore.burnNft(collectionId, nftId, maxBurns);248 await expect(executeTransaction(api, alice, txBurnItem), 'burning NFT').to.be.rejectedWith(/rmrkCore\.CollectionUnknown/);223 await expect(executeTransaction(api, alice, txBurnItem), 'burning NFT').to.be.rejectedWith(/rmrkCore\.CollectionUnknown/);249 });250 });224 });251});225});252226tests/src/rmrk/sendNft.test.tsdiffbeforeafterboth2import {getApiConnection} from '../substrate/substrate-api';2import {getApiConnection} from '../substrate/substrate-api';3import {createCollection, mintNft, sendNft} from './util/tx';3import {createCollection, mintNft, sendNft} from './util/tx';4import {NftIdTuple} from './util/fetch';4import {NftIdTuple} from './util/fetch';5import {isNftChildOfAnother, expectTxFailure} from './util/helpers';5import {isNftChildOfAnother, expectTxFailure, requirePallets, Pallets} from './util/helpers';6import {requirePallets, Pallets} from '../deprecated-helpers/helpers';768describe('integration test: send NFT', () => {7describe('integration test: send NFT', () => {9 let api: any;8 let api: any;252 await expectTxFailure(/rmrkCore\.NoPermission/, tx);251 await expectTxFailure(/rmrkCore\.NoPermission/, tx);253 });252 });254253255 after(() => { api.disconnect(); });254 after(async() => { await api.disconnect(); });256});255});257256tests/src/rmrk/setCollectionProperty.test.tsdiffbeforeafterboth1import {getApiConnection} from '../substrate/substrate-api';1import {getApiConnection} from '../substrate/substrate-api';2import {requirePallets, Pallets} from '../deprecated-helpers/helpers';2import {expectTxFailure, requirePallets, Pallets} from './util/helpers';3import {expectTxFailure} from './util/helpers';4import {createCollection, setPropertyCollection} from './util/tx';3import {createCollection, setPropertyCollection} from './util/tx';546describe('integration test: set collection property', () => {5describe('integration test: set collection property', () => {63 });62 });64 });63 });656466 after(() => {65 after(async() => { await api.disconnect(); });67 api.disconnect();68 });69});66});tests/src/rmrk/setEquippableList.test.tsdiffbeforeafterboth1import {getApiConnection} from '../substrate/substrate-api';1import {getApiConnection} from '../substrate/substrate-api';2import {requirePallets, Pallets} from '../deprecated-helpers/helpers';2import {expectTxFailure, requirePallets, Pallets} from './util/helpers';3import {expectTxFailure} from './util/helpers';4import {createCollection, createBase, setEquippableList} from './util/tx';3import {createCollection, createBase, setEquippableList} from './util/tx';546describe("integration test: set slot's Equippable List", () => {5describe("integration test: set slot's Equippable List", () => {111 await expectTxFailure(/rmrkEquip\.PartDoesntExist/, tx);110 await expectTxFailure(/rmrkEquip\.PartDoesntExist/, tx);112 });111 });113112114 after(() => { api.disconnect(); });113 after(async() => { await api.disconnect(); });115});114});116115tests/src/rmrk/setNftProperty.test.tsdiffbeforeafterboth1import {getApiConnection} from '../substrate/substrate-api';1import {getApiConnection} from '../substrate/substrate-api';2import {requirePallets, Pallets} from '../deprecated-helpers/helpers';3import {NftIdTuple} from './util/fetch';2import {NftIdTuple} from './util/fetch';4import {expectTxFailure} from './util/helpers';3import {expectTxFailure, requirePallets, Pallets} from './util/helpers';5import {createCollection, mintNft, sendNft, setNftProperty} from './util/tx';4import {createCollection, mintNft, sendNft, setNftProperty} from './util/tx';657describe('integration test: set NFT property', () => {6describe('integration test: set NFT property', () => {85 await expectTxFailure(/rmrkCore\.NoPermission/, tx);84 await expectTxFailure(/rmrkCore\.NoPermission/, tx);86 });85 });878688 after(() => { api.disconnect(); });87 after(async() => { await api.disconnect(); });89});88});9089tests/src/rmrk/setResourcePriorities.test.tsdiffbeforeafterboth1import {getApiConnection} from '../substrate/substrate-api';1import {getApiConnection} from '../substrate/substrate-api';2import {requirePallets, Pallets} from '../deprecated-helpers/helpers';2import {expectTxFailure, requirePallets, Pallets} from './util/helpers';3import {expectTxFailure} from './util/helpers';4import {mintNft, createCollection, setResourcePriorities} from './util/tx';3import {mintNft, createCollection, setResourcePriorities} from './util/tx';546describe('integration test: set NFT resource priorities', () => {5describe('integration test: set NFT resource priorities', () => {55 await expectTxFailure(/rmrkCore\.NoAvailableNftId/, tx);54 await expectTxFailure(/rmrkCore\.NoAvailableNftId/, tx);56 });55 });575658 after(() => { api.disconnect(); });57 after(async() => { await api.disconnect(); });59});58});6059tests/src/rmrk/util/helpers.tsdiffbeforeafterboth10import {NftIdTuple, getChildren, getOwnedNfts, getCollectionProperties, getNftProperties, getResources} from './fetch';10import {NftIdTuple, getChildren, getOwnedNfts, getCollectionProperties, getNftProperties, getResources} from './fetch';11import chaiAsPromised from 'chai-as-promised';11import chaiAsPromised from 'chai-as-promised';12import chai from 'chai';12import chai from 'chai';13import {getApiConnection} from '../../substrate/substrate-api';14import {Context} from 'mocha';131514chai.use(chaiAsPromised);16chai.use(chaiAsPromised);15const expect = chai.expect;17const expect = chai.expect;19 successData: T | null;21 successData: T | null;20}22}2324export enum Pallets {25 Inflation = 'inflation',26 RmrkCore = 'rmrkcore',27 RmrkEquip = 'rmrkequip',28 ReFungible = 'refungible',29 Fungible = 'fungible',30 NFT = 'nonfungible',31 Scheduler = 'scheduler',32 AppPromotion = 'apppromotion',33}3435let modulesNames: any;36export function getModuleNames(api: ApiPromise): string[] {37 if (typeof modulesNames === 'undefined')38 modulesNames = api.runtimeMetadata.asLatest.pallets.map(m => m.name.toString().toLowerCase());39 return modulesNames;40}4142export async function missingRequiredPallets(requiredPallets: string[]): Promise<string[]> {43 const api = await getApiConnection();44 const pallets = getModuleNames(api);45 await api.disconnect();4647 return requiredPallets.filter(p => !pallets.includes(p));48}4950export async function requirePallets(mocha: Context, requiredPallets: string[]) {51 const missingPallets = await missingRequiredPallets(requiredPallets);5253 if (missingPallets.length > 0) {54 const skippingTestMsg = `\tSkipping test "${mocha.test?.title}".`;55 const missingPalletsMsg = `\tThe following pallets are missing:\n\t- ${missingPallets.join('\n\t- ')}`;56 const skipMsg = `${skippingTestMsg}\n${missingPalletsMsg}`;5758 console.error('\x1b[38:5:208m%s\x1b[0m', skipMsg);5960 mocha.skip();61 }62}216322export function makeNftOwner(api: ApiPromise, owner: string | NftIdTuple): NftOwner {64export function makeNftOwner(api: ApiPromise, owner: string | NftIdTuple): NftOwner {23 const isNftSending = (typeof owner !== 'string');65 const isNftSending = (typeof owner !== 'string');tests/src/util/playgrounds/index.tsdiffbeforeafterboth7import {Context} from 'mocha';7import {Context} from 'mocha';8import config from '../../config';8import config from '../../config';9import '../../interfaces/augment-api-events';9import '../../interfaces/augment-api-events';10import {ChainHelperBase} from './unique';11import {ILogger} from './types';10import {DevUniqueHelper, SilentLogger, SilentConsole} from './unique.dev';12import {DevUniqueHelper, SilentLogger, SilentConsole, DevMoonbeamHelper, DevMoonriverHelper, DevAcalaHelper, DevKaruraHelper, DevRelayHelper, DevWestmintHelper} from './unique.dev';111312chai.use(chaiAsPromised);14chai.use(chaiAsPromised);13export const expect = chai.expect;15export const expect = chai.expect;141615export const usingPlaygrounds = async (code: (helper: DevUniqueHelper, privateKey: (seed: string) => IKeyringPair) => Promise<void>, url: string = config.substrateUrl) => {17async function usingPlaygroundsGeneral<T extends ChainHelperBase>(helperType: new(logger: ILogger) => T, url: string, code: (helper: T, privateKey: (seed: string) => IKeyringPair) => Promise<void>) {16 const silentConsole = new SilentConsole();18 const silentConsole = new SilentConsole();17 silentConsole.enable();19 silentConsole.enable();182019 const helper = new DevUniqueHelper(new SilentLogger());21 const helper = new helperType(new SilentLogger());202221 try {23 try {22 await helper.connect(url);24 await helper.connect(url);28 await helper.disconnect();30 await helper.disconnect();29 silentConsole.disable();31 silentConsole.disable();30 }32 }31};33}3435export const usingPlaygrounds = (code: (helper: DevUniqueHelper, privateKey: (seed: string) => IKeyringPair) => Promise<void>, url: string = config.substrateUrl) => {36 return usingPlaygroundsGeneral<DevUniqueHelper>(DevUniqueHelper, url, code);37};3839export const usingWestmintPlaygrounds = async (url: string, code: (helper: DevWestmintHelper, privateKey: (seed: string) => IKeyringPair) => Promise<void>) => {40 return usingPlaygroundsGeneral<DevWestmintHelper>(DevWestmintHelper, url, code);41};4243export const usingRelayPlaygrounds = async (url: string, code: (helper: DevRelayHelper, privateKey: (seed: string) => IKeyringPair) => Promise<void>) => {44 return usingPlaygroundsGeneral<DevRelayHelper>(DevRelayHelper, url, code);45};4647export const usingAcalaPlaygrounds = async (url: string, code: (helper: DevAcalaHelper, privateKey: (seed: string) => IKeyringPair) => Promise<void>) => {48 return usingPlaygroundsGeneral<DevAcalaHelper>(DevAcalaHelper, url, code);49};5051export const usingKaruraPlaygrounds = async (url: string, code: (helper: DevKaruraHelper, privateKey: (seed: string) => IKeyringPair) => Promise<void>) => {52 return usingPlaygroundsGeneral<DevKaruraHelper>(DevAcalaHelper, url, code);53};5455export const usingMoonbeamPlaygrounds = async (url: string, code: (helper: DevMoonbeamHelper, privateKey: (seed: string) => IKeyringPair) => Promise<void>) => {56 return usingPlaygroundsGeneral<DevMoonbeamHelper>(DevMoonbeamHelper, url, code);57};5859export const usingMoonriverPlaygrounds = async (url: string, code: (helper: DevMoonbeamHelper, privateKey: (seed: string) => IKeyringPair) => Promise<void>) => {60 return usingPlaygroundsGeneral<DevMoonriverHelper>(DevMoonriverHelper, url, code);61};326233export enum Pallets {63export enum Pallets {34 Inflation = 'inflation',64 Inflation = 'inflation',73itSubIfWithPallet.skip = (name: string, required: string[], cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => IKeyringPair }) => any) => itSubIfWithPallet(name, required, cb, {skip: true});103itSubIfWithPallet.skip = (name: string, required: string[], cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => IKeyringPair }) => any) => itSubIfWithPallet(name, required, cb, {skip: true});74itSub.ifWithPallets = itSubIfWithPallet;104itSub.ifWithPallets = itSubIfWithPallet;105106export async function describeXcm(name: string, cb: () => any) {107 (108 process.env.RUN_XCM_TESTS ? describe : describe.skip109 )(name, cb);110}75111tests/src/util/playgrounds/types.tsdiffbeforeafterboth172 },172 },173}173}174175export interface IForeignAssetMetadata {176 name?: number | Uint8Array,177 symbol?: string,178 decimals?: number,179 minimalBalance?: bigint,180}181182export interface MoonbeamAssetInfo {183 location: any,184 metadata: {185 name: string,186 symbol: string,187 decimals: number,188 isFrozen: boolean,189 minimalBalance: bigint,190 },191 existentialDeposit: bigint,192 isSufficient: boolean,193 unitsPerSecond: bigint,194 numAssetsWeightHint: number,195}196197export interface AcalaAssetMetadata {198 name: string,199 symbol: string,200 decimals: number,201 minimalBalance: bigint,202}203204export interface DemocracyStandardAccountVote {205 balance: bigint,206 vote: {207 aye: boolean,208 conviction: number,209 },210}174211175export type TSubstrateAccount = string;212export type TSubstrateAccount = string;176export type TEthereumAccount = string;213export type TEthereumAccount = string;177export type TApiAllowedListeners = 'connected' | 'disconnected' | 'error' | 'ready' | 'decorated';214export type TApiAllowedListeners = 'connected' | 'disconnected' | 'error' | 'ready' | 'decorated';178export type TUniqueNetworks = 'opal' | 'quartz' | 'unique';215export type TUniqueNetworks = 'opal' | 'quartz' | 'unique';216export type TSiblingNetworkds = 'moonbeam' | 'moonriver' | 'acala' | 'karura' | 'westmint';217export type TRelayNetworks = 'rococo' | 'westend';218export type TNetworks = TUniqueNetworks | TSiblingNetworkds | TRelayNetworks;179export type TSigner = IKeyringPair; // | 'string'219export type TSigner = IKeyringPair; // | 'string'180220tests/src/util/playgrounds/unique.dev.tsdiffbeforeafterboth2// SPDX-License-Identifier: Apache-2.02// SPDX-License-Identifier: Apache-2.0334import {mnemonicGenerate} from '@polkadot/util-crypto';4import {mnemonicGenerate} from '@polkadot/util-crypto';5import {UniqueHelper} from './unique';5import {UniqueHelper, MoonbeamHelper, ChainHelperBase, AcalaHelper, RelayHelper, WestmintHelper} from './unique';6import {ApiPromise, WsProvider} from '@polkadot/api';6import {ApiPromise, Keyring, WsProvider} from '@polkadot/api';7import * as defs from '../../interfaces/definitions';7import * as defs from '../../interfaces/definitions';8import {IKeyringPair} from '@polkadot/types/types';8import {IKeyringPair} from '@polkadot/types/types';9import {EventRecord} from '@polkadot/types/interfaces';9import {ICrossAccountId} from './types';10import {ICrossAccountId} from './types';11import {FrameSystemEventRecord} from '@polkadot/types/lookup';101211export class SilentLogger {13export class SilentLogger {12 log(_msg: any, _level: any): void { }14 log(_msg: any, _level: any): void { }108 }109 }109}110}111112export class DevRelayHelper extends RelayHelper {}113114export class DevWestmintHelper extends WestmintHelper {115 wait: WaitGroup;116117 constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {118 options.helperBase = options.helperBase ?? DevWestmintHelper;119120 super(logger, options);121 this.wait = new WaitGroup(this);122 }123}124125export class DevMoonbeamHelper extends MoonbeamHelper {126 account: MoonbeamAccountGroup;127 wait: WaitGroup;128129 constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {130 options.helperBase = options.helperBase ?? DevMoonbeamHelper;131132 super(logger, options);133 this.account = new MoonbeamAccountGroup(this);134 this.wait = new WaitGroup(this);135 }136}137138export class DevMoonriverHelper extends DevMoonbeamHelper {}139140export class DevAcalaHelper extends AcalaHelper {141 wait: WaitGroup;142143 constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {144 options.helperBase = options.helperBase ?? DevAcalaHelper;145146 super(logger, options);147 this.wait = new WaitGroup(this);148 }149}150151export class DevKaruraHelper extends DevAcalaHelper {}110152111class ArrangeGroup {153class ArrangeGroup {112 helper: DevUniqueHelper;154 helper: DevUniqueHelper;245 }287 }246}288}289290class MoonbeamAccountGroup {291 helper: MoonbeamHelper;292293 keyring: Keyring;294 _alithAccount: IKeyringPair;295 _baltatharAccount: IKeyringPair;296 _dorothyAccount: IKeyringPair;297298 constructor(helper: MoonbeamHelper) {299 this.helper = helper;300301 this.keyring = new Keyring({type: 'ethereum'});302 const alithPrivateKey = '0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133';303 const baltatharPrivateKey = '0x8075991ce870b93a8870eca0c0f91913d12f47948ca0fd25b49c6fa7cdbeee8b';304 const dorothyPrivateKey = '0x39539ab1876910bbf3a223d84a29e28f1cb4e2e456503e7e91ed39b2e7223d68';305306 this._alithAccount = this.keyring.addFromUri(alithPrivateKey, undefined, 'ethereum');307 this._baltatharAccount = this.keyring.addFromUri(baltatharPrivateKey, undefined, 'ethereum');308 this._dorothyAccount = this.keyring.addFromUri(dorothyPrivateKey, undefined, 'ethereum');309 }310311 alithAccount() {312 return this._alithAccount;313 }314315 baltatharAccount() {316 return this._baltatharAccount;317 }318319 dorothyAccount() {320 return this._dorothyAccount;321 }322323 create() {324 return this.keyring.addFromUri(mnemonicGenerate());325 }326}247327248class WaitGroup {328class WaitGroup {249 helper: DevUniqueHelper;329 helper: ChainHelperBase;250330251 constructor(helper: DevUniqueHelper) {331 constructor(helper: ChainHelperBase) {252 this.helper = helper;332 this.helper = helper;253 }333 }254334297 });377 });298 }378 }379380 async event(maxBlocksToWait: number, eventSection: string, eventMethod: string) {381 // eslint-disable-next-line no-async-promise-executor382 const promise = new Promise<EventRecord | null>(async (resolve) => {383 const unsubscribe = await this.helper.getApi().rpc.chain.subscribeNewHeads(async header => {384 const blockNumber = header.number.toHuman();385 const blockHash = header.hash;386 const eventIdStr = `${eventSection}.${eventMethod}`;387 const waitLimitStr = `wait blocks remaining: ${maxBlocksToWait}`;388 389 this.helper.logger.log(`[Block #${blockNumber}] Waiting for event \`${eventIdStr}\` (${waitLimitStr})`);390 391 const apiAt = await this.helper.getApi().at(blockHash);392 const eventRecords = (await apiAt.query.system.events()) as any;393 394 const neededEvent = eventRecords.toArray().find((r: FrameSystemEventRecord) => {395 return r.event.section == eventSection && r.event.method == eventMethod;396 });397 398 if (neededEvent) {399 unsubscribe();400 resolve(neededEvent);401 } else if (maxBlocksToWait > 0) {402 maxBlocksToWait--;403 } else {404 this.helper.logger.log(`Event \`${eventIdStr}\` is NOT found`);405 406 unsubscribe();407 resolve(null);408 }409 });410 });411 return promise;412 }299}413}300414301class AdminGroup {415class AdminGroup {tests/src/util/playgrounds/unique.tsdiffbeforeafterboth9import {ApiInterfaceEvents, SignerOptions} from '@polkadot/api/types';9import {ApiInterfaceEvents, SignerOptions} from '@polkadot/api/types';10import {encodeAddress, decodeAddress, keccakAsHex, evmToAddress, addressToEvm} from '@polkadot/util-crypto';10import {encodeAddress, decodeAddress, keccakAsHex, evmToAddress, addressToEvm} from '@polkadot/util-crypto';11import {IKeyringPair} from '@polkadot/types/types';11import {IKeyringPair} from '@polkadot/types/types';12import {IApiListeners, IBlock, IEvent, IChainProperties, ICollectionCreationOptions, ICollectionLimits, ICollectionPermissions, ICrossAccountId, ICrossAccountIdLower, ILogger, INestingPermissions, IProperty, IStakingInfo, ISchedulerOptions, ISubstrateBalance, IToken, ITokenPropertyPermission, ITransactionResult, IUniqueHelperLog, TApiAllowedListeners, TEthereumAccount, TSigner, TSubstrateAccount, TUniqueNetworks} from './types';12import {IApiListeners, IBlock, IEvent, IChainProperties, ICollectionCreationOptions, ICollectionLimits, ICollectionPermissions, ICrossAccountId, ICrossAccountIdLower, ILogger, INestingPermissions, IProperty, IStakingInfo, ISchedulerOptions, ISubstrateBalance, IToken, ITokenPropertyPermission, ITransactionResult, IUniqueHelperLog, TApiAllowedListeners, TEthereumAccount, TSigner, TSubstrateAccount, IForeignAssetMetadata, TNetworks, MoonbeamAssetInfo, DemocracyStandardAccountVote, AcalaAssetMetadata} from './types';131314export class CrossAccountId implements ICrossAccountId {14export class CrossAccountId implements ICrossAccountId {15 Substrate?: TSubstrateAccount;15 Substrate?: TSubstrateAccount;252 isSuccess = isSuccess && amount === transfer.amount;252 isSuccess = isSuccess && amount === transfer.amount;253 return isSuccess;253 return isSuccess;254 }254 }255256 static bigIntToDecimals(number: bigint, decimals = 18) {257 const numberStr = number.toString();258 const dotPos = numberStr.length - decimals;259 260 if (dotPos <= 0) {261 return '0.' + '0'.repeat(Math.abs(dotPos)) + numberStr;262 } else {263 const intPart = numberStr.substring(0, dotPos);264 const fractPart = numberStr.substring(dotPos);265 return intPart + '.' + fractPart;266 }267 }255}268}256269257class UniqueEventHelper {270class UniqueEventHelper {308 }321 }309}322}310323311class ChainHelperBase {324export class ChainHelperBase {325 helperBase: any;326312 transactionStatus = UniqueUtil.transactionStatus;327 transactionStatus = UniqueUtil.transactionStatus;313 chainLogType = UniqueUtil.chainLogType;328 chainLogType = UniqueUtil.chainLogType;314 util: typeof UniqueUtil;329 util: typeof UniqueUtil;315 eventHelper: typeof UniqueEventHelper;330 eventHelper: typeof UniqueEventHelper;316 logger: ILogger;331 logger: ILogger;317 api: ApiPromise | null;332 api: ApiPromise | null;318 forcedNetwork: TUniqueNetworks | null;333 forcedNetwork: TNetworks | null;319 network: TUniqueNetworks | null;334 network: TNetworks | null;320 chainLog: IUniqueHelperLog[];335 chainLog: IUniqueHelperLog[];321 children: ChainHelperBase[];336 children: ChainHelperBase[];337 address: AddressGroup;338 chain: ChainGroup;322339323 constructor(logger?: ILogger) {340 constructor(logger?: ILogger, helperBase?: any) {341 this.helperBase = helperBase;342324 this.util = UniqueUtil;343 this.util = UniqueUtil;325 this.eventHelper = UniqueEventHelper;344 this.eventHelper = UniqueEventHelper;330 this.network = null;349 this.network = null;331 this.chainLog = [];350 this.chainLog = [];332 this.children = [];351 this.children = [];352 this.address = new AddressGroup(this);353 this.chain = new ChainGroup(this);333 }354 }334355356 clone(helperCls: ChainHelperBaseConstructor, options: {[key: string]: any} = {}) {357 Object.setPrototypeOf(helperCls.prototype, this);358 const newHelper = new helperCls(this.logger, options);359360 newHelper.api = this.api;361 newHelper.network = this.network;362 newHelper.forceNetwork = this.forceNetwork;363364 this.children.push(newHelper);365366 return newHelper;367 }368335 getApi(): ApiPromise {369 getApi(): ApiPromise {336 if(this.api === null) throw Error('API not initialized');370 if(this.api === null) throw Error('API not initialized');337 return this.api;371 return this.api;341 this.chainLog = [];375 this.chainLog = [];342 }376 }343377344 forceNetwork(value: TUniqueNetworks): void {378 forceNetwork(value: TNetworks): void {345 this.forcedNetwork = value;379 this.forcedNetwork = value;346 }380 }347381367 this.network = null;401 this.network = null;368 }402 }369403370 static async detectNetwork(api: ApiPromise): Promise<TUniqueNetworks> {404 static async detectNetwork(api: ApiPromise): Promise<TNetworks> {371 const spec = (await api.query.system.lastRuntimeUpgrade()).toJSON() as any;405 const spec = (await api.query.system.lastRuntimeUpgrade()).toJSON() as any;406 const xcmChains = ['rococo', 'westend', 'westmint', 'acala', 'karura', 'moonbeam', 'moonriver'];407408 if(xcmChains.indexOf(spec.specName) > -1) return spec.specName;409372 if(['quartz', 'unique'].indexOf(spec.specName) > -1) return spec.specName;410 if(['quartz', 'unique'].indexOf(spec.specName) > -1) return spec.specName;373 return 'opal';411 return 'opal';374 }412 }375413376 static async detectNetworkByWsEndpoint(wsEndpoint: string): Promise<TUniqueNetworks> {414 static async detectNetworkByWsEndpoint(wsEndpoint: string): Promise<TNetworks> {377 const api = new ApiPromise({provider: new WsProvider(wsEndpoint)});415 const api = new ApiPromise({provider: new WsProvider(wsEndpoint)});378 await api.isReady;416 await api.isReady;379417384 return network;422 return network;385 }423 }386424387 static async createConnection(wsEndpoint: string, listeners?: IApiListeners, network?: TUniqueNetworks | null): Promise<{425 static async createConnection(wsEndpoint: string, listeners?: IApiListeners, network?: TNetworks | null): Promise<{388 api: ApiPromise;426 api: ApiPromise;389 network: TUniqueNetworks;427 network: TNetworks;390 }> {428 }> {391 if(typeof network === 'undefined' || network === null) network = 'opal';429 if(typeof network === 'undefined' || network === null) network = 'opal';392 const supportedRPC = {430 const supportedRPC = {399 unique: {437 unique: {400 unique: require('@unique-nft/unique-mainnet-types/definitions').unique.rpc,438 unique: require('@unique-nft/unique-mainnet-types/definitions').unique.rpc,401 },439 },440 rococo: {},441 westend: {},442 moonbeam: {},443 moonriver: {},444 acala: {},445 karura: {},446 westmint: {},402 };447 };403 if(!supportedRPC.hasOwnProperty(network)) network = await this.detectNetworkByWsEndpoint(wsEndpoint);448 if(!supportedRPC.hasOwnProperty(network)) network = await this.detectNetworkByWsEndpoint(wsEndpoint);404 const rpc = supportedRPC[network];449 const rpc = supportedRPC[network];590}635}591636592637593class HelperGroup {638class HelperGroup<T extends ChainHelperBase> {594 helper: UniqueHelper;639 helper: T;595640596 constructor(uniqueHelper: UniqueHelper) {641 constructor(uniqueHelper: T) {597 this.helper = uniqueHelper;642 this.helper = uniqueHelper;598 }643 }599}644}600645601646602class CollectionGroup extends HelperGroup {647class CollectionGroup extends HelperGroup<UniqueHelper> {603 /**648 /**604 * Get number of blocks when sponsored transaction is available.649 * Get number of blocks when sponsored transaction is available.605 *650 *2000}2045}20012046200220472003class ChainGroup extends HelperGroup {2048class ChainGroup extends HelperGroup<ChainHelperBase> {2004 /**2049 /**2005 * Get system properties of a chain2050 * Get system properties of a chain2006 * @example getChainProperties();2051 * @example getChainProperties();2054 }2099 }2055}2100}205621012102class SubstrateBalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2103 /**2104 * Get substrate address balance2105 * @param address substrate address2106 * @example getSubstrate("5GrwvaEF5zXb26Fz...")2107 * @returns amount of tokens on address2108 */2109 async getSubstrate(address: TSubstrateAccount): Promise<bigint> {2110 return (await this.helper.callRpc('api.query.system.account', [address])).data.free.toBigInt();2111 }205721122058class BalanceGroup extends HelperGroup {2113 /**2114 * Transfer tokens to substrate address2115 * @param signer keyring of signer2116 * @param address substrate address of a recipient2117 * @param amount amount of tokens to be transfered2118 * @example transferToSubstrate(aliceKeyring, "5GrwvaEF5zXb26Fz...", 100_000_000_000n);2119 * @returns ```true``` if extrinsic success, otherwise ```false```2120 */2121 async transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2122 const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true/*, `Unable to transfer balance from ${this.helper.getSignerAddress(signer)} to ${address}`*/);21232124 let transfer = {from: null, to: null, amount: 0n} as any;2125 result.result.events.forEach(({event: {data, method, section}}) => {2126 if ((section === 'balances') && (method === 'Transfer')) {2127 transfer = {2128 from: this.helper.address.normalizeSubstrate(data[0]),2129 to: this.helper.address.normalizeSubstrate(data[1]),2130 amount: BigInt(data[2]),2131 };2132 }2133 });2134 const isSuccess = this.helper.address.normalizeSubstrate(typeof signer === 'string' ? signer : signer.address) === transfer.from 2135 && this.helper.address.normalizeSubstrate(address) === transfer.to 2136 && BigInt(amount) === transfer.amount;2137 return isSuccess;2138 }21392140 /**2141 * Get full substrate balance including free, miscFrozen, feeFrozen, and reserved2142 * @param address substrate address2143 * @returns2144 */2145 async getSubstrateFull(address: TSubstrateAccount): Promise<ISubstrateBalance> {2146 const accountInfo = (await this.helper.callRpc('api.query.system.account', [address])).data;2147 return {free: accountInfo.free.toBigInt(), miscFrozen: accountInfo.miscFrozen.toBigInt(), feeFrozen: accountInfo.feeFrozen.toBigInt(), reserved: accountInfo.reserved.toBigInt()};2148 }2149}21502151class EthereumBalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2152 /**2153 * Get ethereum address balance2154 * @param address ethereum address2155 * @example getEthereum("0x9F0583DbB855d...")2156 * @returns amount of tokens on address2157 */2158 async getEthereum(address: TEthereumAccount): Promise<bigint> {2159 return (await this.helper.callRpc('api.rpc.eth.getBalance', [address])).toBigInt();2160 }21612162 /**2163 * Transfer tokens to address2164 * @param signer keyring of signer2165 * @param address Ethereum address of a recipient2166 * @param amount amount of tokens to be transfered2167 * @example transferToEthereum(alithKeyring, "0x9F0583DbB855d...", 100_000_000_000n);2168 * @returns ```true``` if extrinsic success, otherwise ```false```2169 */2170 async transferToEthereum(signer: TSigner, address: TEthereumAccount, amount: bigint | string): Promise<boolean> {2171 const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true);21722173 let transfer = {from: null, to: null, amount: 0n} as any;2174 result.result.events.forEach(({event: {data, method, section}}) => {2175 if ((section === 'balances') && (method === 'Transfer')) {2176 transfer = {2177 from: data[0].toString(),2178 to: data[1].toString(),2179 amount: BigInt(data[2]),2180 };2181 }2182 });2183 const isSuccess = (typeof signer === 'string' ? signer : signer.address) === transfer.from 2184 && address === transfer.to 2185 && BigInt(amount) === transfer.amount;2186 return isSuccess;2187 }2188}21892190class BalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2191 subBalanceGroup: SubstrateBalanceGroup<T>;2192 ethBalanceGroup: EthereumBalanceGroup<T>;21932194 constructor(helper: T) {2195 super(helper);2196 this.subBalanceGroup = new SubstrateBalanceGroup(helper);2197 this.ethBalanceGroup = new EthereumBalanceGroup(helper);2198 }21992059 getCollectionCreationPrice(): bigint {2200 getCollectionCreationPrice(): bigint {2060 return 2n * this.helper.balance.getOneTokenNominal();2201 return 2n * this.getOneTokenNominal();2061 }2202 }2062 /**2203 /**2063 * Representation of the native token in the smallest unit - one OPAL (OPL), QUARTZ (QTZ), or UNIQUE (UNQ).2204 * Representation of the native token in the smallest unit - one OPAL (OPL), QUARTZ (QTZ), or UNIQUE (UNQ).2076 * @returns amount of tokens on address2217 * @returns amount of tokens on address2077 */2218 */2078 async getSubstrate(address: TSubstrateAccount): Promise<bigint> {2219 async getSubstrate(address: TSubstrateAccount): Promise<bigint> {2079 return (await this.helper.callRpc('api.query.system.account', [address])).data.free.toBigInt();2220 return this.subBalanceGroup.getSubstrate(address);2080 }2221 }208122222082 /**2223 /**2085 * @returns2226 * @returns2086 */2227 */2087 async getSubstrateFull(address: TSubstrateAccount): Promise<ISubstrateBalance> {2228 async getSubstrateFull(address: TSubstrateAccount): Promise<ISubstrateBalance> {2088 const accountInfo = (await this.helper.callRpc('api.query.system.account', [address])).data;2229 return this.subBalanceGroup.getSubstrateFull(address);2089 return {free: accountInfo.free.toBigInt(), miscFrozen: accountInfo.miscFrozen.toBigInt(), feeFrozen: accountInfo.feeFrozen.toBigInt(), reserved: accountInfo.reserved.toBigInt()};2090 }2230 }209122312092 /**2232 /**2096 * @returns amount of tokens on address2236 * @returns amount of tokens on address2097 */2237 */2098 async getEthereum(address: TEthereumAccount): Promise<bigint> {2238 async getEthereum(address: TEthereumAccount): Promise<bigint> {2099 return (await this.helper.callRpc('api.rpc.eth.getBalance', [address])).toBigInt();2239 return this.ethBalanceGroup.getEthereum(address);2100 }2240 }210122412102 /**2242 /**2108 * @returns ```true``` if extrinsic success, otherwise ```false```2248 * @returns ```true``` if extrinsic success, otherwise ```false```2109 */2249 */2110 async transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2250 async transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2111 const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true/*, `Unable to transfer balance from ${this.helper.getSignerAddress(signer)} to ${address}`*/);2251 return this.subBalanceGroup.transferToSubstrate(signer, address, amount);21122113 let transfer = {from: null, to: null, amount: 0n} as any;2114 result.result.events.forEach(({event: {data, method, section}}) => {2115 if ((section === 'balances') && (method === 'Transfer')) {2116 transfer = {2117 from: this.helper.address.normalizeSubstrate(data[0]),2118 to: this.helper.address.normalizeSubstrate(data[1]),2119 amount: BigInt(data[2]),2120 };2121 }2122 });2123 const isSuccess = this.helper.address.normalizeSubstrate(typeof signer === 'string' ? signer : signer.address) === transfer.from 2124 && this.helper.address.normalizeSubstrate(address) === transfer.to 2125 && BigInt(amount) === transfer.amount;2126 return isSuccess;2127 }2252 }2128}2253}2129225421302131class AddressGroup extends HelperGroup {2255class AddressGroup extends HelperGroup<ChainHelperBase> {2132 /**2256 /**2133 * Normalizes the address to the specified ss58 format, by default ```42```.2257 * Normalizes the address to the specified ss58 format, by default ```42```.2134 * @param address substrate address2258 * @param address substrate address2170 substrateToEth(subAddress: TSubstrateAccount): TEthereumAccount {2294 substrateToEth(subAddress: TSubstrateAccount): TEthereumAccount {2171 return CrossAccountId.translateSubToEth(subAddress);2295 return CrossAccountId.translateSubToEth(subAddress);2172 }2296 }22972298 paraSiblingSovereignAccount(paraid: number) {2299 // We are getting a *sibling* parachain sovereign account,2300 // so we need a sibling prefix: encoded(b"sibl") == 0x7369626c2301 const siblingPrefix = '0x7369626c';23022303 const encodedParaId = this.helper.getApi().createType('u32', paraid).toHex(true).substring(2);2304 const suffix = '000000000000000000000000000000000000000000000000';23052306 return siblingPrefix + encodedParaId + suffix;2307 }2173}2308}217423092175class StakingGroup extends HelperGroup {2310class StakingGroup extends HelperGroup<UniqueHelper> {2176 /**2311 /**2177 * Stake tokens for App Promotion2312 * Stake tokens for App Promotion2178 * @param signer keyring of signer2313 * @param signer keyring of signer2258 }2393 }2259}2394}226023952261class SchedulerGroup extends HelperGroup {2396class SchedulerGroup extends HelperGroup<UniqueHelper> {2262 constructor(helper: UniqueHelper) {2397 constructor(helper: UniqueHelper) {2263 super(helper);2398 super(helper);2264 }2399 }2314 }2449 }2315}2450}231624512452class ForeignAssetsGroup extends HelperGroup<UniqueHelper> {2453 async register(signer: TSigner, ownerAddress: TSubstrateAccount, location: any, metadata: IForeignAssetMetadata) {2454 await this.helper.executeExtrinsic(2455 signer,2456 'api.tx.foreignAssets.registerForeignAsset',2457 [ownerAddress, location, metadata],2458 true,2459 );2460 }24612462 async update(signer: TSigner, foreignAssetId: number, location: any, metadata: IForeignAssetMetadata) {2463 await this.helper.executeExtrinsic(2464 signer,2465 'api.tx.foreignAssets.updateForeignAsset',2466 [foreignAssetId, location, metadata],2467 true,2468 );2469 }2470}24712472class XcmGroup<T extends ChainHelperBase> extends HelperGroup<T> {2473 palletName: string;24742475 constructor(helper: T, palletName: string) {2476 super(helper);24772478 this.palletName = palletName;2479 }24802481 async limitedReserveTransferAssets(signer: TSigner, destination: any, beneficiary: any, assets: any, feeAssetItem: number, weightLimit: number) {2482 await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.limitedReserveTransferAssets`, [destination, beneficiary, assets, feeAssetItem, {Limited: weightLimit}], true);2483 }2484}24852486class XTokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {2487 async transfer(signer: TSigner, currencyId: any, amount: bigint, destination: any, destWeight: number) {2488 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transfer', [currencyId, amount, destination, destWeight], true);2489 }24902491 async transferMultiasset(signer: TSigner, asset: any, destination: any, destWeight: number) {2492 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMultiasset', [asset, destination, destWeight], true);2493 }24942495 async transferMulticurrencies(signer: TSigner, currencies: any[], feeItem: number, destLocation: any, destWeight: number) {2496 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMulticurrencies', [currencies, feeItem, destLocation, destWeight], true);2497 }2498}24992500class TokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {2501 async accounts(address: string, currencyId: any) {2502 const {free} = (await this.helper.callRpc('api.query.tokens.accounts', [address, currencyId])).toJSON() as any;2503 return BigInt(free);2504 }2505}25062507class AssetsGroup<T extends ChainHelperBase> extends HelperGroup<T> {2508 async create(signer: TSigner, assetId: number, admin: string, minimalBalance: bigint) {2509 await this.helper.executeExtrinsic(signer, 'api.tx.assets.create', [assetId, admin, minimalBalance], true);2510 }25112512 async setMetadata(signer: TSigner, assetId: number, name: string, symbol: string, decimals: number) {2513 await this.helper.executeExtrinsic(signer, 'api.tx.assets.setMetadata', [assetId, name, symbol, decimals], true);2514 }25152516 async mint(signer: TSigner, assetId: number, beneficiary: string, amount: bigint) {2517 await this.helper.executeExtrinsic(signer, 'api.tx.assets.mint', [assetId, beneficiary, amount], true);2518 }25192520 async account(assetId: string | number, address: string) {2521 const accountAsset = (2522 await this.helper.callRpc('api.query.assets.account', [assetId, address])2523 ).toJSON()! as any;25242525 if (accountAsset !== null) {2526 return BigInt(accountAsset['balance']);2527 } else {2528 return null;2529 }2530 }2531}25322533class AcalaAssetRegistryGroup extends HelperGroup<AcalaHelper> {2534 async registerForeignAsset(signer: TSigner, destination: any, metadata: AcalaAssetMetadata) {2535 await this.helper.executeExtrinsic(signer, 'api.tx.assetRegistry.registerForeignAsset', [destination, metadata], true);2536 }2537}25382539class MoonbeamAssetManagerGroup extends HelperGroup<MoonbeamHelper> {2540 makeRegisterForeignAssetProposal(assetInfo: MoonbeamAssetInfo) {2541 const apiPrefix = 'api.tx.assetManager.';25422543 const registerTx = this.helper.constructApiCall(2544 apiPrefix + 'registerForeignAsset',2545 [assetInfo.location, assetInfo.metadata, assetInfo.existentialDeposit, assetInfo.isSufficient],2546 );25472548 const setUnitsTx = this.helper.constructApiCall(2549 apiPrefix + 'setAssetUnitsPerSecond',2550 [assetInfo.location, assetInfo.unitsPerSecond, assetInfo.numAssetsWeightHint],2551 );25522553 const batchCall = this.helper.getApi().tx.utility.batchAll([registerTx, setUnitsTx]);2554 const encodedProposal = batchCall?.method.toHex() || '';2555 return encodedProposal;2556 }25572558 async assetTypeId(location: any) {2559 return await this.helper.callRpc('api.query.assetManager.assetTypeId', [location]);2560 }2561}25622563class MoonbeamDemocracyGroup extends HelperGroup<MoonbeamHelper> {2564 async notePreimage(signer: TSigner, encodedProposal: string) {2565 await this.helper.executeExtrinsic(signer, 'api.tx.democracy.notePreimage', [encodedProposal], true);2566 }25672568 externalProposeMajority(proposalHash: string) {2569 return this.helper.constructApiCall('api.tx.democracy.externalProposeMajority', [proposalHash]);2570 }25712572 fastTrack(proposalHash: string, votingPeriod: number, delayPeriod: number) {2573 return this.helper.constructApiCall('api.tx.democracy.fastTrack', [proposalHash, votingPeriod, delayPeriod]);2574 }25752576 async referendumVote(signer: TSigner, referendumIndex: number, accountVote: DemocracyStandardAccountVote) {2577 await this.helper.executeExtrinsic(signer, 'api.tx.democracy.vote', [referendumIndex, {Standard: accountVote}], true);2578 }2579}25802581class MoonbeamCollectiveGroup extends HelperGroup<MoonbeamHelper> {2582 collective: string;25832584 constructor(helper: MoonbeamHelper, collective: string) {2585 super(helper);25862587 this.collective = collective;2588 }25892590 async propose(signer: TSigner, threshold: number, proposalHash: string, lengthBound: number) {2591 await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.propose`, [threshold, proposalHash, lengthBound], true);2592 }25932594 async vote(signer: TSigner, proposalHash: string, proposalIndex: number, approve: boolean) {2595 await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [proposalHash, proposalIndex, approve], true);2596 }25972598 async close(signer: TSigner, proposalHash: string, proposalIndex: number, weightBound: number, lengthBound: number) {2599 await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.close`, [proposalHash, proposalIndex, weightBound, lengthBound], true);2600 }26012602 async proposalCount() {2603 return Number(await this.helper.callRpc(`api.query.${this.collective}.proposalCount`, []));2604 }2605}26062607export type ChainHelperBaseConstructor = new(...args: any[]) => ChainHelperBase;2317export type UniqueHelperConstructor = new(...args: any[]) => UniqueHelper;2608export type UniqueHelperConstructor = new(...args: any[]) => UniqueHelper;231826092319export class UniqueHelper extends ChainHelperBase {2610export class UniqueHelper extends ChainHelperBase {2320 helperBase: any;2611 balance: BalanceGroup<UniqueHelper>;23212322 chain: ChainGroup;2323 balance: BalanceGroup;2324 address: AddressGroup;2325 collection: CollectionGroup;2612 collection: CollectionGroup;2326 nft: NFTGroup;2613 nft: NFTGroup;2327 rft: RFTGroup;2614 rft: RFTGroup;2328 ft: FTGroup;2615 ft: FTGroup;2329 staking: StakingGroup;2616 staking: StakingGroup;2330 scheduler: SchedulerGroup;2617 scheduler: SchedulerGroup;2618 foreignAssets: ForeignAssetsGroup;2619 xcm: XcmGroup<UniqueHelper>;2620 xTokens: XTokensGroup<UniqueHelper>;2621 tokens: TokensGroup<UniqueHelper>;233126222332 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2623 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2333 super(logger);2624 super(logger, options.helperBase ?? UniqueHelper);233426252335 this.helperBase = options.helperBase ?? UniqueHelper;23362337 this.chain = new ChainGroup(this);2338 this.balance = new BalanceGroup(this);2626 this.balance = new BalanceGroup(this);2339 this.address = new AddressGroup(this);2340 this.collection = new CollectionGroup(this);2627 this.collection = new CollectionGroup(this);2341 this.nft = new NFTGroup(this);2628 this.nft = new NFTGroup(this);2342 this.rft = new RFTGroup(this);2629 this.rft = new RFTGroup(this);2343 this.ft = new FTGroup(this);2630 this.ft = new FTGroup(this);2344 this.staking = new StakingGroup(this);2631 this.staking = new StakingGroup(this);2345 this.scheduler = new SchedulerGroup(this);2632 this.scheduler = new SchedulerGroup(this);2633 this.foreignAssets = new ForeignAssetsGroup(this);2634 this.xcm = new XcmGroup(this, 'polkadotXcm');2635 this.xTokens = new XTokensGroup(this);2636 this.tokens = new TokensGroup(this);2346 }2637 }234726382348 clone(helperCls: UniqueHelperConstructor, options: {[key: string]: any} = {}) {2639 getSudo<T extends UniqueHelper>() {2640 // eslint-disable-next-line @typescript-eslint/naming-convention2349 Object.setPrototypeOf(helperCls.prototype, this);2641 const SudoHelperType = SudoHelper(this.helperBase);2350 const newHelper = new helperCls(this.logger, options);2642 return this.clone(SudoHelperType) as T;2643 }2644}235126452352 newHelper.api = this.api;2646export class XcmChainHelper extends ChainHelperBase {2647 async connect(wsEndpoint: string, _listeners?: any): Promise<void> {2648 const wsProvider = new WsProvider(wsEndpoint);2649 this.api = new ApiPromise({2650 provider: wsProvider,2651 });2353 newHelper.network = this.network;2652 await this.api.isReadyOrError;2354 newHelper.forceNetwork = this.forceNetwork;2653 this.network = await UniqueHelper.detectNetwork(this.api);2654 }2655}235526562356 this.children.push(newHelper);2657export class RelayHelper extends XcmChainHelper {2658 xcm: XcmGroup<RelayHelper>;235726592358 return newHelper;2660 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2661 super(logger, options.helperBase ?? RelayHelper);26622663 this.xcm = new XcmGroup(this, 'xcmPallet');2359 }2664 }2665}236026662361 getSudo<T extends UniqueHelper>() {2667export class WestmintHelper extends XcmChainHelper {2668 balance: SubstrateBalanceGroup<WestmintHelper>;2669 xcm: XcmGroup<WestmintHelper>;2670 assets: AssetsGroup<WestmintHelper>;2671 xTokens: XTokensGroup<WestmintHelper>;26722673 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2674 super(logger, options.helperBase ?? WestmintHelper);26752676 this.balance = new SubstrateBalanceGroup(this);2677 this.xcm = new XcmGroup(this, 'polkadotXcm');2678 this.assets = new AssetsGroup(this);2679 this.xTokens = new XTokensGroup(this);2680 }2681}26822683export class MoonbeamHelper extends XcmChainHelper {2684 balance: EthereumBalanceGroup<MoonbeamHelper>;2685 assetManager: MoonbeamAssetManagerGroup;2686 assets: AssetsGroup<MoonbeamHelper>;2687 xTokens: XTokensGroup<MoonbeamHelper>;2688 democracy: MoonbeamDemocracyGroup;2689 collective: {2690 council: MoonbeamCollectiveGroup,2691 techCommittee: MoonbeamCollectiveGroup,2692 };26932694 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2695 super(logger, options.helperBase ?? MoonbeamHelper);26962697 this.balance = new EthereumBalanceGroup(this);2698 this.assetManager = new MoonbeamAssetManagerGroup(this);2699 this.assets = new AssetsGroup(this);2700 this.xTokens = new XTokensGroup(this);2701 this.democracy = new MoonbeamDemocracyGroup(this);2702 this.collective = {2703 council: new MoonbeamCollectiveGroup(this, 'councilCollective'),2704 techCommittee: new MoonbeamCollectiveGroup(this, 'techCommitteeCollective'),2705 };2706 }2707}27082709export class AcalaHelper extends XcmChainHelper {2710 balance: SubstrateBalanceGroup<AcalaHelper>;2711 assetRegistry: AcalaAssetRegistryGroup;2712 xTokens: XTokensGroup<AcalaHelper>;2713 tokens: TokensGroup<AcalaHelper>;27142715 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2716 super(logger, options.helperBase ?? AcalaHelper);27172718 this.balance = new SubstrateBalanceGroup(this);2719 this.assetRegistry = new AcalaAssetRegistryGroup(this);2720 this.xTokens = new XTokensGroup(this);2721 this.tokens = new TokensGroup(this);2722 }27232724 getSudo<T extends AcalaHelper>() {2362 // eslint-disable-next-line @typescript-eslint/naming-convention2725 // eslint-disable-next-line @typescript-eslint/naming-convention2363 const SudoHelperType = SudoUniqueHelper(this.helperBase);2726 const SudoHelperType = SudoHelper(this.helperBase);2364 return this.clone(SudoHelperType) as T;2727 return this.clone(SudoHelperType) as T;2365 }2728 }2366}2729}2411}2774}241227752413// eslint-disable-next-line @typescript-eslint/naming-convention2776// eslint-disable-next-line @typescript-eslint/naming-convention2414function SudoUniqueHelper<T extends UniqueHelperConstructor>(Base: T) {2777function SudoHelper<T extends ChainHelperBaseConstructor>(Base: T) {2415 return class extends Base {2778 return class extends Base {2416 constructor(...args: any[]) {2779 constructor(...args: any[]) {2417 super(...args);2780 super(...args);tests/src/xcm/xcmOpal.test.tsdiffbeforeafterboth14// You should have received a copy of the GNU General Public License14// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.161617import chai from 'chai';18import chaiAsPromised from 'chai-as-promised';1920import {WsProvider} from '@polkadot/api';21import {ApiOptions} from '@polkadot/api/types';22import {IKeyringPair} from '@polkadot/types/types';17import {IKeyringPair} from '@polkadot/types/types';23import usingApi, {executeTransaction} from './../substrate/substrate-api';18import config from '../config';24import {bigIntToDecimals, describe_xcm, getGenericResult, paraSiblingSovereignAccount, normalizeAccountId} from './../deprecated-helpers/helpers';19import {itSub, expect, describeXcm, usingPlaygrounds, usingWestmintPlaygrounds, usingRelayPlaygrounds} from '../util/playgrounds';25import waitNewBlocks from './../substrate/wait-new-blocks';26import getBalance from './../substrate/get-balance';27202829chai.use(chaiAsPromised);30const expect = chai.expect;3132const STATEMINE_CHAIN = 1000;21const STATEMINE_CHAIN = 1000;33const UNIQUE_CHAIN = 2095;22const UNIQUE_CHAIN = 2095;342335const RELAY_PORT = '9844';24const relayUrl = config.relayUrl;36const UNIQUE_PORT = '9944';25const westmintUrl = config.westmintUrl;37const STATEMINE_PORT = '9948';2638const STATEMINE_PALLET_INSTANCE = 50;27const STATEMINE_PALLET_INSTANCE = 50;39const ASSET_ID = 100;28const ASSET_ID = 100;40const ASSET_METADATA_DECIMALS = 18;29const ASSET_METADATA_DECIMALS = 18;41const ASSET_METADATA_NAME = 'USDT';30const ASSET_METADATA_NAME = 'USDT';42const ASSET_METADATA_DESCRIPTION = 'USDT';31const ASSET_METADATA_DESCRIPTION = 'USDT';43const ASSET_METADATA_MINIMAL_BALANCE = 1;32const ASSET_METADATA_MINIMAL_BALANCE = 1n;443345const WESTMINT_DECIMALS = 12;34const WESTMINT_DECIMALS = 12;463549// 10,000.00 (ten thousands) USDT38// 10,000.00 (ten thousands) USDT50const ASSET_AMOUNT = 1_000_000_000_000_000_000_000n; 39const ASSET_AMOUNT = 1_000_000_000_000_000_000_000n; 514052describe_xcm('[XCM] Integration test: Exchanging USDT with Westmint', () => {41describeXcm('[XCM] Integration test: Exchanging USDT with Westmint', () => {53 let alice: IKeyringPair;42 let alice: IKeyringPair;54 let bob: IKeyringPair;43 let bob: IKeyringPair;55 44 6958705971 before(async () => {60 before(async () => {72 await usingApi(async (api, privateKeyWrapper) => {61 await usingPlaygrounds(async (_helper, privateKey) => {73 alice = privateKeyWrapper('//Alice');62 alice = privateKey('//Alice');74 bob = privateKeyWrapper('//Bob'); // funds donor63 bob = privateKey('//Bob'); // funds donor75 });64 });766577 const statemineApiOptions: ApiOptions = {66 await usingWestmintPlaygrounds(westmintUrl, async (helper) => {78 provider: new WsProvider('ws://127.0.0.1:' + STATEMINE_PORT),79 };8081 const uniqueApiOptions: ApiOptions = {82 provider: new WsProvider('ws://127.0.0.1:' + UNIQUE_PORT),83 };8485 const relayApiOptions: ApiOptions = {86 provider: new WsProvider('ws://127.0.0.1:' + RELAY_PORT),87 };8889 await usingApi(async (api) => {9091 // 350.00 (three hundred fifty) DOT67 // 350.00 (three hundred fifty) DOT92 const fundingAmount = 3_500_000_000_000; 68 const fundingAmount = 3_500_000_000_000n; 936994 const tx = api.tx.assets.create(ASSET_ID, alice.addressRaw, ASSET_METADATA_MINIMAL_BALANCE);70 await helper.assets.create(alice, ASSET_ID, alice.address, ASSET_METADATA_MINIMAL_BALANCE);95 const events = await executeTransaction(api, alice, tx);71 await helper.assets.setMetadata(alice, ASSET_ID, ASSET_METADATA_NAME, ASSET_METADATA_DESCRIPTION, ASSET_METADATA_DECIMALS);96 const result = getGenericResult(events);72 await helper.assets.mint(alice, ASSET_ID, alice.address, ASSET_AMOUNT);97 expect(result.success).to.be.true;987399 // set metadata100 const tx2 = api.tx.assets.setMetadata(ASSET_ID, ASSET_METADATA_NAME, ASSET_METADATA_DESCRIPTION, ASSET_METADATA_DECIMALS);101 const events2 = await executeTransaction(api, alice, tx2);102 const result2 = getGenericResult(events2);103 expect(result2.success).to.be.true;104105 // mint some amount of asset106 const tx3 = api.tx.assets.mint(ASSET_ID, alice.addressRaw, ASSET_AMOUNT);107 const events3 = await executeTransaction(api, alice, tx3);108 const result3 = getGenericResult(events3);109 expect(result3.success).to.be.true;110111 // funding parachain sovereing account (Parachain: 2095)74 // funding parachain sovereing account (Parachain: 2095)112 const parachainSovereingAccount = await paraSiblingSovereignAccount(UNIQUE_CHAIN);75 const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(UNIQUE_CHAIN);113 const tx4 = api.tx.balances.transfer(parachainSovereingAccount, fundingAmount);76 await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, fundingAmount);114 const events4 = await executeTransaction(api, bob, tx4);77 });115 const result4 = getGenericResult(events4);116 expect(result4.success).to.be.true;11778118 }, statemineApiOptions);11979120121 await usingApi(async (api) => {80 await usingPlaygrounds(async (helper) => {122123 const location = {81 const location = {124 V1: {82 V1: {144 decimals: ASSET_METADATA_DECIMALS,102 decimals: ASSET_METADATA_DECIMALS,145 minimalBalance: ASSET_METADATA_MINIMAL_BALANCE,103 minimalBalance: ASSET_METADATA_MINIMAL_BALANCE,146 };104 };147 //registerForeignAsset(owner, location, metadata)105 await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata);148 const tx = api.tx.foreignAssets.registerForeignAsset(alice.addressRaw, location, metadata);149 const sudoTx = api.tx.sudo.sudo(tx as any);106 balanceOpalBefore = await helper.balance.getSubstrate(alice.address);150 const events = await executeTransaction(api, alice, sudoTx);151 const result = getGenericResult(events);107 });152 expect(result.success).to.be.true;153108154 [balanceOpalBefore] = await getBalance(api, [alice.address]);155109156 }, uniqueApiOptions);157158159 // Providing the relay currency to the unique sender account110 // Providing the relay currency to the unique sender account160 await usingApi(async (api) => {111 await usingRelayPlaygrounds(relayUrl, async (helper) => {161 const destination = {112 const destination = {162 V1: {113 V1: {163 parents: 0,114 parents: 0,196 };147 };197148198 const feeAssetItem = 0;149 const feeAssetItem = 0;150 const weightLimit = 5_000_000_000;199151200 const weightLimit = {152 await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, weightLimit);201 Limited: 5_000_000_000,202 };203204 const tx = api.tx.xcmPallet.limitedReserveTransferAssets(destination, beneficiary, assets, feeAssetItem, weightLimit);205 const events = await executeTransaction(api, alice, tx);153 });206 const result = getGenericResult(events);207 expect(result.success).to.be.true;208 }, relayApiOptions);209 154 210 });155 });211156212 it('Should connect and send USDT from Westmint to Opal', async () => {157 itSub('Should connect and send USDT from Westmint to Opal', async ({helper}) => {213 158 await usingWestmintPlaygrounds(westmintUrl, async (helper) => {214 const statemineApiOptions: ApiOptions = {215 provider: new WsProvider('ws://127.0.0.1:' + STATEMINE_PORT),216 };217218 const uniqueApiOptions: ApiOptions = {219 provider: new WsProvider('ws://127.0.0.1:' + UNIQUE_PORT),220 };221222 await usingApi(async (api) => {223224 const dest = {159 const dest = {225 V1: {160 V1: {267 };202 };268203269 const feeAssetItem = 0;204 const feeAssetItem = 0;205 const weightLimit = 5000000000;270206271 const weightLimit = {207 balanceStmnBefore = await helper.balance.getSubstrate(alice.address);272 Limited: 5000000000,208 await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, weightLimit);273 };274209275 [balanceStmnBefore] = await getBalance(api, [alice.address]);210 balanceStmnAfter = await helper.balance.getSubstrate(alice.address);276211277 const tx = api.tx.polkadotXcm.limitedReserveTransferAssets(dest, beneficiary, assets, feeAssetItem, weightLimit);278 const events = await executeTransaction(api, alice, tx);279 const result = getGenericResult(events);280 expect(result.success).to.be.true;281282 [balanceStmnAfter] = await getBalance(api, [alice.address]);283284 // common good parachain take commission in it native token212 // common good parachain take commission in it native token285 console.log(213 console.log(286 'Opal to Westmint transaction fees on Westmint: %s WND',214 'Opal to Westmint transaction fees on Westmint: %s WND',287 bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, WESTMINT_DECIMALS),215 helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, WESTMINT_DECIMALS),288 );216 );289 expect(balanceStmnBefore > balanceStmnAfter).to.be.true;217 expect(balanceStmnBefore > balanceStmnAfter).to.be.true;290218291 }, statemineApiOptions);219 });292220293221294 // ensure that asset has been delivered222 // ensure that asset has been delivered295 await usingApi(async (api) => {223 await helper.wait.newBlocks(3);296 await waitNewBlocks(api, 3);297 // expext collection id will be with id 1298 const free = (await api.query.fungible.balance(1, normalizeAccountId(alice.address))).toBigInt();299224300 [balanceOpalAfter] = await getBalance(api, [alice.address]);225 // expext collection id will be with id 1226 const free = await helper.ft.getBalance(1, {Substrate: alice.address});301227302 // commission has not paid in USDT token228 balanceOpalAfter = await helper.balance.getSubstrate(alice.address);303 expect(free == TRANSFER_AMOUNT).to.be.true;304 console.log(305 'Opal to Westmint transaction fees on Opal: %s USDT',306 bigIntToDecimals(TRANSFER_AMOUNT - free),307 );308 // ... and parachain native token309 expect(balanceOpalAfter == balanceOpalBefore).to.be.true;310 console.log(311 'Opal to Westmint transaction fees on Opal: %s WND',312 bigIntToDecimals(balanceOpalAfter - balanceOpalBefore, WESTMINT_DECIMALS),313 );314229315 }, uniqueApiOptions);230 // commission has not paid in USDT token231 expect(free == TRANSFER_AMOUNT).to.be.true;232 console.log(233 'Opal to Westmint transaction fees on Opal: %s USDT',234 helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free),235 );236 // ... and parachain native token237 expect(balanceOpalAfter == balanceOpalBefore).to.be.true;238 console.log(239 'Opal to Westmint transaction fees on Opal: %s WND',240 helper.util.bigIntToDecimals(balanceOpalAfter - balanceOpalBefore, WESTMINT_DECIMALS),316 241 ); 317 });242 });318243319 it('Should connect and send USDT from Unique to Statemine back', async () => {244 itSub('Should connect and send USDT from Unique to Statemine back', async ({helper}) => {320321 const uniqueApiOptions: ApiOptions = {322 provider: new WsProvider('ws://127.0.0.1:' + UNIQUE_PORT),323 };324325 const statemineApiOptions: ApiOptions = {326 provider: new WsProvider('ws://127.0.0.1:' + STATEMINE_PORT),327 };328329 await usingApi(async (api) => {330 const destination = {245 const destination = {331 V1: {246 V1: {332 parents: 1,247 parents: 1,333 interior: {X2: [248 interior: {X2: [334 {335 Parachain: STATEMINE_CHAIN,336 },337 {338 AccountId32: {339 network: 'Any',340 id: alice.addressRaw,341 },342 },343 ]},344 },345 };346347 const currencies: [any, bigint][] = [348 [349 {249 {350 ForeignAssetId: 0,250 Parachain: STATEMINE_CHAIN,351 },251 },352 //10_000_000_000_000_000n,353 TRANSFER_AMOUNT,354 ], 355 [356 {252 {357 NativeAssetId: 'Parent',253 AccountId32: {254 network: 'Any',255 id: alice.addressRaw,256 },358 },257 },359 400_000_000_000_000n,258 ]},360 ],259 },361 ];260 };362261363 const feeItem = 1;262 const currencies: [any, bigint][] = [364 const destWeight = 500000000000;263 [264 {265 ForeignAssetId: 0,266 },267 //10_000_000_000_000_000n,268 TRANSFER_AMOUNT,269 ], 270 [271 {272 NativeAssetId: 'Parent',273 },274 400_000_000_000_000n,275 ],276 ];365277366 const tx = api.tx.xTokens.transferMulticurrencies(currencies, feeItem, destination, destWeight);278 const feeItem = 1;367 const events = await executeTransaction(api, alice, tx);368 const result = getGenericResult(events);279 const destWeight = 500000000000;369 expect(result.success).to.be.true;370 371 // the commission has been paid in parachain native token372 [balanceOpalFinal] = await getBalance(api, [alice.address]);373 expect(balanceOpalAfter > balanceOpalFinal).to.be.true;374 }, uniqueApiOptions);375280376 await usingApi(async (api) => {281 await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, destWeight);282 283 // the commission has been paid in parachain native token284 balanceOpalFinal = await helper.balance.getSubstrate(alice.address);285 expect(balanceOpalAfter > balanceOpalFinal).to.be.true;286287 await usingWestmintPlaygrounds(westmintUrl, async (helper) => {377 await waitNewBlocks(api, 3);288 await helper.wait.newBlocks(3);378 289 379 // The USDT token never paid fees. Its amount not changed from begin value.290 // The USDT token never paid fees. Its amount not changed from begin value.380 // Also check that xcm transfer has been succeeded 291 // Also check that xcm transfer has been succeeded 381 const free = ((await api.query.assets.account(100, alice.address)).toHuman()) as any;292 expect((await helper.assets.account(ASSET_ID, alice.address))! == ASSET_AMOUNT).to.be.true;382 expect(BigInt(free.balance.replace(/,/g, '')) == ASSET_AMOUNT).to.be.true;383 }, statemineApiOptions);293 });384 });294 });385295386 it('Should connect and send Relay token to Unique', async () => {296 itSub('Should connect and send Relay token to Unique', async ({helper}) => {387388 const uniqueApiOptions: ApiOptions = {389 provider: new WsProvider('ws://127.0.0.1:' + UNIQUE_PORT),390 };391392 const uniqueApiOptions2: ApiOptions = {393 provider: new WsProvider('ws://127.0.0.1:' + UNIQUE_PORT),394 };395396 const relayApiOptions: ApiOptions = {397 provider: new WsProvider('ws://127.0.0.1:' + RELAY_PORT),398 };399400 const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n;297 const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n;401298402 await usingApi(async (api) => {299 balanceBobBefore = await helper.balance.getSubstrate(bob.address);403 [balanceBobBefore] = await getBalance(api, [bob.address]);404 balanceBobRelayTokenBefore = BigInt(((await api.query.tokens.accounts(bob.addressRaw, {NativeAssetId: 'Parent'})).toJSON() as any).free);300 balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});405301406 }, uniqueApiOptions);407408 // Providing the relay currency to the unique sender account302 // Providing the relay currency to the unique sender account409 await usingApi(async (api) => {303 await usingRelayPlaygrounds(relayUrl, async (helper) => {410 const destination = {304 const destination = {411 V1: {305 V1: {412 parents: 0,306 parents: 0,445 };339 };446340447 const feeAssetItem = 0;341 const feeAssetItem = 0;342 const weightLimit = 5_000_000_000;448343449 const weightLimit = {344 await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, weightLimit);450 Limited: 5_000_000_000,451 };452453 const tx = api.tx.xcmPallet.limitedReserveTransferAssets(destination, beneficiary, assets, feeAssetItem, weightLimit);454 const events = await executeTransaction(api, bob, tx);345 });455 const result = getGenericResult(events);456 expect(result.success).to.be.true;457 }, relayApiOptions);458 346 347 await helper.wait.newBlocks(3);459348460 await usingApi(async (api) => {349 balanceBobAfter = await helper.balance.getSubstrate(bob.address); 461 await waitNewBlocks(api, 3);350 balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});462351463 [balanceBobAfter] = await getBalance(api, [bob.address]);352 const wndFee = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore; 464 balanceBobRelayTokenAfter = BigInt(((await api.query.tokens.accounts(bob.addressRaw, {NativeAssetId: 'Parent'})).toJSON() as any).free);465 const wndFee = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore; 466 console.log(353 console.log(467 'Relay (Westend) to Opal transaction fees: %s OPL',354 'Relay (Westend) to Opal transaction fees: %s OPL',468 bigIntToDecimals(balanceBobAfter - balanceBobBefore),355 helper.util.bigIntToDecimals(balanceBobAfter - balanceBobBefore),469 );356 );470 console.log(357 console.log(471 'Relay (Westend) to Opal transaction fees: %s WND',358 'Relay (Westend) to Opal transaction fees: %s WND',472 bigIntToDecimals(wndFee, WESTMINT_DECIMALS),359 helper.util.bigIntToDecimals(wndFee, WESTMINT_DECIMALS),473 );360 );474 expect(balanceBobBefore == balanceBobAfter).to.be.true;361 expect(balanceBobBefore == balanceBobAfter).to.be.true;475 expect(balanceBobRelayTokenBefore < balanceBobRelayTokenAfter).to.be.true;362 expect(balanceBobRelayTokenBefore < balanceBobRelayTokenAfter).to.be.true;476 }, uniqueApiOptions2);477478 });363 });479364480 it('Should connect and send Relay token back', async () => {365 itSub('Should connect and send Relay token back', async ({helper}) => {481 const uniqueApiOptions: ApiOptions = {366 const destination = {482 provider: new WsProvider('ws://127.0.0.1:' + UNIQUE_PORT),367 V1: {368 parents: 1,369 interior: {X2: [370 {371 Parachain: STATEMINE_CHAIN,372 },373 {374 AccountId32: {375 network: 'Any',376 id: bob.addressRaw,377 },378 },379 ]},380 },483 };381 };484382485 await usingApi(async (api) => {383 const currencies: any = [486 const destination = {487 V1: {488 parents: 1,489 interior: {X2: [384 [490 {385 {491 Parachain: STATEMINE_CHAIN,386 NativeAssetId: 'Parent',492 },493 {494 AccountId32: {495 network: 'Any',496 id: bob.addressRaw,497 },498 },499 ]},500 },387 },501 };388 50_000_000_000_000_000n,389 ],390 ];502391503 const currencies: any = [392 const feeItem = 0;504 [393 const destWeight = 500000000000;505 {506 NativeAssetId: 'Parent',507 },508 50_000_000_000_000_000n,509 ],510 ];511394512 const feeItem = 0;395 await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, destWeight);513 const destWeight = 500000000000;514396515 const tx = api.tx.xTokens.transferMulticurrencies(currencies, feeItem, destination, destWeight);397 balanceBobFinal = await helper.balance.getSubstrate(bob.address);516 const events = await executeTransaction(api, bob, tx);517 const result = getGenericResult(events);518 expect(result.success).to.be.true;519520 [balanceBobFinal] = await getBalance(api, [bob.address]);521 console.log('Relay (Westend) to Opal transaction fees: %s OPL', balanceBobAfter - balanceBobFinal);398 console.log('Relay (Westend) to Opal transaction fees: %s OPL', balanceBobAfter - balanceBobFinal);522523 }, uniqueApiOptions);524 });399 });525526});400});527401tests/src/xcm/xcmQuartz.test.tsdiffbeforeafterboth14// You should have received a copy of the GNU General Public License14// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.161617import chai from 'chai';18import chaiAsPromised from 'chai-as-promised';1920import {WsProvider, Keyring} from '@polkadot/api';21import {ApiOptions} from '@polkadot/api/types';22import {IKeyringPair} from '@polkadot/types/types';17import {IKeyringPair} from '@polkadot/types/types';23import usingApi, {submitTransactionAsync} from '../substrate/substrate-api';24import {getGenericResult, generateKeyringPair, waitEvent, describe_xcm, bigIntToDecimals} from '../deprecated-helpers/helpers';25import {MultiLocation} from '@polkadot/types/interfaces';26import {blake2AsHex} from '@polkadot/util-crypto';18import {blake2AsHex} from '@polkadot/util-crypto';27import waitNewBlocks from '../substrate/wait-new-blocks';19import config from '../config';28import getBalance from '../substrate/get-balance';29import {XcmV2TraitsOutcome, XcmV2TraitsError} from '../interfaces';20import {XcmV2TraitsOutcome, XcmV2TraitsError} from '../interfaces';21import {itSub, expect, describeXcm, usingPlaygrounds, usingKaruraPlaygrounds, usingRelayPlaygrounds, usingMoonriverPlaygrounds} from '../util/playgrounds';302231chai.use(chaiAsPromised);32const expect = chai.expect;3334const QUARTZ_CHAIN = 2095;23const QUARTZ_CHAIN = 2095;35const KARURA_CHAIN = 2000;24const KARURA_CHAIN = 2000;36const MOONRIVER_CHAIN = 2023;25const MOONRIVER_CHAIN = 2023;372638const RELAY_PORT = 9844;27const relayUrl = config.relayUrl;39const KARURA_PORT = 9946;28const karuraUrl = config.karuraUrl;40const MOONRIVER_PORT = 9947;29const moonriverUrl = config.moonriverUrl;413042const KARURA_DECIMALS = 12;31const KARURA_DECIMALS = 12;433244const TRANSFER_AMOUNT = 2000000000000000000000000n;33const TRANSFER_AMOUNT = 2000000000000000000000000n;453446function parachainApiOptions(port: number): ApiOptions {47 return {48 provider: new WsProvider('ws://127.0.0.1:' + port.toString()),49 };50}5152function karuraOptions(): ApiOptions {53 return parachainApiOptions(KARURA_PORT);54}5556function moonriverOptions(): ApiOptions {57 return parachainApiOptions(MOONRIVER_PORT);58}5960function relayOptions(): ApiOptions {61 return parachainApiOptions(RELAY_PORT);62}6364describe_xcm('[XCM] Integration test: Exchanging tokens with Karura', () => {35describeXcm('[XCM] Integration test: Exchanging tokens with Karura', () => {65 let alice: IKeyringPair;36 let alice: IKeyringPair;66 let randomAccount: IKeyringPair;37 let randomAccount: IKeyringPair;673876 let balanceQuartzForeignTokenFinal: bigint;47 let balanceQuartzForeignTokenFinal: bigint;774878 before(async () => {49 before(async () => {79 await usingApi(async (api, privateKeyWrapper) => {50 await usingPlaygrounds(async (helper, privateKey) => {80 const keyringSr25519 = new Keyring({type: 'sr25519'});51 alice = privateKey('//Alice');8182 alice = privateKeyWrapper('//Alice');83 randomAccount = generateKeyringPair(keyringSr25519);52 [randomAccount] = await helper.arrange.createAccounts([0n], alice);84 });53 });855486 // Karura side55 await usingKaruraPlaygrounds(karuraUrl, async (helper) => {87 await usingApi(88 async (api) => {89 const destination = {90 V0: {91 X2: [92 'Parent',93 {94 Parachain: QUARTZ_CHAIN,95 },96 ],97 },98 };99100 const metadata = {101 name: 'QTZ',102 symbol: 'QTZ',103 decimals: 18,104 minimalBalance: 1,105 };106107 const tx = api.tx.assetRegistry.registerForeignAsset(destination, metadata);108 const sudoTx = api.tx.sudo.sudo(tx as any);109 const events = await submitTransactionAsync(alice, sudoTx);110 const result = getGenericResult(events);111 expect(result.success).to.be.true;112113 const tx1 = api.tx.balances.transfer(randomAccount.address, 10000000000000n);114 const events1 = await submitTransactionAsync(alice, tx1);115 const result1 = getGenericResult(events1);116 expect(result1.success).to.be.true;117118 [balanceKaruraTokenInit] = await getBalance(api, [randomAccount.address]);119 {120 const {free} = (await api.query.tokens.accounts(randomAccount.addressRaw, {ForeignAsset: 0})).toJSON() as any;121 balanceQuartzForeignTokenInit = BigInt(free);122 }123 },124 karuraOptions(),125 );126127 // Quartz side128 await usingApi(async (api) => {129 const tx0 = api.tx.balances.transfer(randomAccount.address, 10n * TRANSFER_AMOUNT);130 const events0 = await submitTransactionAsync(alice, tx0);131 const result0 = getGenericResult(events0);132 expect(result0.success).to.be.true;133134 [balanceQuartzTokenInit] = await getBalance(api, [randomAccount.address]);135 });136 });137138 it('Should connect and send QTZ to Karura', async () => {139140 // Quartz side141 await usingApi(async (api) => {142143 const destination = {56 const destination = {144 V0: {57 V0: {145 X2: [58 X2: [146 'Parent',59 'Parent',147 {60 {148 Parachain: KARURA_CHAIN,61 Parachain: QUARTZ_CHAIN,149 },62 },150 ],63 ],151 },64 },152 };65 };15366154 const beneficiary = {67 const metadata = {155 V0: {68 name: 'QTZ',156 X1: {69 symbol: 'QTZ',157 AccountId32: {158 network: 'Any',159 id: randomAccount.addressRaw,70 decimals: 18,160 },71 minimalBalance: 1n,161 },162 },163 };72 };16473165 const assets = {74 await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);75 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);76 balanceKaruraTokenInit = await helper.balance.getSubstrate(randomAccount.address);77 balanceQuartzForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});78 });7980 await usingPlaygrounds(async (helper) => {81 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);82 balanceQuartzTokenInit = await helper.balance.getSubstrate(randomAccount.address);83 });84 });8586 itSub('Should connect and send QTZ to Karura', async ({helper}) => {87 const destination = {166 V1: [88 V0: {89 X2: [90 'Parent',167 {91 {168 id: {92 Parachain: KARURA_CHAIN,169 Concrete: {170 parents: 0,171 interior: 'Here',172 },173 },174 fun: {175 Fungible: TRANSFER_AMOUNT,176 },177 },93 },178 ],94 ],179 };95 },96 };18097181 const feeAssetItem = 0;98 const beneficiary = {99 V0: {100 X1: {101 AccountId32: {102 network: 'Any',103 id: randomAccount.addressRaw,104 },105 },106 },107 };182108183 const weightLimit = {109 const assets = {184 Limited: 5000000000,110 V1: [111 {112 id: {113 Concrete: {114 parents: 0,185 };115 interior: 'Here',116 },117 },118 fun: {119 Fungible: TRANSFER_AMOUNT,120 },121 },122 ],123 };186124187 const tx = api.tx.polkadotXcm.limitedReserveTransferAssets(destination, beneficiary, assets, feeAssetItem, weightLimit);125 const feeAssetItem = 0;188 const events = await submitTransactionAsync(randomAccount, tx);189 const result = getGenericResult(events);126 const weightLimit = 5000000000;190 expect(result.success).to.be.true;191127192 [balanceQuartzTokenMiddle] = await getBalance(api, [randomAccount.address]);128 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, weightLimit);129 balanceQuartzTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);193130194 const qtzFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;131 const qtzFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;195 console.log('[Quartz -> Karura] transaction fees on Quartz: %s QTZ', bigIntToDecimals(qtzFees));132 console.log('[Quartz -> Karura] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees));196 expect(qtzFees > 0n).to.be.true;133 expect(qtzFees > 0n).to.be.true;197 });198134199 // Karura side135 await usingKaruraPlaygrounds(karuraUrl, async (helper) => {200 await usingApi(201 async (api) => {202 await waitNewBlocks(api, 3);136 await helper.wait.newBlocks(3);203 const {free} = (await api.query.tokens.accounts(randomAccount.addressRaw, {ForeignAsset: 0})).toJSON() as any;137 balanceQuartzForeignTokenMiddle = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});204 balanceQuartzForeignTokenMiddle = BigInt(free);138 balanceKaruraTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);205139206 [balanceKaruraTokenMiddle] = await getBalance(api, [randomAccount.address]);140 const karFees = balanceKaruraTokenInit - balanceKaruraTokenMiddle;141 const qtzIncomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenInit;207142208 const karFees = balanceKaruraTokenInit - balanceKaruraTokenMiddle;143 console.log(209 const qtzIncomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenInit;210211 console.log(212 '[Quartz -> Karura] transaction fees on Karura: %s KAR',144 '[Quartz -> Karura] transaction fees on Karura: %s KAR',213 bigIntToDecimals(karFees, KARURA_DECIMALS),145 helper.util.bigIntToDecimals(karFees, KARURA_DECIMALS),214 );146 );215 console.log('[Quartz -> Karura] income %s QTZ', bigIntToDecimals(qtzIncomeTransfer));147 console.log('[Quartz -> Karura] income %s QTZ', helper.util.bigIntToDecimals(qtzIncomeTransfer));216 expect(karFees == 0n).to.be.true;148 expect(karFees == 0n).to.be.true;217 expect(qtzIncomeTransfer == TRANSFER_AMOUNT).to.be.true;149 expect(qtzIncomeTransfer == TRANSFER_AMOUNT).to.be.true;218 },150 });219 karuraOptions(),220 );221 });151 });222152223 it('Should connect to Karura and send QTZ back', async () => {153 itSub('Should connect to Karura and send QTZ back', async ({helper}) => {224225 // Karura side154 await usingKaruraPlaygrounds(karuraUrl, async (helper) => {226 await usingApi(227 async (api) => {228 const destination = {155 const destination = {229 V1: {156 V1: {230 parents: 1,157 parents: 1,231 interior: {158 interior: {232 X2: [159 X2: [233 {Parachain: QUARTZ_CHAIN},160 {Parachain: QUARTZ_CHAIN},234 {161 {235 AccountId32: {162 AccountId32: {236 network: 'Any',163 network: 'Any',237 id: randomAccount.addressRaw,164 id: randomAccount.addressRaw,238 },239 },165 },240 ],166 },241 },167 ],242 },168 },243 };169 },170 };244171245 const id = {172 const id = {246 ForeignAsset: 0,173 ForeignAsset: 0,247 };174 };248175249 const destWeight = 50000000;176 const destWeight = 50000000;250177251 const tx = api.tx.xTokens.transfer(id as any, TRANSFER_AMOUNT, destination, destWeight);178 await helper.xTokens.transfer(randomAccount, id, TRANSFER_AMOUNT, destination, destWeight);252 const events = await submitTransactionAsync(randomAccount, tx);179 balanceKaruraTokenFinal = await helper.balance.getSubstrate(randomAccount.address);253 const result = getGenericResult(events);180 balanceQuartzForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);254 expect(result.success).to.be.true;255181256 [balanceKaruraTokenFinal] = await getBalance(api, [randomAccount.address]);182 const karFees = balanceKaruraTokenMiddle - balanceKaruraTokenFinal;257 {183 const qtzOutcomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenFinal;258 const {free} = (await api.query.tokens.accounts(randomAccount.addressRaw, id)).toJSON() as any;259 balanceQuartzForeignTokenFinal = BigInt(free);260 }261184262 const karFees = balanceKaruraTokenMiddle - balanceKaruraTokenFinal;185 console.log(186 '[Karura -> Quartz] transaction fees on Karura: %s KAR',263 const qtzOutcomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenFinal;187 helper.util.bigIntToDecimals(karFees, KARURA_DECIMALS),188 );189 console.log('[Karura -> Quartz] outcome %s QTZ', helper.util.bigIntToDecimals(qtzOutcomeTransfer));264190265 console.log(191 expect(karFees > 0).to.be.true;266 '[Karura -> Quartz] transaction fees on Karura: %s KAR',267 bigIntToDecimals(karFees, KARURA_DECIMALS),192 expect(qtzOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;268 );269 console.log('[Karura -> Quartz] outcome %s QTZ', bigIntToDecimals(qtzOutcomeTransfer));193 });270194271 expect(karFees > 0).to.be.true;195 await helper.wait.newBlocks(3);272 expect(qtzOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;273 },274 karuraOptions(),275 );276196277 // Quartz side197 balanceQuartzTokenFinal = await helper.balance.getSubstrate(randomAccount.address);278 await usingApi(async (api) => {198 const actuallyDelivered = balanceQuartzTokenFinal - balanceQuartzTokenMiddle;279 await waitNewBlocks(api, 3);199 expect(actuallyDelivered > 0).to.be.true;280200281 [balanceQuartzTokenFinal] = await getBalance(api, [randomAccount.address]);201 console.log('[Karura -> Quartz] actually delivered %s QTZ', helper.util.bigIntToDecimals(actuallyDelivered));282 const actuallyDelivered = balanceQuartzTokenFinal - balanceQuartzTokenMiddle;283 expect(actuallyDelivered > 0).to.be.true;284202285 console.log('[Karura -> Quartz] actually delivered %s QTZ', bigIntToDecimals(actuallyDelivered));203 const qtzFees = TRANSFER_AMOUNT - actuallyDelivered;286287 const qtzFees = TRANSFER_AMOUNT - actuallyDelivered;288 console.log('[Karura -> Quartz] transaction fees on Quartz: %s QTZ', bigIntToDecimals(qtzFees));204 console.log('[Karura -> Quartz] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees));289 expect(qtzFees == 0n).to.be.true;205 expect(qtzFees == 0n).to.be.true;290 });291 });206 });292});207});293208294// These tests are relevant only when the foreign asset pallet is disabled209// These tests are relevant only when the foreign asset pallet is disabled295describe_xcm('[XCM] Integration test: Quartz rejects non-native tokens', () => {210describeXcm('[XCM] Integration test: Quartz rejects non-native tokens', () => {296 let alice: IKeyringPair;211 let alice: IKeyringPair;297212298 before(async () => {213 before(async () => {299 await usingApi(async (api, privateKeyWrapper) => {214 await usingPlaygrounds(async (_helper, privateKey) => {300 alice = privateKeyWrapper('//Alice');215 alice = privateKey('//Alice');301 });216 });302 });217 });303218304 it('Quartz rejects tokens from the Relay', async () => {219 itSub('Quartz rejects tokens from the Relay', async ({helper}) => {305 await usingApi(async (api) => {220 await usingRelayPlaygrounds(relayUrl, async (helper) => {306 const destination = {221 const destination = {307 V1: {222 V1: {308 parents: 0,223 parents: 0,341 };256 };342257343 const feeAssetItem = 0;258 const feeAssetItem = 0;259 const weightLimit = 5_000_000_000;344260345 const weightLimit = {261 await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, weightLimit);346 Limited: 5_000_000_000,347 };262 });348263349 const tx = api.tx.xcmPallet.limitedReserveTransferAssets(destination, beneficiary, assets, feeAssetItem, weightLimit);264 const maxWaitBlocks = 3;350 const events = await submitTransactionAsync(alice, tx);351 const result = getGenericResult(events);352 expect(result.success).to.be.true;353 }, relayOptions());354265355 await usingApi(async api => {266 const dmpQueueExecutedDownward = await helper.wait.event(maxWaitBlocks, 'dmpQueue', 'ExecutedDownward');356 const maxWaitBlocks = 3;357 const dmpQueueExecutedDownward = await waitEvent(358 api,359 maxWaitBlocks,360 'dmpQueue',361 'ExecutedDownward',362 );363267364 expect(268 expect(365 dmpQueueExecutedDownward != null,269 dmpQueueExecutedDownward != null,366 '[Relay] dmpQueue.ExecutedDownward event is expected',270 '[Relay] dmpQueue.ExecutedDownward event is expected',367 ).to.be.true;271 ).to.be.true;368272369 const event = dmpQueueExecutedDownward!.event;273 const event = dmpQueueExecutedDownward!.event;370 const outcome = event.data[1] as XcmV2TraitsOutcome;274 const outcome = event.data[1] as XcmV2TraitsOutcome;371275372 expect(276 expect(373 outcome.isIncomplete,277 outcome.isIncomplete,374 '[Relay] The outcome of the XCM should be `Incomplete`',278 '[Relay] The outcome of the XCM should be `Incomplete`',375 ).to.be.true;279 ).to.be.true;376280377 const incomplete = outcome.asIncomplete;281 const incomplete = outcome.asIncomplete;378 expect(282 expect(379 incomplete[1].toString() == 'AssetNotFound',283 incomplete[1].toString() == 'AssetNotFound',380 '[Relay] The XCM error should be `AssetNotFound`',284 '[Relay] The XCM error should be `AssetNotFound`',381 ).to.be.true;285 ).to.be.true;382 });383 });286 });384287385 it('Quartz rejects KAR tokens from Karura', async () => {288 itSub('Quartz rejects KAR tokens from Karura', async ({helper}) => {386 await usingApi(async (api) => {289 await usingKaruraPlaygrounds(karuraUrl, async (helper) => {387 const destination = {290 const destination = {388 V1: {291 V1: {389 parents: 1,292 parents: 1,407310408 const destWeight = 50000000;311 const destWeight = 50000000;409312410 const tx = api.tx.xTokens.transfer(id as any, 100_000_000_000, destination, destWeight);313 await helper.xTokens.transfer(alice, id, 100_000_000_000n, destination, destWeight);411 const events = await submitTransactionAsync(alice, tx);314 });412 const result = getGenericResult(events);413 expect(result.success).to.be.true;414 }, karuraOptions());415315416 await usingApi(async api => {316 const maxWaitBlocks = 3;417 const maxWaitBlocks = 3;418 const xcmpQueueFailEvent = await waitEvent(api, maxWaitBlocks, 'xcmpQueue', 'Fail');419317420 expect(318 const xcmpQueueFailEvent = await helper.wait.event(maxWaitBlocks, 'xcmpQueue', 'Fail');421 xcmpQueueFailEvent != null,422 '[Karura] xcmpQueue.FailEvent event is expected',423 ).to.be.true;424319425 const event = xcmpQueueFailEvent!.event;320 expect(321 xcmpQueueFailEvent != null,426 const outcome = event.data[1] as XcmV2TraitsError;322 '[Karura] xcmpQueue.FailEvent event is expected',323 ).to.be.true;427324428 expect(325 const event = xcmpQueueFailEvent!.event;326 const outcome = event.data[1] as XcmV2TraitsError;327328 expect(429 outcome.isUntrustedReserveLocation,329 outcome.isUntrustedReserveLocation,430 '[Karura] The XCM error should be `UntrustedReserveLocation`',330 '[Karura] The XCM error should be `UntrustedReserveLocation`',431 ).to.be.true;331 ).to.be.true;432 });433 });332 });434});333});435334436describe_xcm('[XCM] Integration test: Exchanging QTZ with Moonriver', () => {335describeXcm('[XCM] Integration test: Exchanging QTZ with Moonriver', () => {437336438 // Quartz constants337 // Quartz constants439 let quartzAlice: IKeyringPair;338 let quartzDonor: IKeyringPair;440 let quartzAssetLocation;339 let quartzAssetLocation;441340442 let randomAccountQuartz: IKeyringPair;341 let randomAccountQuartz: IKeyringPair;445 // Moonriver constants344 // Moonriver constants446 let assetId: string;345 let assetId: string;447346448 const moonriverKeyring = new Keyring({type: 'ethereum'});449 const alithPrivateKey = '0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133';450 const baltatharPrivateKey = '0x8075991ce870b93a8870eca0c0f91913d12f47948ca0fd25b49c6fa7cdbeee8b';451 const dorothyPrivateKey = '0x39539ab1876910bbf3a223d84a29e28f1cb4e2e456503e7e91ed39b2e7223d68';452453 const alithAccount = moonriverKeyring.addFromUri(alithPrivateKey, undefined, 'ethereum');454 const baltatharAccount = moonriverKeyring.addFromUri(baltatharPrivateKey, undefined, 'ethereum');455 const dorothyAccount = moonriverKeyring.addFromUri(dorothyPrivateKey, undefined, 'ethereum');456457 const councilVotingThreshold = 2;347 const councilVotingThreshold = 2;458 const technicalCommitteeThreshold = 2;348 const technicalCommitteeThreshold = 2;459 const votingPeriod = 3;349 const votingPeriod = 3;464 symbol: 'xcQTZ',354 symbol: 'xcQTZ',465 decimals: 18,355 decimals: 18,466 isFrozen: false,356 isFrozen: false,467 minimalBalance: 1,357 minimalBalance: 1n,468 };358 };469359470 let balanceQuartzTokenInit: bigint;360 let balanceQuartzTokenInit: bigint;478 let balanceMovrTokenFinal: bigint;368 let balanceMovrTokenFinal: bigint;479369480 before(async () => {370 before(async () => {481 await usingApi(async (api, privateKeyWrapper) => {371 await usingPlaygrounds(async (helper, privateKey) => {482 const keyringEth = new Keyring({type: 'ethereum'});372 quartzDonor = privateKey('//Alice');483 const keyringSr25519 = new Keyring({type: 'sr25519'});373 [randomAccountQuartz] = await helper.arrange.createAccounts([0n], quartzDonor);484374485 quartzAlice = privateKeyWrapper('//Alice');486 randomAccountQuartz = generateKeyringPair(keyringSr25519);487 randomAccountMoonriver = generateKeyringPair(keyringEth);488489 balanceForeignQtzTokenInit = 0n;375 balanceForeignQtzTokenInit = 0n;490 });376 });491377492 await usingApi(378 await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {493 async (api) => {379 const alithAccount = helper.account.alithAccount();380 const baltatharAccount = helper.account.baltatharAccount();381 const dorothyAccount = helper.account.dorothyAccount();494382495 // >>> Sponsoring Dorothy >>>383 randomAccountMoonriver = helper.account.create();496 console.log('Sponsoring Dorothy.......');497 const tx0 = api.tx.balances.transfer(dorothyAccount.address, 11_000_000_000_000_000_000n);498 const events0 = await submitTransactionAsync(alithAccount, tx0);499 const result0 = getGenericResult(events0);500 expect(result0.success).to.be.true;501 console.log('Sponsoring Dorothy.......DONE');502 // <<< Sponsoring Dorothy <<<503384504 const sourceLocation: MultiLocation = api.createType(385 // >>> Sponsoring Dorothy >>>505 'MultiLocation',386 console.log('Sponsoring Dorothy.......');506 {387 await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);507 parents: 1,388 console.log('Sponsoring Dorothy.......DONE');508 interior: {X1: {Parachain: QUARTZ_CHAIN}},389 // <<< Sponsoring Dorothy <<<509 },510 );511390512 quartzAssetLocation = {XCM: sourceLocation};391 quartzAssetLocation = {392 XCM: {393 parents: 1,394 interior: {X1: {Parachain: QUARTZ_CHAIN}},513 const existentialDeposit = 1;395 },396 };397 const existentialDeposit = 1n;514 const isSufficient = true;398 const isSufficient = true;515 const unitsPerSecond = '1';399 const unitsPerSecond = 1n;516 const numAssetsWeightHint = 0;400 const numAssetsWeightHint = 0;517401518 const registerTx = api.tx.assetManager.registerForeignAsset(402 const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({519 quartzAssetLocation,403 location: quartzAssetLocation,520 quartzAssetMetadata,404 metadata: quartzAssetMetadata,521 existentialDeposit,405 existentialDeposit,522 isSufficient,406 isSufficient,523 );407 unitsPerSecond,524 console.log('Encoded proposal for registerAsset is %s', registerTx.method.toHex() || '');408 numAssetsWeightHint,409 });410 const proposalHash = blake2AsHex(encodedProposal);525411526 const setUnitsTx = api.tx.assetManager.setAssetUnitsPerSecond(412 console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);527 quartzAssetLocation,528 unitsPerSecond,529 numAssetsWeightHint,413 console.log('Encoded length %d', encodedProposal.length);530 );531 console.log('Encoded proposal for setAssetUnitsPerSecond is %s', setUnitsTx.method.toHex() || '');414 console.log('Encoded proposal hash for batch utility after schedule is %s', proposalHash);532415533 const batchCall = api.tx.utility.batchAll([registerTx, setUnitsTx]);416 // >>> Note motion preimage >>>417 console.log('Note motion preimage.......');418 await helper.democracy.notePreimage(baltatharAccount, encodedProposal);534 console.log('Encoded proposal for batchCall is %s', batchCall.method.toHex() || '');419 console.log('Note motion preimage.......DONE');420 // <<< Note motion preimage <<<535421536 // >>> Note motion preimage >>>422 // >>> Propose external motion through council >>>537 console.log('Note motion preimage.......');423 console.log('Propose external motion through council.......');538 const encodedProposal = batchCall?.method.toHex() || '';424 const externalMotion = helper.democracy.externalProposeMajority(proposalHash);539 const proposalHash = blake2AsHex(encodedProposal);425 const encodedMotion = externalMotion?.method.toHex() || '';540 console.log('Encoded proposal for batch utility after schedule is %s', encodedProposal);426 const motionHash = blake2AsHex(encodedMotion);541 console.log('Encoded proposal hash for batch utility after schedule is %s', proposalHash);427 console.log('Motion hash is %s', motionHash);542 console.log('Encoded length %d', encodedProposal.length);543428544 const tx1 = api.tx.democracy.notePreimage(encodedProposal);429 await helper.collective.council.propose(baltatharAccount, councilVotingThreshold, externalMotion, externalMotion.encodedLength);545 const events1 = await submitTransactionAsync(baltatharAccount, tx1);546 const result1 = getGenericResult(events1);547 expect(result1.success).to.be.true;548 console.log('Note motion preimage.......DONE');549 // <<< Note motion preimage <<<550430551 // >>> Propose external motion through council >>>431 const councilProposalIdx = await helper.collective.council.proposalCount() - 1;552 console.log('Propose external motion through council.......');553 const externalMotion = api.tx.democracy.externalProposeMajority(proposalHash);554 const tx2 = api.tx.councilCollective.propose(432 await helper.collective.council.vote(dorothyAccount, motionHash, councilProposalIdx, true);555 councilVotingThreshold,556 externalMotion,557 externalMotion.encodedLength,558 );559 const events2 = await submitTransactionAsync(baltatharAccount, tx2);433 await helper.collective.council.vote(baltatharAccount, motionHash, councilProposalIdx, true);560 const result2 = getGenericResult(events2);561 expect(result2.success).to.be.true;562434563 const encodedMotion = externalMotion?.method.toHex() || '';435 await helper.collective.council.close(dorothyAccount, motionHash, councilProposalIdx, 1_000_000_000, externalMotion.encodedLength);564 const motionHash = blake2AsHex(encodedMotion);436 console.log('Propose external motion through council.......DONE');565 console.log('Motion hash is %s', motionHash);437 // <<< Propose external motion through council <<<566438567 const tx3 = api.tx.councilCollective.vote(motionHash, 0, true);439 // >>> Fast track proposal through technical committee >>>568 {569 const events3 = await submitTransactionAsync(dorothyAccount, tx3);440 console.log('Fast track proposal through technical committee.......');570 const result3 = getGenericResult(events3);441 const fastTrack = helper.democracy.fastTrack(proposalHash, votingPeriod, delayPeriod);571 expect(result3.success).to.be.true;572 }442 const encodedFastTrack = fastTrack?.method.toHex() || '';573 {574 const events3 = await submitTransactionAsync(baltatharAccount, tx3);575 const result3 = getGenericResult(events3);443 const fastTrackHash = blake2AsHex(encodedFastTrack);576 expect(result3.success).to.be.true;444 console.log('FastTrack hash is %s', fastTrackHash);577 }578445579 const tx4 = api.tx.councilCollective.close(motionHash, 0, 1_000_000_000, externalMotion.encodedLength);446 await helper.collective.techCommittee.propose(alithAccount, technicalCommitteeThreshold, fastTrack, fastTrack.encodedLength);580 const events4 = await submitTransactionAsync(dorothyAccount, tx4);581 const result4 = getGenericResult(events4);582 expect(result4.success).to.be.true;583 console.log('Propose external motion through council.......DONE');584 // <<< Propose external motion through council <<<585447586 // >>> Fast track proposal through technical committee >>>448 const techProposalIdx = await helper.collective.techCommittee.proposalCount() - 1;587 console.log('Fast track proposal through technical committee.......');588 const fastTrack = api.tx.democracy.fastTrack(proposalHash, votingPeriod, delayPeriod);589 const tx5 = api.tx.techCommitteeCollective.propose(449 await helper.collective.techCommittee.vote(baltatharAccount, fastTrackHash, techProposalIdx, true);590 technicalCommitteeThreshold,591 fastTrack,592 fastTrack.encodedLength,593 );594 const events5 = await submitTransactionAsync(alithAccount, tx5);450 await helper.collective.techCommittee.vote(alithAccount, fastTrackHash, techProposalIdx, true);595 const result5 = getGenericResult(events5);596 expect(result5.success).to.be.true;597451598 const encodedFastTrack = fastTrack?.method.toHex() || '';452 await helper.collective.techCommittee.close(baltatharAccount, fastTrackHash, techProposalIdx, 1_000_000_000, fastTrack.encodedLength);599 const fastTrackHash = blake2AsHex(encodedFastTrack);453 console.log('Fast track proposal through technical committee.......DONE');600 console.log('FastTrack hash is %s', fastTrackHash);454 // <<< Fast track proposal through technical committee <<<601455602 const proposalIdx = Number(await api.query.techCommitteeCollective.proposalCount()) - 1;456 // >>> Referendum voting >>>603 const tx6 = api.tx.techCommitteeCollective.vote(fastTrackHash, proposalIdx, true);457 console.log('Referendum voting.......');604 {458 await helper.democracy.referendumVote(dorothyAccount, 0, {605 const events6 = await submitTransactionAsync(baltatharAccount, tx6);606 const result6 = getGenericResult(events6);607 expect(result6.success).to.be.true;459 balance: 10_000_000_000_000_000_000n,608 }460 vote: {aye: true, conviction: 1},609 {610 const events6 = await submitTransactionAsync(alithAccount, tx6);611 const result6 = getGenericResult(events6);461 });612 expect(result6.success).to.be.true;462 console.log('Referendum voting.......DONE');613 }463 // <<< Referendum voting <<<614464615 const tx7 = api.tx.techCommitteeCollective465 // >>> Acquire Quartz AssetId Info on Moonriver >>>616 .close(fastTrackHash, proposalIdx, 1_000_000_000, fastTrack.encodedLength);617 const events7 = await submitTransactionAsync(baltatharAccount, tx7);466 console.log('Acquire Quartz AssetId Info on Moonriver.......');618 const result7 = getGenericResult(events7);619 expect(result7.success).to.be.true;620 console.log('Fast track proposal through technical committee.......DONE');621 // <<< Fast track proposal through technical committee <<<622467623 // >>> Referendum voting >>>468 // Wait for the democracy execute624 console.log('Referendum voting.......');625 const tx8 = api.tx.democracy.vote(626 0,627 {Standard: {balance: 10_000_000_000_000_000_000n, vote: {aye: true, conviction: 1}}},469 await helper.wait.newBlocks(5);628 );629 const events8 = await submitTransactionAsync(dorothyAccount, tx8);630 const result8 = getGenericResult(events8);631 expect(result8.success).to.be.true;632 console.log('Referendum voting.......DONE');633 // <<< Referendum voting <<<634470635 // >>> Acquire Quartz AssetId Info on Moonriver >>>471 assetId = (await helper.assetManager.assetTypeId(quartzAssetLocation)).toString();636 console.log('Acquire Quartz AssetId Info on Moonriver.......');637472638 // Wait for the democracy execute473 console.log('QTZ asset ID is %s', assetId);474 console.log('Acquire Quartz AssetId Info on Moonriver.......DONE');639 await waitNewBlocks(api, 5);475 // >>> Acquire Quartz AssetId Info on Moonriver >>>640476641 assetId = (await api.query.assetManager.assetTypeId({477 // >>> Sponsoring random Account >>>478 console.log('Sponsoring random Account.......');642 XCM: sourceLocation,479 await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonriver.address, 11_000_000_000_000_000_000n);643 })).toString();480 console.log('Sponsoring random Account.......DONE');481 // <<< Sponsoring random Account <<<644482645 console.log('QTZ asset ID is %s', assetId);483 balanceMovrTokenInit = await helper.balance.getEthereum(randomAccountMoonriver.address);646 console.log('Acquire Quartz AssetId Info on Moonriver.......DONE');647 // >>> Acquire Quartz AssetId Info on Moonriver >>>484 });648485649 // >>> Sponsoring random Account >>>486 await usingPlaygrounds(async (helper) => {650 console.log('Sponsoring random Account.......');651 const tx10 = api.tx.balances.transfer(randomAccountMoonriver.address, 11_000_000_000_000_000_000n);652 const events10 = await submitTransactionAsync(baltatharAccount, tx10);653 const result10 = getGenericResult(events10);654 expect(result10.success).to.be.true;655 console.log('Sponsoring random Account.......DONE');656 // <<< Sponsoring random Account <<<657658 [balanceMovrTokenInit] = await getBalance(api, [randomAccountMoonriver.address]);659 },660 moonriverOptions(),661 );662663 await usingApi(async (api) => {664 const tx0 = api.tx.balances.transfer(randomAccountQuartz.address, 10n * TRANSFER_AMOUNT);487 await helper.balance.transferToSubstrate(quartzDonor, randomAccountQuartz.address, 10n * TRANSFER_AMOUNT);665 const events0 = await submitTransactionAsync(quartzAlice, tx0);488 balanceQuartzTokenInit = await helper.balance.getSubstrate(randomAccountQuartz.address);666 const result0 = getGenericResult(events0);667 expect(result0.success).to.be.true;668669 [balanceQuartzTokenInit] = await getBalance(api, [randomAccountQuartz.address]);670 });489 });671 });490 });672491673 it('Should connect and send QTZ to Moonriver', async () => {492 itSub('Should connect and send QTZ to Moonriver', async ({helper}) => {674 await usingApi(async (api) => {675 const currencyId = {493 const currencyId = {676 NativeAssetId: 'Here',494 NativeAssetId: 'Here',677 };495 };678 const dest = {496 const dest = {679 V1: {497 V1: {680 parents: 1,498 parents: 1,681 interior: {499 interior: {682 X2: [500 X2: [683 {Parachain: MOONRIVER_CHAIN},501 {Parachain: MOONRIVER_CHAIN},684 {AccountKey20: {network: 'Any', key: randomAccountMoonriver.address}},502 {AccountKey20: {network: 'Any', key: randomAccountMoonriver.address}},685 ],503 ],686 },687 },504 },688 };505 },689 const amount = TRANSFER_AMOUNT;506 };507 const amount = TRANSFER_AMOUNT;690 const destWeight = 850000000;508 const destWeight = 850000000;691509692 const tx = api.tx.xTokens.transfer(currencyId, amount, dest, destWeight);510 await helper.xTokens.transfer(randomAccountQuartz, currencyId, amount, dest, destWeight);693 const events = await submitTransactionAsync(randomAccountQuartz, tx);694 const result = getGenericResult(events);695 expect(result.success).to.be.true;696511697 [balanceQuartzTokenMiddle] = await getBalance(api, [randomAccountQuartz.address]);512 balanceQuartzTokenMiddle = await helper.balance.getSubstrate(randomAccountQuartz.address);698 expect(balanceQuartzTokenMiddle < balanceQuartzTokenInit).to.be.true;513 expect(balanceQuartzTokenMiddle < balanceQuartzTokenInit).to.be.true;699514700 const transactionFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;515 const transactionFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;701 console.log('[Quartz -> Moonriver] transaction fees on Quartz: %s QTZ', bigIntToDecimals(transactionFees));516 console.log('[Quartz -> Moonriver] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(transactionFees));702 expect(transactionFees > 0).to.be.true;517 expect(transactionFees > 0).to.be.true;703 });704518705 await usingApi(519 await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {706 async (api) => {707 await waitNewBlocks(api, 3);520 await helper.wait.newBlocks(3);708521709 [balanceMovrTokenMiddle] = await getBalance(api, [randomAccountMoonriver.address]);522 balanceMovrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonriver.address);710523711 const movrFees = balanceMovrTokenInit - balanceMovrTokenMiddle;524 const movrFees = balanceMovrTokenInit - balanceMovrTokenMiddle;712 console.log('[Quartz -> Moonriver] transaction fees on Moonriver: %s MOVR',bigIntToDecimals(movrFees));525 console.log('[Quartz -> Moonriver] transaction fees on Moonriver: %s MOVR',helper.util.bigIntToDecimals(movrFees));713 expect(movrFees == 0n).to.be.true;526 expect(movrFees == 0n).to.be.true;714527715 const qtzRandomAccountAsset = (528 balanceForeignQtzTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonriver.address))!; // BigInt(qtzRandomAccountAsset['balance']);716 await api.query.assets.account(assetId, randomAccountMoonriver.address)717 ).toJSON()! as any;718719 balanceForeignQtzTokenMiddle = BigInt(qtzRandomAccountAsset['balance']);720 const qtzIncomeTransfer = balanceForeignQtzTokenMiddle - balanceForeignQtzTokenInit;529 const qtzIncomeTransfer = balanceForeignQtzTokenMiddle - balanceForeignQtzTokenInit;721 console.log('[Quartz -> Moonriver] income %s QTZ', bigIntToDecimals(qtzIncomeTransfer));530 console.log('[Quartz -> Moonriver] income %s QTZ', helper.util.bigIntToDecimals(qtzIncomeTransfer));722 expect(qtzIncomeTransfer == TRANSFER_AMOUNT).to.be.true;531 expect(qtzIncomeTransfer == TRANSFER_AMOUNT).to.be.true;723 },532 });724 moonriverOptions(),725 );726 });533 });727534728 it('Should connect to Moonriver and send QTZ back', async () => {535 itSub('Should connect to Moonriver and send QTZ back', async ({helper}) => {729 await usingApi(536 await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {730 async (api) => {731 const asset = {537 const asset = {732 V1: {538 V1: {733 id: {539 id: {734 Concrete: {540 Concrete: {735 parents: 1,541 parents: 1,736 interior: {542 interior: {737 X1: {Parachain: QUARTZ_CHAIN},543 X1: {Parachain: QUARTZ_CHAIN},738 },739 },544 },740 },545 },741 fun: {742 Fungible: TRANSFER_AMOUNT,743 },744 },546 },745 };547 fun: {746 const destination = {747 V1: {748 parents: 1,548 Fungible: TRANSFER_AMOUNT,749 interior: {750 X2: [751 {Parachain: QUARTZ_CHAIN},752 {AccountId32: {network: 'Any', id: randomAccountQuartz.addressRaw}},753 ],754 },755 },549 },756 };550 },551 };757 const destWeight = 50000000;552 const destination = {553 V1: {554 parents: 1,555 interior: {556 X2: [557 {Parachain: QUARTZ_CHAIN},558 {AccountId32: {network: 'Any', id: randomAccountQuartz.addressRaw}},559 ],560 },561 },562 };563 const destWeight = 50000000;758564759 const tx = api.tx.xTokens.transferMultiasset(asset, destination, destWeight);565 await helper.xTokens.transferMultiasset(randomAccountMoonriver, asset, destination, destWeight);760 const events = await submitTransactionAsync(randomAccountMoonriver, tx);761 const result = getGenericResult(events);762 expect(result.success).to.be.true;763566764 [balanceMovrTokenFinal] = await getBalance(api, [randomAccountMoonriver.address]);567 balanceMovrTokenFinal = await helper.balance.getEthereum(randomAccountMoonriver.address);765568766 const movrFees = balanceMovrTokenMiddle - balanceMovrTokenFinal;569 const movrFees = balanceMovrTokenMiddle - balanceMovrTokenFinal;767 console.log('[Moonriver -> Quartz] transaction fees on Moonriver: %s MOVR', bigIntToDecimals(movrFees));570 console.log('[Moonriver -> Quartz] transaction fees on Moonriver: %s MOVR', helper.util.bigIntToDecimals(movrFees));768 expect(movrFees > 0).to.be.true;571 expect(movrFees > 0).to.be.true;769572770 const qtzRandomAccountAsset = (573 const qtzRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonriver.address);771 await api.query.assets.account(assetId, randomAccountMoonriver.address)772 ).toJSON()! as any;773574774 expect(qtzRandomAccountAsset).to.be.null;575 expect(qtzRandomAccountAsset).to.be.null;775576776 balanceForeignQtzTokenFinal = 0n;577 balanceForeignQtzTokenFinal = 0n;777578778 const qtzOutcomeTransfer = balanceForeignQtzTokenMiddle - balanceForeignQtzTokenFinal;579 const qtzOutcomeTransfer = balanceForeignQtzTokenMiddle - balanceForeignQtzTokenFinal;779 console.log('[Quartz -> Moonriver] outcome %s QTZ', bigIntToDecimals(qtzOutcomeTransfer));580 console.log('[Quartz -> Moonriver] outcome %s QTZ', helper.util.bigIntToDecimals(qtzOutcomeTransfer));780 expect(qtzOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;581 expect(qtzOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;781 },582 });782 moonriverOptions(),783 );784583785 await usingApi(async (api) => {584 await helper.wait.newBlocks(3);786 await waitNewBlocks(api, 3);787585788 [balanceQuartzTokenFinal] = await getBalance(api, [randomAccountQuartz.address]);586 balanceQuartzTokenFinal = await helper.balance.getSubstrate(randomAccountQuartz.address);789 const actuallyDelivered = balanceQuartzTokenFinal - balanceQuartzTokenMiddle;587 const actuallyDelivered = balanceQuartzTokenFinal - balanceQuartzTokenMiddle;790 expect(actuallyDelivered > 0).to.be.true;588 expect(actuallyDelivered > 0).to.be.true;791589792 console.log('[Moonriver -> Quartz] actually delivered %s QTZ', bigIntToDecimals(actuallyDelivered));590 console.log('[Moonriver -> Quartz] actually delivered %s QTZ', helper.util.bigIntToDecimals(actuallyDelivered));793591794 const qtzFees = TRANSFER_AMOUNT - actuallyDelivered;592 const qtzFees = TRANSFER_AMOUNT - actuallyDelivered;795 console.log('[Moonriver -> Quartz] transaction fees on Quartz: %s QTZ', bigIntToDecimals(qtzFees));593 console.log('[Moonriver -> Quartz] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees));796 expect(qtzFees == 0n).to.be.true;594 expect(qtzFees == 0n).to.be.true;797 });798 });595 });799});596});800597tests/src/xcm/xcmUnique.test.tsdiffbeforeafterboth14// You should have received a copy of the GNU General Public License14// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.161617import chai from 'chai';18import chaiAsPromised from 'chai-as-promised';1920import {WsProvider, Keyring} from '@polkadot/api';21import {ApiOptions} from '@polkadot/api/types';22import {IKeyringPair} from '@polkadot/types/types';17import {IKeyringPair} from '@polkadot/types/types';23import usingApi, {submitTransactionAsync} from '../substrate/substrate-api';24import {getGenericResult, generateKeyringPair, waitEvent, describe_xcm, bigIntToDecimals} from '../deprecated-helpers/helpers';25import {MultiLocation} from '@polkadot/types/interfaces';26import {blake2AsHex} from '@polkadot/util-crypto';18import {blake2AsHex} from '@polkadot/util-crypto';27import waitNewBlocks from '../substrate/wait-new-blocks';19import config from '../config';28import getBalance from '../substrate/get-balance';29import {XcmV2TraitsError, XcmV2TraitsOutcome} from '../interfaces';20import {XcmV2TraitsError, XcmV2TraitsOutcome} from '../interfaces';21import {itSub, expect, describeXcm, usingPlaygrounds, usingAcalaPlaygrounds, usingRelayPlaygrounds, usingMoonbeamPlaygrounds} from '../util/playgrounds';302231chai.use(chaiAsPromised);32const expect = chai.expect;3334const UNIQUE_CHAIN = 2037;23const UNIQUE_CHAIN = 2037;35const ACALA_CHAIN = 2000;24const ACALA_CHAIN = 2000;36const MOONBEAM_CHAIN = 2004;25const MOONBEAM_CHAIN = 2004;372638const RELAY_PORT = 9844;27const relayUrl = config.relayUrl;39const ACALA_PORT = 9946;28const acalaUrl = config.acalaUrl;40const MOONBEAM_PORT = 9947;29const moonbeamUrl = config.moonbeamUrl;413042const ACALA_DECIMALS = 12;31const ACALA_DECIMALS = 12;433244const TRANSFER_AMOUNT = 2000000000000000000000000n;33const TRANSFER_AMOUNT = 2000000000000000000000000n;453446function parachainApiOptions(port: number): ApiOptions {47 return {48 provider: new WsProvider('ws://127.0.0.1:' + port.toString()),49 };50}5152function acalaOptions(): ApiOptions {53 return parachainApiOptions(ACALA_PORT);54}5556function moonbeamOptions(): ApiOptions {57 return parachainApiOptions(MOONBEAM_PORT);58}5960function relayOptions(): ApiOptions {61 return parachainApiOptions(RELAY_PORT);62}6364describe_xcm('[XCM] Integration test: Exchanging tokens with Acala', () => {35describeXcm('[XCM] Integration test: Exchanging tokens with Acala', () => {65 let alice: IKeyringPair;36 let alice: IKeyringPair;66 let randomAccount: IKeyringPair;37 let randomAccount: IKeyringPair;673876 let balanceUniqueForeignTokenFinal: bigint;47 let balanceUniqueForeignTokenFinal: bigint;774878 before(async () => {49 before(async () => {79 await usingApi(async (api, privateKeyWrapper) => {50 await usingPlaygrounds(async (helper, privateKey) => {80 const keyringSr25519 = new Keyring({type: 'sr25519'});51 alice = privateKey('//Alice');8182 alice = privateKeyWrapper('//Alice');83 randomAccount = generateKeyringPair(keyringSr25519);52 [randomAccount] = await helper.arrange.createAccounts([0n], alice);84 });53 });855486 // Acala side55 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {87 await usingApi(88 async (api) => {89 const destination = {90 V0: {91 X2: [92 'Parent',93 {94 Parachain: UNIQUE_CHAIN,95 },96 ],97 },98 };99100 const metadata = {101 name: 'UNQ',102 symbol: 'UNQ',103 decimals: 18,104 minimalBalance: 1,105 };106107 const tx = api.tx.assetRegistry.registerForeignAsset(destination, metadata);108 const sudoTx = api.tx.sudo.sudo(tx as any);109 const events = await submitTransactionAsync(alice, sudoTx);110 const result = getGenericResult(events);111 expect(result.success).to.be.true;112113 const tx1 = api.tx.balances.transfer(randomAccount.address, 10000000000000n);114 const events1 = await submitTransactionAsync(alice, tx1);115 const result1 = getGenericResult(events1);116 expect(result1.success).to.be.true;117118 [balanceAcalaTokenInit] = await getBalance(api, [randomAccount.address]);119 {120 const {free} = (await api.query.tokens.accounts(randomAccount.addressRaw, {ForeignAsset: 0})).toJSON() as any;121 balanceUniqueForeignTokenInit = BigInt(free);122 }123 },124 acalaOptions(),125 );126127 // Unique side128 await usingApi(async (api) => {129 const tx0 = api.tx.balances.transfer(randomAccount.address, 10n * TRANSFER_AMOUNT);130 const events0 = await submitTransactionAsync(alice, tx0);131 const result0 = getGenericResult(events0);132 expect(result0.success).to.be.true;133134 [balanceUniqueTokenInit] = await getBalance(api, [randomAccount.address]);135 });136 });137138 it('Should connect and send UNQ to Acala', async () => {139140 // Unique side141 await usingApi(async (api) => {142143 const destination = {56 const destination = {144 V0: {57 V0: {145 X2: [58 X2: [146 'Parent',59 'Parent',147 {60 {148 Parachain: ACALA_CHAIN,61 Parachain: UNIQUE_CHAIN,149 },62 },150 ],63 ],151 },64 },152 };65 };15366154 const beneficiary = {67 const metadata = {155 V0: {68 name: 'UNQ',156 X1: {69 symbol: 'UNQ',157 AccountId32: {158 network: 'Any',159 id: randomAccount.addressRaw,70 decimals: 18,160 },71 minimalBalance: 1n,161 },162 },163 };72 };16473165 const assets = {74 await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);75 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);76 balanceAcalaTokenInit = await helper.balance.getSubstrate(randomAccount.address);77 balanceUniqueForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});78 });7980 await usingPlaygrounds(async (helper) => {81 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);82 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);83 });84 });8586 itSub('Should connect and send UNQ to Acala', async ({helper}) => {8788 const destination = {166 V1: [89 V0: {90 X2: [91 'Parent',167 {92 {168 id: {93 Parachain: ACALA_CHAIN,169 Concrete: {170 parents: 0,171 interior: 'Here',172 },173 },174 fun: {175 Fungible: TRANSFER_AMOUNT,176 },177 },94 },178 ],95 ],179 };96 },97 };18098181 const feeAssetItem = 0;99 const beneficiary = {100 V0: {101 X1: {102 AccountId32: {103 network: 'Any',104 id: randomAccount.addressRaw,105 },106 },107 },108 };182109183 const weightLimit = {110 const assets = {184 Limited: 5000000000,111 V1: [112 {113 id: {114 Concrete: {115 parents: 0,185 };116 interior: 'Here',117 },118 },119 fun: {120 Fungible: TRANSFER_AMOUNT,121 },122 },123 ],124 };186125187 const tx = api.tx.polkadotXcm.limitedReserveTransferAssets(destination, beneficiary, assets, feeAssetItem, weightLimit);126 const feeAssetItem = 0;188 const events = await submitTransactionAsync(randomAccount, tx);189 const result = getGenericResult(events);127 const weightLimit = 5000000000;190 expect(result.success).to.be.true;191128192 [balanceUniqueTokenMiddle] = await getBalance(api, [randomAccount.address]);129 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, weightLimit);193130194 const unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;131 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);195 console.log('[Unique -> Acala] transaction fees on Unique: %s UNQ', bigIntToDecimals(unqFees));196 expect(unqFees > 0n).to.be.true;197 });198132199 // Acala side133 const unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;200 await usingApi(201 async (api) => {134 console.log('[Unique -> Acala] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));202 await waitNewBlocks(api, 3);203 const {free} = (await api.query.tokens.accounts(randomAccount.addressRaw, {ForeignAsset: 0})).toJSON() as any;204 balanceUniqueForeignTokenMiddle = BigInt(free);135 expect(unqFees > 0n).to.be.true;205136206 [balanceAcalaTokenMiddle] = await getBalance(api, [randomAccount.address]);137 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {138 await helper.wait.newBlocks(3);207139208 const acaFees = balanceAcalaTokenInit - balanceAcalaTokenMiddle;140 balanceUniqueForeignTokenMiddle = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});209 const unqIncomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenInit;141 balanceAcalaTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);210142211 console.log(143 const acaFees = balanceAcalaTokenInit - balanceAcalaTokenMiddle;144 const unqIncomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenInit;145146 console.log(212 '[Unique -> Acala] transaction fees on Acala: %s ACA',147 '[Unique -> Acala] transaction fees on Acala: %s ACA',213 bigIntToDecimals(acaFees, ACALA_DECIMALS),148 helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),214 );149 );215 console.log('[Unique -> Acala] income %s UNQ', bigIntToDecimals(unqIncomeTransfer));150 console.log('[Unique -> Acala] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));216 expect(acaFees == 0n).to.be.true;151 expect(acaFees == 0n).to.be.true;217 expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;152 expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;218 },153 });219 acalaOptions(),220 );221 });154 });222155223 it('Should connect to Acala and send UNQ back', async () => {156 itSub('Should connect to Acala and send UNQ back', async ({helper}) => {224225 // Acala side157 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {226 await usingApi(227 async (api) => {228 const destination = {158 const destination = {229 V1: {159 V1: {230 parents: 1,160 parents: 1,231 interior: {161 interior: {232 X2: [162 X2: [233 {Parachain: UNIQUE_CHAIN},163 {Parachain: UNIQUE_CHAIN},234 {164 {235 AccountId32: {165 AccountId32: {236 network: 'Any',166 network: 'Any',237 id: randomAccount.addressRaw,167 id: randomAccount.addressRaw,238 },239 },168 },240 ],169 },241 },170 ],242 },171 },243 };172 },173 };244174245 const id = {175 const id = {246 ForeignAsset: 0,176 ForeignAsset: 0,247 };177 };248178249 const destWeight = 50000000;179 const destWeight = 50000000;250180251 const tx = api.tx.xTokens.transfer(id as any, TRANSFER_AMOUNT, destination, destWeight);181 await helper.xTokens.transfer(randomAccount, id, TRANSFER_AMOUNT, destination, destWeight);252 const events = await submitTransactionAsync(randomAccount, tx);253 const result = getGenericResult(events);254 expect(result.success).to.be.true;255182256 [balanceAcalaTokenFinal] = await getBalance(api, [randomAccount.address]);183 balanceAcalaTokenFinal = await helper.balance.getSubstrate(randomAccount.address);257 {184 balanceUniqueForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);258 const {free} = (await api.query.tokens.accounts(randomAccount.addressRaw, id)).toJSON() as any;259 balanceUniqueForeignTokenFinal = BigInt(free);260 }261185262 const acaFees = balanceAcalaTokenMiddle - balanceAcalaTokenFinal;186 const acaFees = balanceAcalaTokenMiddle - balanceAcalaTokenFinal;263 const unqOutcomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenFinal;187 const unqOutcomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenFinal;264188265 console.log(189 console.log(266 '[Acala -> Unique] transaction fees on Acala: %s ACA',190 '[Acala -> Unique] transaction fees on Acala: %s ACA',267 bigIntToDecimals(acaFees, ACALA_DECIMALS),191 helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),268 );192 );269 console.log('[Acala -> Unique] outcome %s UNQ', bigIntToDecimals(unqOutcomeTransfer));193 console.log('[Acala -> Unique] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));270194271 expect(acaFees > 0).to.be.true;195 expect(acaFees > 0).to.be.true;272 expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;196 expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;273 },197 });274 acalaOptions(),275 );276198277 // Unique side199 await helper.wait.newBlocks(3);278 await usingApi(async (api) => {279 await waitNewBlocks(api, 3);280200281 [balanceUniqueTokenFinal] = await getBalance(api, [randomAccount.address]);201 balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address);282 const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;202 const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;283 expect(actuallyDelivered > 0).to.be.true;203 expect(actuallyDelivered > 0).to.be.true;284204285 console.log('[Acala -> Unique] actually delivered %s UNQ', bigIntToDecimals(actuallyDelivered));205 console.log('[Acala -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));286206287 const unqFees = TRANSFER_AMOUNT - actuallyDelivered;207 const unqFees = TRANSFER_AMOUNT - actuallyDelivered;288 console.log('[Acala -> Unique] transaction fees on Unique: %s UNQ', bigIntToDecimals(unqFees));208 console.log('[Acala -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));289 expect(unqFees == 0n).to.be.true;209 expect(unqFees == 0n).to.be.true;290 });291 });210 });292});211});293212294// These tests are relevant only when the foreign asset pallet is disabled213// These tests are relevant only when the foreign asset pallet is disabled295describe_xcm('[XCM] Integration test: Unique rejects non-native tokens', () => {214describeXcm('[XCM] Integration test: Unique rejects non-native tokens', () => {296 let alice: IKeyringPair;215 let alice: IKeyringPair;297216298 before(async () => {217 before(async () => {299 await usingApi(async (api, privateKeyWrapper) => {218 await usingPlaygrounds(async (_helper, privateKey) => {300 alice = privateKeyWrapper('//Alice');219 alice = privateKey('//Alice');301 });220 });302 });221 });303222304 it('Unique rejects tokens from the Relay', async () => {223 itSub('Unique rejects tokens from the Relay', async ({helper}) => {305 await usingApi(async (api) => {224 await usingRelayPlaygrounds(relayUrl, async (helper) => {306 const destination = {225 const destination = {307 V1: {226 V1: {308 parents: 0,227 parents: 0,341 };260 };342261343 const feeAssetItem = 0;262 const feeAssetItem = 0;263 const weightLimit = 5_000_000_000;344264345 const weightLimit = {265 await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, weightLimit);346 Limited: 5_000_000_000,347 };266 });348267349 const tx = api.tx.xcmPallet.limitedReserveTransferAssets(destination, beneficiary, assets, feeAssetItem, weightLimit);268 const maxWaitBlocks = 3;350 const events = await submitTransactionAsync(alice, tx);351 const result = getGenericResult(events);352 expect(result.success).to.be.true;353 }, relayOptions());354269355 await usingApi(async api => {270 const dmpQueueExecutedDownward = await helper.wait.event(maxWaitBlocks, 'dmpQueue', 'ExecutedDownward');356 const maxWaitBlocks = 3;357 const dmpQueueExecutedDownward = await waitEvent(358 api,359 maxWaitBlocks,360 'dmpQueue',361 'ExecutedDownward',362 );363271364 expect(272 expect(365 dmpQueueExecutedDownward != null,273 dmpQueueExecutedDownward != null,366 '[Relay] dmpQueue.ExecutedDownward event is expected',274 '[Relay] dmpQueue.ExecutedDownward event is expected',367 ).to.be.true;275 ).to.be.true;368276369 const event = dmpQueueExecutedDownward!.event;277 const event = dmpQueueExecutedDownward!.event;370 const outcome = event.data[1] as XcmV2TraitsOutcome;278 const outcome = event.data[1] as XcmV2TraitsOutcome;371279372 expect(280 expect(373 outcome.isIncomplete,281 outcome.isIncomplete,374 '[Relay] The outcome of the XCM should be `Incomplete`',282 '[Relay] The outcome of the XCM should be `Incomplete`',375 ).to.be.true;283 ).to.be.true;376284377 const incomplete = outcome.asIncomplete;285 const incomplete = outcome.asIncomplete;378 expect(286 expect(379 incomplete[1].toString() == 'AssetNotFound',287 incomplete[1].toString() == 'AssetNotFound',380 '[Relay] The XCM error should be `AssetNotFound`',288 '[Relay] The XCM error should be `AssetNotFound`',381 ).to.be.true;289 ).to.be.true;382 });383 });290 });384291385 it('Unique rejects ACA tokens from Acala', async () => {292 itSub('Unique rejects ACA tokens from Acala', async ({helper}) => {386 await usingApi(async (api) => {293 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {387 const destination = {294 const destination = {388 V1: {295 V1: {389 parents: 1,296 parents: 1,407314408 const destWeight = 50000000;315 const destWeight = 50000000;409316410 const tx = api.tx.xTokens.transfer(id as any, 100_000_000_000, destination, destWeight);317 await helper.xTokens.transfer(alice, id, 100_000_000_000n, destination, destWeight);411 const events = await submitTransactionAsync(alice, tx);318 });412 const result = getGenericResult(events);413 expect(result.success).to.be.true;414 }, acalaOptions());415319416 await usingApi(async api => {320 const maxWaitBlocks = 3;417 const maxWaitBlocks = 3;418 const xcmpQueueFailEvent = await waitEvent(api, maxWaitBlocks, 'xcmpQueue', 'Fail');419321420 expect(322 const xcmpQueueFailEvent = await helper.wait.event(maxWaitBlocks, 'xcmpQueue', 'Fail');421 xcmpQueueFailEvent != null,422 '[Acala] xcmpQueue.FailEvent event is expected',423 ).to.be.true;424323425 const event = xcmpQueueFailEvent!.event;324 expect(325 xcmpQueueFailEvent != null,426 const outcome = event.data[1] as XcmV2TraitsError;326 '[Acala] xcmpQueue.FailEvent event is expected',327 ).to.be.true;427328428 expect(329 const event = xcmpQueueFailEvent!.event;330 const outcome = event.data[1] as XcmV2TraitsError;331332 expect(429 outcome.isUntrustedReserveLocation,333 outcome.isUntrustedReserveLocation,430 '[Acala] The XCM error should be `UntrustedReserveLocation`',334 '[Acala] The XCM error should be `UntrustedReserveLocation`',431 ).to.be.true;335 ).to.be.true;432 });433 });336 });434});337});435338436describe_xcm('[XCM] Integration test: Exchanging UNQ with Moonbeam', () => {339describeXcm('[XCM] Integration test: Exchanging UNQ with Moonbeam', () => {437340438 // Unique constants341 // Unique constants439 let uniqueAlice: IKeyringPair;342 let uniqueDonor: IKeyringPair;440 let uniqueAssetLocation;343 let uniqueAssetLocation;441344442 let randomAccountUnique: IKeyringPair;345 let randomAccountUnique: IKeyringPair;445 // Moonbeam constants348 // Moonbeam constants446 let assetId: string;349 let assetId: string;447350448 const moonbeamKeyring = new Keyring({type: 'ethereum'});449 const alithPrivateKey = '0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133';450 const baltatharPrivateKey = '0x8075991ce870b93a8870eca0c0f91913d12f47948ca0fd25b49c6fa7cdbeee8b';451 const dorothyPrivateKey = '0x39539ab1876910bbf3a223d84a29e28f1cb4e2e456503e7e91ed39b2e7223d68';452453 const alithAccount = moonbeamKeyring.addFromUri(alithPrivateKey, undefined, 'ethereum');454 const baltatharAccount = moonbeamKeyring.addFromUri(baltatharPrivateKey, undefined, 'ethereum');455 const dorothyAccount = moonbeamKeyring.addFromUri(dorothyPrivateKey, undefined, 'ethereum');456457 const councilVotingThreshold = 2;351 const councilVotingThreshold = 2;458 const technicalCommitteeThreshold = 2;352 const technicalCommitteeThreshold = 2;459 const votingPeriod = 3;353 const votingPeriod = 3;464 symbol: 'xcUNQ',358 symbol: 'xcUNQ',465 decimals: 18,359 decimals: 18,466 isFrozen: false,360 isFrozen: false,467 minimalBalance: 1,361 minimalBalance: 1n,468 };362 };469363470 let balanceUniqueTokenInit: bigint;364 let balanceUniqueTokenInit: bigint;478 let balanceGlmrTokenFinal: bigint;372 let balanceGlmrTokenFinal: bigint;479373480 before(async () => {374 before(async () => {481 await usingApi(async (api, privateKeyWrapper) => {375 await usingPlaygrounds(async (helper, privateKey) => {482 const keyringEth = new Keyring({type: 'ethereum'});376 uniqueDonor = privateKey('//Alice');483 const keyringSr25519 = new Keyring({type: 'sr25519'});377 [randomAccountUnique] = await helper.arrange.createAccounts([0n], uniqueDonor);484378485 uniqueAlice = privateKeyWrapper('//Alice');486 randomAccountUnique = generateKeyringPair(keyringSr25519);487 randomAccountMoonbeam = generateKeyringPair(keyringEth);488489 balanceForeignUnqTokenInit = 0n;379 balanceForeignUnqTokenInit = 0n;490 });380 });491381492 await usingApi(382 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {493 async (api) => {383 const alithAccount = helper.account.alithAccount();384 const baltatharAccount = helper.account.baltatharAccount();385 const dorothyAccount = helper.account.dorothyAccount();494386495 // >>> Sponsoring Dorothy >>>387 randomAccountMoonbeam = helper.account.create();496 console.log('Sponsoring Dorothy.......');497 const tx0 = api.tx.balances.transfer(dorothyAccount.address, 11_000_000_000_000_000_000n);498 const events0 = await submitTransactionAsync(alithAccount, tx0);499 const result0 = getGenericResult(events0);500 expect(result0.success).to.be.true;501 console.log('Sponsoring Dorothy.......DONE');502 // <<< Sponsoring Dorothy <<<503388504 const sourceLocation: MultiLocation = api.createType(389 // >>> Sponsoring Dorothy >>>505 'MultiLocation',390 console.log('Sponsoring Dorothy.......');506 {391 await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);507 parents: 1,392 console.log('Sponsoring Dorothy.......DONE');508 interior: {X1: {Parachain: UNIQUE_CHAIN}},393 // <<< Sponsoring Dorothy <<<509 },510 );511394512 uniqueAssetLocation = {XCM: sourceLocation};395 uniqueAssetLocation = {396 XCM: {397 parents: 1,398 interior: {X1: {Parachain: UNIQUE_CHAIN}},513 const existentialDeposit = 1;399 },400 };401 const existentialDeposit = 1n;514 const isSufficient = true;402 const isSufficient = true;515 const unitsPerSecond = '1';403 const unitsPerSecond = 1n;516 const numAssetsWeightHint = 0;404 const numAssetsWeightHint = 0;517405518 const registerTx = api.tx.assetManager.registerForeignAsset(406 const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({519 uniqueAssetLocation,407 location: uniqueAssetLocation,520 uniqueAssetMetadata,408 metadata: uniqueAssetMetadata,521 existentialDeposit,409 existentialDeposit,522 isSufficient,410 isSufficient,523 );411 unitsPerSecond,524 console.log('Encoded proposal for registerAsset is %s', registerTx.method.toHex() || '');412 numAssetsWeightHint,413 });414 const proposalHash = blake2AsHex(encodedProposal);525415526 const setUnitsTx = api.tx.assetManager.setAssetUnitsPerSecond(416 console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);527 uniqueAssetLocation,528 unitsPerSecond,529 numAssetsWeightHint,417 console.log('Encoded length %d', encodedProposal.length);530 );531 console.log('Encoded proposal for setAssetUnitsPerSecond is %s', setUnitsTx.method.toHex() || '');418 console.log('Encoded proposal hash for batch utility after schedule is %s', proposalHash);532419533 const batchCall = api.tx.utility.batchAll([registerTx, setUnitsTx]);420 // >>> Note motion preimage >>>421 console.log('Note motion preimage.......');422 await helper.democracy.notePreimage(baltatharAccount, encodedProposal);534 console.log('Encoded proposal for batchCall is %s', batchCall.method.toHex() || '');423 console.log('Note motion preimage.......DONE');424 // <<< Note motion preimage <<<535425536 // >>> Note motion preimage >>>426 // >>> Propose external motion through council >>>537 console.log('Note motion preimage.......');427 console.log('Propose external motion through council.......');538 const encodedProposal = batchCall?.method.toHex() || '';428 const externalMotion = helper.democracy.externalProposeMajority(proposalHash);539 const proposalHash = blake2AsHex(encodedProposal);429 const encodedMotion = externalMotion?.method.toHex() || '';540 console.log('Encoded proposal for batch utility after schedule is %s', encodedProposal);430 const motionHash = blake2AsHex(encodedMotion);541 console.log('Encoded proposal hash for batch utility after schedule is %s', proposalHash);431 console.log('Motion hash is %s', motionHash);542 console.log('Encoded length %d', encodedProposal.length);543432544 const tx1 = api.tx.democracy.notePreimage(encodedProposal);433 await helper.collective.council.propose(baltatharAccount, councilVotingThreshold, externalMotion, externalMotion.encodedLength);545 const events1 = await submitTransactionAsync(baltatharAccount, tx1);546 const result1 = getGenericResult(events1);547 expect(result1.success).to.be.true;548 console.log('Note motion preimage.......DONE');549 // <<< Note motion preimage <<<550434551 // >>> Propose external motion through council >>>435 const councilProposalIdx = await helper.collective.council.proposalCount() - 1;552 console.log('Propose external motion through council.......');553 const externalMotion = api.tx.democracy.externalProposeMajority(proposalHash);554 const tx2 = api.tx.councilCollective.propose(436 await helper.collective.council.vote(dorothyAccount, motionHash, councilProposalIdx, true);555 councilVotingThreshold,556 externalMotion,557 externalMotion.encodedLength,558 );559 const events2 = await submitTransactionAsync(baltatharAccount, tx2);437 await helper.collective.council.vote(baltatharAccount, motionHash, councilProposalIdx, true);560 const result2 = getGenericResult(events2);561 expect(result2.success).to.be.true;562438563 const encodedMotion = externalMotion?.method.toHex() || '';439 await helper.collective.council.close(dorothyAccount, motionHash, councilProposalIdx, 1_000_000_000, externalMotion.encodedLength);564 const motionHash = blake2AsHex(encodedMotion);440 console.log('Propose external motion through council.......DONE');565 console.log('Motion hash is %s', motionHash);441 // <<< Propose external motion through council <<<566442567 const tx3 = api.tx.councilCollective.vote(motionHash, 0, true);443 // >>> Fast track proposal through technical committee >>>568 {569 const events3 = await submitTransactionAsync(dorothyAccount, tx3);444 console.log('Fast track proposal through technical committee.......');570 const result3 = getGenericResult(events3);445 const fastTrack = helper.democracy.fastTrack(proposalHash, votingPeriod, delayPeriod);571 expect(result3.success).to.be.true;572 }446 const encodedFastTrack = fastTrack?.method.toHex() || '';573 {574 const events3 = await submitTransactionAsync(baltatharAccount, tx3);575 const result3 = getGenericResult(events3);447 const fastTrackHash = blake2AsHex(encodedFastTrack);576 expect(result3.success).to.be.true;448 console.log('FastTrack hash is %s', fastTrackHash);577 }578449579 const tx4 = api.tx.councilCollective.close(motionHash, 0, 1_000_000_000, externalMotion.encodedLength);450 await helper.collective.techCommittee.propose(alithAccount, technicalCommitteeThreshold, fastTrack, fastTrack.encodedLength);580 const events4 = await submitTransactionAsync(dorothyAccount, tx4);581 const result4 = getGenericResult(events4);582 expect(result4.success).to.be.true;583 console.log('Propose external motion through council.......DONE');584 // <<< Propose external motion through council <<<585451586 // >>> Fast track proposal through technical committee >>>452 const techProposalIdx = await helper.collective.techCommittee.proposalCount() - 1;587 console.log('Fast track proposal through technical committee.......');588 const fastTrack = api.tx.democracy.fastTrack(proposalHash, votingPeriod, delayPeriod);589 const tx5 = api.tx.techCommitteeCollective.propose(453 await helper.collective.techCommittee.vote(baltatharAccount, fastTrackHash, techProposalIdx, true);590 technicalCommitteeThreshold,591 fastTrack,592 fastTrack.encodedLength,593 );594 const events5 = await submitTransactionAsync(alithAccount, tx5);454 await helper.collective.techCommittee.vote(alithAccount, fastTrackHash, techProposalIdx, true);595 const result5 = getGenericResult(events5);596 expect(result5.success).to.be.true;597455598 const encodedFastTrack = fastTrack?.method.toHex() || '';456 await helper.collective.techCommittee.close(baltatharAccount, fastTrackHash, techProposalIdx, 1_000_000_000, fastTrack.encodedLength);599 const fastTrackHash = blake2AsHex(encodedFastTrack);457 console.log('Fast track proposal through technical committee.......DONE');600 console.log('FastTrack hash is %s', fastTrackHash);458 // <<< Fast track proposal through technical committee <<<601459602 const proposalIdx = Number(await api.query.techCommitteeCollective.proposalCount()) - 1;460 // >>> Referendum voting >>>603 const tx6 = api.tx.techCommitteeCollective.vote(fastTrackHash, proposalIdx, true);461 console.log('Referendum voting.......');604 {462 await helper.democracy.referendumVote(dorothyAccount, 0, {605 const events6 = await submitTransactionAsync(baltatharAccount, tx6);606 const result6 = getGenericResult(events6);607 expect(result6.success).to.be.true;463 balance: 10_000_000_000_000_000_000n,608 }464 vote: {aye: true, conviction: 1},609 {610 const events6 = await submitTransactionAsync(alithAccount, tx6);611 const result6 = getGenericResult(events6);465 });612 expect(result6.success).to.be.true;466 console.log('Referendum voting.......DONE');613 }467 // <<< Referendum voting <<<614468615 const tx7 = api.tx.techCommitteeCollective469 // >>> Acquire Unique AssetId Info on Moonbeam >>>616 .close(fastTrackHash, proposalIdx, 1_000_000_000, fastTrack.encodedLength);617 const events7 = await submitTransactionAsync(baltatharAccount, tx7);470 console.log('Acquire Unique AssetId Info on Moonbeam.......');618 const result7 = getGenericResult(events7);619 expect(result7.success).to.be.true;620 console.log('Fast track proposal through technical committee.......DONE');621 // <<< Fast track proposal through technical committee <<<622471623 // >>> Referendum voting >>>472 // Wait for the democracy execute624 console.log('Referendum voting.......');625 const tx8 = api.tx.democracy.vote(626 0,627 {Standard: {balance: 10_000_000_000_000_000_000n, vote: {aye: true, conviction: 1}}},473 await helper.wait.newBlocks(5);628 );629 const events8 = await submitTransactionAsync(dorothyAccount, tx8);630 const result8 = getGenericResult(events8);631 expect(result8.success).to.be.true;632 console.log('Referendum voting.......DONE');633 // <<< Referendum voting <<<634474635 // >>> Acquire Unique AssetId Info on Moonbeam >>>475 assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString();636 console.log('Acquire Unique AssetId Info on Moonbeam.......');637476638 // Wait for the democracy execute477 console.log('UNQ asset ID is %s', assetId);478 console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');639 await waitNewBlocks(api, 5);479 // >>> Acquire Unique AssetId Info on Moonbeam >>>640480641 assetId = (await api.query.assetManager.assetTypeId({481 // >>> Sponsoring random Account >>>482 console.log('Sponsoring random Account.......');642 XCM: sourceLocation,483 await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);643 })).toString();484 console.log('Sponsoring random Account.......DONE');485 // <<< Sponsoring random Account <<<644486645 console.log('UNQ asset ID is %s', assetId);487 balanceGlmrTokenInit = await helper.balance.getEthereum(randomAccountMoonbeam.address);646 console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');647 // >>> Acquire Unique AssetId Info on Moonbeam >>>488 });648489649 // >>> Sponsoring random Account >>>490 await usingPlaygrounds(async (helper) => {650 console.log('Sponsoring random Account.......');651 const tx10 = api.tx.balances.transfer(randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);652 const events10 = await submitTransactionAsync(baltatharAccount, tx10);653 const result10 = getGenericResult(events10);654 expect(result10.success).to.be.true;655 console.log('Sponsoring random Account.......DONE');656 // <<< Sponsoring random Account <<<657658 [balanceGlmrTokenInit] = await getBalance(api, [randomAccountMoonbeam.address]);659 },660 moonbeamOptions(),661 );662663 await usingApi(async (api) => {664 const tx0 = api.tx.balances.transfer(randomAccountUnique.address, 10n * TRANSFER_AMOUNT);491 await helper.balance.transferToSubstrate(uniqueDonor, randomAccountUnique.address, 10n * TRANSFER_AMOUNT);665 const events0 = await submitTransactionAsync(uniqueAlice, tx0);492 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccountUnique.address);666 const result0 = getGenericResult(events0);667 expect(result0.success).to.be.true;668669 [balanceUniqueTokenInit] = await getBalance(api, [randomAccountUnique.address]);670 });493 });671 });494 });672495673 it('Should connect and send UNQ to Moonbeam', async () => {496 itSub('Should connect and send UNQ to Moonbeam', async ({helper}) => {674 await usingApi(async (api) => {675 const currencyId = {497 const currencyId = {676 NativeAssetId: 'Here',498 NativeAssetId: 'Here',677 };499 };678 const dest = {500 const dest = {679 V1: {501 V1: {680 parents: 1,502 parents: 1,681 interior: {503 interior: {682 X2: [504 X2: [683 {Parachain: MOONBEAM_CHAIN},505 {Parachain: MOONBEAM_CHAIN},684 {AccountKey20: {network: 'Any', key: randomAccountMoonbeam.address}},506 {AccountKey20: {network: 'Any', key: randomAccountMoonbeam.address}},685 ],507 ],686 },687 },508 },688 };509 },689 const amount = TRANSFER_AMOUNT;510 };511 const amount = TRANSFER_AMOUNT;690 const destWeight = 850000000;512 const destWeight = 850000000;691513692 const tx = api.tx.xTokens.transfer(currencyId, amount, dest, destWeight);514 await helper.xTokens.transfer(randomAccountUnique, currencyId, amount, dest, destWeight);693 const events = await submitTransactionAsync(randomAccountUnique, tx);694 const result = getGenericResult(events);695 expect(result.success).to.be.true;696515697 [balanceUniqueTokenMiddle] = await getBalance(api, [randomAccountUnique.address]);516 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccountUnique.address);698 expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true;517 expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true;699518700 const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;519 const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;701 console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', bigIntToDecimals(transactionFees));520 console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(transactionFees));702 expect(transactionFees > 0).to.be.true;521 expect(transactionFees > 0).to.be.true;703 });704522705 await usingApi(523 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {706 async (api) => {707 await waitNewBlocks(api, 3);524 await helper.wait.newBlocks(3);708525709 [balanceGlmrTokenMiddle] = await getBalance(api, [randomAccountMoonbeam.address]);526 balanceGlmrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonbeam.address);710527711 const glmrFees = balanceGlmrTokenInit - balanceGlmrTokenMiddle;528 const glmrFees = balanceGlmrTokenInit - balanceGlmrTokenMiddle;712 console.log('[Unique -> Moonbeam] transaction fees on Moonbeam: %s GLMR', bigIntToDecimals(glmrFees));529 console.log('[Unique -> Moonbeam] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));713 expect(glmrFees == 0n).to.be.true;530 expect(glmrFees == 0n).to.be.true;714531715 const unqRandomAccountAsset = (532 balanceForeignUnqTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonbeam.address))!;716 await api.query.assets.account(assetId, randomAccountMoonbeam.address)717 ).toJSON()! as any;718533719 balanceForeignUnqTokenMiddle = BigInt(unqRandomAccountAsset['balance']);534 const unqIncomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenInit;720 const unqIncomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenInit;721 console.log('[Unique -> Moonbeam] income %s UNQ', bigIntToDecimals(unqIncomeTransfer));535 console.log('[Unique -> Moonbeam] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));722 expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;536 expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;723 },537 });724 moonbeamOptions(),725 );726 });538 });727539728 it('Should connect to Moonbeam and send UNQ back', async () => {540 itSub('Should connect to Moonbeam and send UNQ back', async ({helper}) => {729 await usingApi(541 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {730 async (api) => {731 const asset = {542 const asset = {732 V1: {543 V1: {733 id: {544 id: {734 Concrete: {545 Concrete: {735 parents: 1,546 parents: 1,736 interior: {547 interior: {737 X1: {Parachain: UNIQUE_CHAIN},548 X1: {Parachain: UNIQUE_CHAIN},738 },739 },549 },740 },550 },741 fun: {742 Fungible: TRANSFER_AMOUNT,743 },744 },551 },745 };552 fun: {746 const destination = {747 V1: {748 parents: 1,553 Fungible: TRANSFER_AMOUNT,749 interior: {750 X2: [751 {Parachain: UNIQUE_CHAIN},752 {AccountId32: {network: 'Any', id: randomAccountUnique.addressRaw}},753 ],754 },755 },554 },756 };555 },556 };757 const destWeight = 50000000;557 const destination = {558 V1: {559 parents: 1,560 interior: {561 X2: [562 {Parachain: UNIQUE_CHAIN},563 {AccountId32: {network: 'Any', id: randomAccountUnique.addressRaw}},564 ],565 },566 },567 };568 const destWeight = 50000000;758569759 const tx = api.tx.xTokens.transferMultiasset(asset, destination, destWeight);570 await helper.xTokens.transferMultiasset(randomAccountMoonbeam, asset, destination, destWeight);760 const events = await submitTransactionAsync(randomAccountMoonbeam, tx);761 const result = getGenericResult(events);762 expect(result.success).to.be.true;763571764 [balanceGlmrTokenFinal] = await getBalance(api, [randomAccountMoonbeam.address]);572 balanceGlmrTokenFinal = await helper.balance.getEthereum(randomAccountMoonbeam.address);765573766 const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal;574 const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal;767 console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', bigIntToDecimals(glmrFees));575 console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));768 expect(glmrFees > 0).to.be.true;576 expect(glmrFees > 0).to.be.true;769577770 const unqRandomAccountAsset = (578 const unqRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonbeam.address);771 await api.query.assets.account(assetId, randomAccountMoonbeam.address)772 ).toJSON()! as any;773579774 expect(unqRandomAccountAsset).to.be.null;580 expect(unqRandomAccountAsset).to.be.null;581 582 balanceForeignUnqTokenFinal = 0n;775583776 balanceForeignUnqTokenFinal = 0n;584 const unqOutcomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenFinal;585 console.log('[Unique -> Moonbeam] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));586 expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;587 });777588778 const unqOutcomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenFinal;589 await helper.wait.newBlocks(3);779 console.log('[Unique -> Moonbeam] outcome %s UNQ', bigIntToDecimals(unqOutcomeTransfer));780 expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;781 },782 moonbeamOptions(),783 );784590785 await usingApi(async (api) => {591 balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccountUnique.address);786 await waitNewBlocks(api, 3);592 const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;593 expect(actuallyDelivered > 0).to.be.true;787594788 [balanceUniqueTokenFinal] = await getBalance(api, [randomAccountUnique.address]);595 console.log('[Moonbeam -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));789 const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;790 expect(actuallyDelivered > 0).to.be.true;791596792 console.log('[Moonbeam -> Unique] actually delivered %s UNQ', bigIntToDecimals(actuallyDelivered));597 const unqFees = TRANSFER_AMOUNT - actuallyDelivered;793794 const unqFees = TRANSFER_AMOUNT - actuallyDelivered;795 console.log('[Moonbeam -> Unique] transaction fees on Unique: %s UNQ', bigIntToDecimals(unqFees));598 console.log('[Moonbeam -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));796 expect(unqFees == 0n).to.be.true;599 expect(unqFees == 0n).to.be.true;797 });798 });600 });799});601});800602