difftreelog
Merge branch 'develop' into tests/feed-alices
in: master
47 files changed
.docker/additional/xcm-rococo/.envdiffbeforeafterbothno changes
.docker/additional/xcm-rococo/Dockerfile-xcm-opal-rococodiffbeforeafterbothno changes
.docker/additional/xcm-rococo/Dockerfile-xcm-quartz-rococodiffbeforeafterbothno changes
.docker/additional/xcm-rococo/Dockerfile-xcm-unique-rococodiffbeforeafterbothno changes
.docker/additional/xcm-rococo/docker-compose-xcm-opal-rococo.ymldiffbeforeafterbothno changes
.docker/additional/xcm-rococo/docker-compose-xcm-quartz-rococo.ymldiffbeforeafterbothno changes
.docker/additional/xcm-rococo/docker-compose-xcm-unique-rococo.ymldiffbeforeafterbothno changes
.docker/xcm-config/launch-config-xcm-opal-rococo.jsondiffbeforeafterbothno changes
.docker/xcm-config/launch-config-xcm-quartz-rococo.jsondiffbeforeafterbothno changes
.docker/xcm-config/launch-config-xcm-unique-rococo.jsondiffbeforeafterbothno syntactic changes
.envdiffbeforeafterboth1RUST_TOOLCHAIN=nightly-2022-07-241RUST_TOOLCHAIN=nightly-2022-07-242POLKADOT_BUILD_BRANCH=release-v0.9.272POLKADOT_BUILD_BRANCH=release-v0.9.29334POLKADOT_MAINNET_BRANCH=release-v0.9.264POLKADOT_MAINNET_BRANCH=release-v0.9.265UNIQUE_MAINNET_TAG=v9240105UNIQUE_MAINNET_TAG=v92401013QUARTZ_REPLICA_FROM=wss://eu-ws-quartz.unique.network:44313QUARTZ_REPLICA_FROM=wss://eu-ws-quartz.unique.network:44314UNIQUE_REPLICA_FROM=wss://eu-ws.unique.network:44314UNIQUE_REPLICA_FROM=wss://eu-ws.unique.network:443151516POLKADOT_LAUNCH_BRANCH=feature/rewrite-chain-id-in-spec16POLKADOT_LAUNCH_BRANCH=unique-network171718KARURA_BUILD_BRANCH=2.9.118KARURA_BUILD_BRANCH=2.9.119ACALA_BUILD_BRANCH=2.9.219ACALA_BUILD_BRANCH=2.9.2.github/workflows/testnet-build.ymldiffbeforeafterboth125 run: docker pull uniquenetwork/builder-polkadot:${{ env.POLKADOT_BUILD_BRANCH }} 125 run: docker pull uniquenetwork/builder-polkadot:${{ env.POLKADOT_BUILD_BRANCH }} 126126127 - name: Build the stack127 - name: Build the stack128 run: cd .docker/ && docker build --file ./Dockerfile-testnet.${{ matrix.network }}.yml --tag uniquenetwork/${{ matrix.network }}-testnet-local:nightly-${{ steps.branchname.outputs.value }}-${{ github.sha }} --tag uniquenetwork/${{ matrix.network }}-testnet-local:latest . 128 run: cd .docker/ && docker build --file ./Dockerfile-testnet.${{ matrix.network }}.yml --tag uniquenetwork/${{ matrix.network }}-testnet-local-nightly:nightly-${{ steps.branchname.outputs.value }}-${{ github.sha }} . 129129130 - name: Push docker version image130 - name: Push docker version image131 run: docker push uniquenetwork/${{ matrix.network }}-testnet-local:nightly-${{ steps.branchname.outputs.value }}-${{ github.sha }}131 run: docker push uniquenetwork/${{ matrix.network }}-testnet-local-nightly:nightly-${{ steps.branchname.outputs.value }}-${{ github.sha }}132133 - name: Push docker latest image134 run: docker push uniquenetwork/${{ matrix.network }}-testnet-local:latest135132136 - name: Clean Workspace133 - name: Clean Workspace137 if: always()134 if: always()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.seqtest.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.seqtest.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.seqtest.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.seqtest.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.seqtest.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.seqtest.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.seqtest.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.seqtest.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.seqtest.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, 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 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.seqtest.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.seqtest.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.seqtest.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.seqtest.tsdiffbeforeafterboth7 rejectNft,7 rejectNft,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: 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.seqtest.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 {4import {6 acceptResourceRemoval, addNftBasicResource, createCollection, mintNft, removeNftResource, sendNft,5 acceptResourceRemoval, addNftBasicResource, createCollection, mintNft, removeNftResource, sendNft,7} from './util/tx';6} from './util/tx';337 await expectTxFailure(/rmrkCore\.NoPermission/, tx);336 await expectTxFailure(/rmrkCore\.NoPermission/, tx);338 });337 });339338340 after(() => {339 after(async() => { await api.disconnect(); });341 api.disconnect();342 });343});340});tests/src/rmrk/rmrkIsolation.seqtest.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';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 itRmrk = (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 = await privateKey('//Alice');67 await requirePallets(this, [Pallets.RmrkCore]);68 });65 });69 });66 });706771 itRmrk('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 itRmrk = (await isUnique() ? it : it.skip);87 before(async function() {88 await usingPlaygrounds(async (helper, privateKey) => {89 alice = await privateKey('//Alice');90 bob = await privateKey('//Bob');91 normalizedAlice = {Substrate: helper.address.normalizeSubstrateToChainFormat(alice.address)};929293 before(async () => {93 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 itRmrk('[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 itRmrk('[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 itRmrk = (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 = await privateKey('//Alice');233 bob = privateKeyWrapper('//Bob');206 bob = await 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 itRmrk('[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.seqtest.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.seqtest.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.seqtest.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.seqtest.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.seqtest.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', () => {53 await expectTxFailure(/rmrkCore\.NoAvailableNftId/, tx);52 await expectTxFailure(/rmrkCore\.NoAvailableNftId/, tx);54 });53 });555456 after(() => { api.disconnect(); });55 after(async() => { await api.disconnect(); });57});56});5857tests/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/index.tsdiffbeforeafterboth8import chaiAsPromised from 'chai-as-promised';8import chaiAsPromised from 'chai-as-promised';9import {Context} from 'mocha';9import {Context} from 'mocha';10import config from '../config';10import config from '../config';11import '../interfaces/augment-api-events';11import {ChainHelperBase} from './playgrounds/unique';12import {ILogger} from './playgrounds/types';12import {DevUniqueHelper, SilentLogger, SilentConsole} from './playgrounds/unique.dev';13import {DevUniqueHelper, SilentLogger, SilentConsole, DevMoonbeamHelper, DevMoonriverHelper, DevAcalaHelper, DevKaruraHelper, DevRelayHelper, DevWestmintHelper} from './playgrounds/unique.dev';131414chai.use(chaiAsPromised);15chai.use(chaiAsPromised);15export const expect = chai.expect;16export const expect = chai.expect;22 return `//Alice+${getTestHash(filename)}`;23 return `//Alice+${getTestHash(filename)}`;23};24};242525export const usingPlaygrounds = async (code: (helper: DevUniqueHelper, privateKey: (seed: string | {filename: string, ignoreFundsPresence?: boolean}) => Promise<IKeyringPair>) => Promise<void>, url: string = config.substrateUrl) => {26async function usingPlaygroundsGeneral<T extends ChainHelperBase>(helperType: new(logger: ILogger) => T, url: string, code: (helper: T, privateKey: (seed: string | {filename: string, ignoreFundsPresence?: boolean}) => Promise<IKeyringPair>) => Promise<void>) {26 const silentConsole = new SilentConsole();27 const silentConsole = new SilentConsole();27 silentConsole.enable();28 silentConsole.enable();282929 const helper = new DevUniqueHelper(new SilentLogger());30 const helper = new helperType(new SilentLogger());303131 try {32 try {32 await helper.connect(url);33 await helper.connect(url);38 else {39 else {39 const actualSeed = getTestSeed(seed.filename);40 const actualSeed = getTestSeed(seed.filename);40 let account = helper.util.fromSeed(actualSeed, ss58Format);41 let account = helper.util.fromSeed(actualSeed, ss58Format);42 // here's to hoping that no 41 if (!seed.ignoreFundsPresence && await helper.balance.getSubstrate(account.address) < MINIMUM_DONOR_FUND) {43 if (!seed.ignoreFundsPresence && ((helper as any)['balance'] == undefined || await (helper as any).balance.getSubstrate(account.address) < MINIMUM_DONOR_FUND)) {42 console.warn(`${path.basename(seed.filename)}: Not enough funds present on the filename account. Using the default one as the donor instead.`);44 console.warn(`${path.basename(seed.filename)}: Not enough funds present on the filename account. Using the default one as the donor instead.`);43 account = helper.util.fromSeed('//Alice', ss58Format);45 account = helper.util.fromSeed('//Alice', ss58Format);44 }46 }51 await helper.disconnect();53 await helper.disconnect();52 silentConsole.disable();54 silentConsole.disable();53 }55 }54};56}5758export const usingPlaygrounds = (code: (helper: DevUniqueHelper, privateKey: (seed: string | {filename: string, ignoreFundsPresence?: boolean}) => Promise<IKeyringPair>) => Promise<void>, url: string = config.substrateUrl) => {59 return usingPlaygroundsGeneral<DevUniqueHelper>(DevUniqueHelper, url, code);60};6162export const usingWestmintPlaygrounds = async (url: string, code: (helper: DevWestmintHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => {63 return usingPlaygroundsGeneral<DevWestmintHelper>(DevWestmintHelper, url, code);64};6566export const usingRelayPlaygrounds = async (url: string, code: (helper: DevRelayHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => {67 return usingPlaygroundsGeneral<DevRelayHelper>(DevRelayHelper, url, code);68};6970export const usingAcalaPlaygrounds = async (url: string, code: (helper: DevAcalaHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => {71 return usingPlaygroundsGeneral<DevAcalaHelper>(DevAcalaHelper, url, code);72};7374export const usingKaruraPlaygrounds = async (url: string, code: (helper: DevKaruraHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => {75 return usingPlaygroundsGeneral<DevKaruraHelper>(DevAcalaHelper, url, code);76};7778export const usingMoonbeamPlaygrounds = async (url: string, code: (helper: DevMoonbeamHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => {79 return usingPlaygroundsGeneral<DevMoonbeamHelper>(DevMoonbeamHelper, url, code);80};8182export const usingMoonriverPlaygrounds = async (url: string, code: (helper: DevMoonbeamHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => {83 return usingPlaygroundsGeneral<DevMoonriverHelper>(DevMoonriverHelper, url, code);84};558556export const MINIMUM_DONOR_FUND = 100_000n;86export const MINIMUM_DONOR_FUND = 100_000n;57export const DONOR_FUNDING = 1_000_000n;87export const DONOR_FUNDING = 1_000_000n;99itSubIfWithPallet.skip = (name: string, required: string[], cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any) => itSubIfWithPallet(name, required, cb, {skip: true});129itSubIfWithPallet.skip = (name: string, required: string[], cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any) => itSubIfWithPallet(name, required, cb, {skip: true});100itSub.ifWithPallets = itSubIfWithPallet;130itSub.ifWithPallets = itSubIfWithPallet;131132export async function describeXCM(title: string, fn: (this: Mocha.Suite) => void, opts: {skip?: boolean} = {}) {133 (process.env.RUN_XCM_TESTS && !opts.skip134 ? describe135 : describe.skip)(title, fn);136}137138describeXCM.skip = (name: string, fn: (this: Mocha.Suite) => void) => describeXCM(name, fn, {skip: true});101139tests/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.tsdiffbeforeafterboth334import {stringToU8a} from '@polkadot/util';4import {stringToU8a} from '@polkadot/util';5import {encodeAddress, mnemonicGenerate} from '@polkadot/util-crypto';5import {encodeAddress, mnemonicGenerate} from '@polkadot/util-crypto';6import {UniqueHelper} from './unique';6import {UniqueHelper, MoonbeamHelper, ChainHelperBase, AcalaHelper, RelayHelper, WestmintHelper} from './unique';7import {ApiPromise, WsProvider} from '@polkadot/api';7import {ApiPromise, Keyring, WsProvider} from '@polkadot/api';8import * as defs from '../../interfaces/definitions';8import * as defs from '../../interfaces/definitions';9import {IKeyringPair} from '@polkadot/types/types';9import {IKeyringPair} from '@polkadot/types/types';10import {EventRecord} from '@polkadot/types/interfaces';10import {ICrossAccountId} from './types';11import {ICrossAccountId} from './types';12import {FrameSystemEventRecord} from '@polkadot/types/lookup';111312export class SilentLogger {14export class SilentLogger {13 log(_msg: any, _level: any): void { }15 log(_msg: any, _level: any): void { }109 }110 }110}111}112113export class DevRelayHelper extends RelayHelper {}114115export class DevWestmintHelper extends WestmintHelper {116 wait: WaitGroup;117118 constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {119 options.helperBase = options.helperBase ?? DevWestmintHelper;120121 super(logger, options);122 this.wait = new WaitGroup(this);123 }124}125126export class DevMoonbeamHelper extends MoonbeamHelper {127 account: MoonbeamAccountGroup;128 wait: WaitGroup;129130 constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {131 options.helperBase = options.helperBase ?? DevMoonbeamHelper;132133 super(logger, options);134 this.account = new MoonbeamAccountGroup(this);135 this.wait = new WaitGroup(this);136 }137}138139export class DevMoonriverHelper extends DevMoonbeamHelper {}140141export class DevAcalaHelper extends AcalaHelper {142 wait: WaitGroup;143144 constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {145 options.helperBase = options.helperBase ?? DevAcalaHelper;146147 super(logger, options);148 this.wait = new WaitGroup(this);149 }150}151152export class DevKaruraHelper extends DevAcalaHelper {}111153112class ArrangeGroup {154class ArrangeGroup {113 helper: DevUniqueHelper;155 helper: DevUniqueHelper;252 }294 }253}295}296297class MoonbeamAccountGroup {298 helper: MoonbeamHelper;299300 keyring: Keyring;301 _alithAccount: IKeyringPair;302 _baltatharAccount: IKeyringPair;303 _dorothyAccount: IKeyringPair;304305 constructor(helper: MoonbeamHelper) {306 this.helper = helper;307308 this.keyring = new Keyring({type: 'ethereum'});309 const alithPrivateKey = '0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133';310 const baltatharPrivateKey = '0x8075991ce870b93a8870eca0c0f91913d12f47948ca0fd25b49c6fa7cdbeee8b';311 const dorothyPrivateKey = '0x39539ab1876910bbf3a223d84a29e28f1cb4e2e456503e7e91ed39b2e7223d68';312313 this._alithAccount = this.keyring.addFromUri(alithPrivateKey, undefined, 'ethereum');314 this._baltatharAccount = this.keyring.addFromUri(baltatharPrivateKey, undefined, 'ethereum');315 this._dorothyAccount = this.keyring.addFromUri(dorothyPrivateKey, undefined, 'ethereum');316 }317318 alithAccount() {319 return this._alithAccount;320 }321322 baltatharAccount() {323 return this._baltatharAccount;324 }325326 dorothyAccount() {327 return this._dorothyAccount;328 }329330 create() {331 return this.keyring.addFromUri(mnemonicGenerate());332 }333}254334255class WaitGroup {335class WaitGroup {256 helper: DevUniqueHelper;336 helper: ChainHelperBase;257337258 constructor(helper: DevUniqueHelper) {338 constructor(helper: ChainHelperBase) {259 this.helper = helper;339 this.helper = helper;260 }340 }261341304 });384 });305 }385 }386387 async event(maxBlocksToWait: number, eventSection: string, eventMethod: string) {388 // eslint-disable-next-line no-async-promise-executor389 const promise = new Promise<EventRecord | null>(async (resolve) => {390 const unsubscribe = await this.helper.getApi().rpc.chain.subscribeNewHeads(async header => {391 const blockNumber = header.number.toHuman();392 const blockHash = header.hash;393 const eventIdStr = `${eventSection}.${eventMethod}`;394 const waitLimitStr = `wait blocks remaining: ${maxBlocksToWait}`;395 396 this.helper.logger.log(`[Block #${blockNumber}] Waiting for event \`${eventIdStr}\` (${waitLimitStr})`);397 398 const apiAt = await this.helper.getApi().at(blockHash);399 const eventRecords = (await apiAt.query.system.events()) as any;400 401 const neededEvent = eventRecords.toArray().find((r: FrameSystemEventRecord) => {402 return r.event.section == eventSection && r.event.method == eventMethod;403 });404 405 if (neededEvent) {406 unsubscribe();407 resolve(neededEvent);408 } else if (maxBlocksToWait > 0) {409 maxBlocksToWait--;410 } else {411 this.helper.logger.log(`Event \`${eventIdStr}\` is NOT found`);412 413 unsubscribe();414 resolve(null);415 }416 });417 });418 return promise;419 }306}420}307421308class AdminGroup {422class 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, describeXCM, getGenericResult, paraSiblingSovereignAccount, normalizeAccountId} from './../deprecated-helpers/helpers';19import {itSub, expect, describeXCM, usingPlaygrounds, usingWestmintPlaygrounds, usingRelayPlaygrounds} from '../util';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;46356958705971 before(async () => {60 before(async () => {72 console.log('hey babe its opal');61 await usingPlaygrounds(async (_helper, privateKey) => {73 await usingApi(async (api, privateKeyWrapper) => {74 alice = privateKeyWrapper('//Alice');62 alice = await privateKey('//Alice');75 bob = privateKeyWrapper('//Bob'); // funds donor63 bob = await privateKey('//Bob'); // funds donor76 });64 });776578 const statemineApiOptions: ApiOptions = {66 await usingWestmintPlaygrounds(westmintUrl, async (helper) => {79 provider: new WsProvider('ws://127.0.0.1:' + STATEMINE_PORT),80 };8182 const uniqueApiOptions: ApiOptions = {83 provider: new WsProvider('ws://127.0.0.1:' + UNIQUE_PORT),84 };8586 const relayApiOptions: ApiOptions = {87 provider: new WsProvider('ws://127.0.0.1:' + RELAY_PORT),88 };8990 await usingApi(async (api) => {9192 // 350.00 (three hundred fifty) DOT67 // 350.00 (three hundred fifty) DOT93 const fundingAmount = 3_500_000_000_000; 68 const fundingAmount = 3_500_000_000_000n; 946995 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);96 const events = await executeTransaction(api, alice, tx);71 await helper.assets.setMetadata(alice, ASSET_ID, ASSET_METADATA_NAME, ASSET_METADATA_DESCRIPTION, ASSET_METADATA_DECIMALS);97 const result = getGenericResult(events);72 await helper.assets.mint(alice, ASSET_ID, alice.address, ASSET_AMOUNT);98 expect(result.success).to.be.true;9973100 // set metadata101 const tx2 = api.tx.assets.setMetadata(ASSET_ID, ASSET_METADATA_NAME, ASSET_METADATA_DESCRIPTION, ASSET_METADATA_DECIMALS);102 const events2 = await executeTransaction(api, alice, tx2);103 const result2 = getGenericResult(events2);104 expect(result2.success).to.be.true;105106 // mint some amount of asset107 const tx3 = api.tx.assets.mint(ASSET_ID, alice.addressRaw, ASSET_AMOUNT);108 const events3 = await executeTransaction(api, alice, tx3);109 const result3 = getGenericResult(events3);110 expect(result3.success).to.be.true;111112 // funding parachain sovereing account (Parachain: 2095)74 // funding parachain sovereing account (Parachain: 2095)113 const parachainSovereingAccount = await paraSiblingSovereignAccount(UNIQUE_CHAIN);75 const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(UNIQUE_CHAIN);114 const tx4 = api.tx.balances.transfer(parachainSovereingAccount, fundingAmount);76 await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, fundingAmount);115 const events4 = await executeTransaction(api, bob, tx4);77 });116 const result4 = getGenericResult(events4);117 expect(result4.success).to.be.true;11878119 }, statemineApiOptions);12079121122 await usingApi(async (api) => {80 await usingPlaygrounds(async (helper) => {123124 const location = {81 const location = {125 V1: {82 V1: {145 decimals: ASSET_METADATA_DECIMALS,102 decimals: ASSET_METADATA_DECIMALS,146 minimalBalance: ASSET_METADATA_MINIMAL_BALANCE,103 minimalBalance: ASSET_METADATA_MINIMAL_BALANCE,147 };104 };148 //registerForeignAsset(owner, location, metadata)105 await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata);149 const tx = api.tx.foreignAssets.registerForeignAsset(alice.addressRaw, location, metadata);150 const sudoTx = api.tx.sudo.sudo(tx as any);106 balanceOpalBefore = await helper.balance.getSubstrate(alice.address);151 const events = await executeTransaction(api, alice, sudoTx);152 const result = getGenericResult(events);107 });153 expect(result.success).to.be.true;154108155 [balanceOpalBefore] = await getBalance(api, [alice.address]);156109157 }, uniqueApiOptions);158159160 // Providing the relay currency to the unique sender account110 // Providing the relay currency to the unique sender account161 await usingApi(async (api) => {111 await usingRelayPlaygrounds(relayUrl, async (helper) => {162 const destination = {112 const destination = {163 V1: {113 V1: {164 parents: 0,114 parents: 0,197 };147 };198148199 const feeAssetItem = 0;149 const feeAssetItem = 0;150 const weightLimit = 5_000_000_000;200151201 const weightLimit = {152 await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, weightLimit);202 Limited: 5_000_000_000,203 };204205 const tx = api.tx.xcmPallet.limitedReserveTransferAssets(destination, beneficiary, assets, feeAssetItem, weightLimit);206 const events = await executeTransaction(api, alice, tx);153 });207 const result = getGenericResult(events);208 expect(result.success).to.be.true;209 }, relayApiOptions);210 154 211 });155 });212156213 it('Should connect and send USDT from Westmint to Opal', async () => {157 itSub('Should connect and send USDT from Westmint to Opal', async ({helper}) => {214 158 await usingWestmintPlaygrounds(westmintUrl, async (helper) => {215 const statemineApiOptions: ApiOptions = {216 provider: new WsProvider('ws://127.0.0.1:' + STATEMINE_PORT),217 };218219 const uniqueApiOptions: ApiOptions = {220 provider: new WsProvider('ws://127.0.0.1:' + UNIQUE_PORT),221 };222223 await usingApi(async (api) => {224225 const dest = {159 const dest = {226 V1: {160 V1: {268 };202 };269203270 const feeAssetItem = 0;204 const feeAssetItem = 0;205 const weightLimit = 5000000000;271206272 const weightLimit = {207 balanceStmnBefore = await helper.balance.getSubstrate(alice.address);273 Limited: 5000000000,208 await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, weightLimit);274 };275209276 [balanceStmnBefore] = await getBalance(api, [alice.address]);210 balanceStmnAfter = await helper.balance.getSubstrate(alice.address);277211278 const tx = api.tx.polkadotXcm.limitedReserveTransferAssets(dest, beneficiary, assets, feeAssetItem, weightLimit);279 const events = await executeTransaction(api, alice, tx);280 const result = getGenericResult(events);281 expect(result.success).to.be.true;282283 [balanceStmnAfter] = await getBalance(api, [alice.address]);284285 // common good parachain take commission in it native token212 // common good parachain take commission in it native token286 console.log(213 console.log(287 'Opal to Westmint transaction fees on Westmint: %s WND',214 'Opal to Westmint transaction fees on Westmint: %s WND',288 bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, WESTMINT_DECIMALS),215 helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, WESTMINT_DECIMALS),289 );216 );290 expect(balanceStmnBefore > balanceStmnAfter).to.be.true;217 expect(balanceStmnBefore > balanceStmnAfter).to.be.true;291218292 }, statemineApiOptions);219 });293220294221295 // ensure that asset has been delivered222 // ensure that asset has been delivered296 await usingApi(async (api) => {223 await helper.wait.newBlocks(3);297 await waitNewBlocks(api, 3);298 // expext collection id will be with id 1299 const free = (await api.query.fungible.balance(1, normalizeAccountId(alice.address))).toBigInt();300224301 [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});302227303 // commission has not paid in USDT token228 balanceOpalAfter = await helper.balance.getSubstrate(alice.address);304 expect(free == TRANSFER_AMOUNT).to.be.true;305 console.log(306 'Opal to Westmint transaction fees on Opal: %s USDT',307 bigIntToDecimals(TRANSFER_AMOUNT - free),308 );309 // ... and parachain native token310 expect(balanceOpalAfter == balanceOpalBefore).to.be.true;311 console.log(312 'Opal to Westmint transaction fees on Opal: %s WND',313 bigIntToDecimals(balanceOpalAfter - balanceOpalBefore, WESTMINT_DECIMALS),314 );315229316 }, 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),317 241 ); 318 });242 });319243320 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}) => {321322 const uniqueApiOptions: ApiOptions = {323 provider: new WsProvider('ws://127.0.0.1:' + UNIQUE_PORT),324 };325326 const statemineApiOptions: ApiOptions = {327 provider: new WsProvider('ws://127.0.0.1:' + STATEMINE_PORT),328 };329330 await usingApi(async (api) => {331 const destination = {245 const destination = {332 V1: {246 V1: {333 parents: 1,247 parents: 1,334 interior: {X2: [248 interior: {X2: [335 {336 Parachain: STATEMINE_CHAIN,337 },338 {339 AccountId32: {340 network: 'Any',341 id: alice.addressRaw,342 },343 },344 ]},345 },346 };347348 const currencies: [any, bigint][] = [349 [350 {249 {351 ForeignAssetId: 0,250 Parachain: STATEMINE_CHAIN,352 },251 },353 //10_000_000_000_000_000n,354 TRANSFER_AMOUNT,355 ], 356 [357 {252 {358 NativeAssetId: 'Parent',253 AccountId32: {254 network: 'Any',255 id: alice.addressRaw,256 },359 },257 },360 400_000_000_000_000n,258 ]},361 ],259 },362 ];260 };363261364 const feeItem = 1;262 const currencies: [any, bigint][] = [365 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 ];366277367 const tx = api.tx.xTokens.transferMulticurrencies(currencies, feeItem, destination, destWeight);278 const feeItem = 1;368 const events = await executeTransaction(api, alice, tx);369 const result = getGenericResult(events);279 const destWeight = 500000000000;370 expect(result.success).to.be.true;371 372 // the commission has been paid in parachain native token373 [balanceOpalFinal] = await getBalance(api, [alice.address]);374 expect(balanceOpalAfter > balanceOpalFinal).to.be.true;375 }, uniqueApiOptions);376280377 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) => {378 await waitNewBlocks(api, 3);288 await helper.wait.newBlocks(3);379 289 380 // 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.381 // Also check that xcm transfer has been succeeded 291 // Also check that xcm transfer has been succeeded 382 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;383 expect(BigInt(free.balance.replace(/,/g, '')) == ASSET_AMOUNT).to.be.true;384 }, statemineApiOptions);293 });385 });294 });386295387 it('Should connect and send Relay token to Unique', async () => {296 itSub('Should connect and send Relay token to Unique', async ({helper}) => {388389 const uniqueApiOptions: ApiOptions = {390 provider: new WsProvider('ws://127.0.0.1:' + UNIQUE_PORT),391 };392393 const uniqueApiOptions2: ApiOptions = {394 provider: new WsProvider('ws://127.0.0.1:' + UNIQUE_PORT),395 };396397 const relayApiOptions: ApiOptions = {398 provider: new WsProvider('ws://127.0.0.1:' + RELAY_PORT),399 };400401 const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n;297 const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n;402298403 await usingApi(async (api) => {299 balanceBobBefore = await helper.balance.getSubstrate(bob.address);404 [balanceBobBefore] = await getBalance(api, [bob.address]);405 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'});406301407 }, uniqueApiOptions);408409 // Providing the relay currency to the unique sender account302 // Providing the relay currency to the unique sender account410 await usingApi(async (api) => {303 await usingRelayPlaygrounds(relayUrl, async (helper) => {411 const destination = {304 const destination = {412 V1: {305 V1: {413 parents: 0,306 parents: 0,446 };339 };447340448 const feeAssetItem = 0;341 const feeAssetItem = 0;342 const weightLimit = 5_000_000_000;449343450 const weightLimit = {344 await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, weightLimit);451 Limited: 5_000_000_000,452 };453454 const tx = api.tx.xcmPallet.limitedReserveTransferAssets(destination, beneficiary, assets, feeAssetItem, weightLimit);455 const events = await executeTransaction(api, bob, tx);345 });456 const result = getGenericResult(events);457 expect(result.success).to.be.true;458 }, relayApiOptions);459 346 347 await helper.wait.newBlocks(3);460348461 await usingApi(async (api) => {349 balanceBobAfter = await helper.balance.getSubstrate(bob.address); 462 await waitNewBlocks(api, 3);350 balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});463351464 [balanceBobAfter] = await getBalance(api, [bob.address]);352 const wndFee = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore; 465 balanceBobRelayTokenAfter = BigInt(((await api.query.tokens.accounts(bob.addressRaw, {NativeAssetId: 'Parent'})).toJSON() as any).free);466 const wndFee = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore; 467 console.log(353 console.log(468 'Relay (Westend) to Opal transaction fees: %s OPL',354 'Relay (Westend) to Opal transaction fees: %s OPL',469 bigIntToDecimals(balanceBobAfter - balanceBobBefore),355 helper.util.bigIntToDecimals(balanceBobAfter - balanceBobBefore),470 );356 );471 console.log(357 console.log(472 'Relay (Westend) to Opal transaction fees: %s WND',358 'Relay (Westend) to Opal transaction fees: %s WND',473 bigIntToDecimals(wndFee, WESTMINT_DECIMALS),359 helper.util.bigIntToDecimals(wndFee, WESTMINT_DECIMALS),474 );360 );475 expect(balanceBobBefore == balanceBobAfter).to.be.true;361 expect(balanceBobBefore == balanceBobAfter).to.be.true;476 expect(balanceBobRelayTokenBefore < balanceBobRelayTokenAfter).to.be.true;362 expect(balanceBobRelayTokenBefore < balanceBobRelayTokenAfter).to.be.true;477 }, uniqueApiOptions2);478479 });363 });480364481 it('Should connect and send Relay token back', async () => {365 itSub('Should connect and send Relay token back', async ({helper}) => {482 const uniqueApiOptions: ApiOptions = {366 const destination = {483 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 },484 };381 };485382486 await usingApi(async (api) => {383 const currencies: any = [487 const destination = {488 V1: {489 parents: 1,490 interior: {X2: [384 [491 {385 {492 Parachain: STATEMINE_CHAIN,386 NativeAssetId: 'Parent',493 },494 {495 AccountId32: {496 network: 'Any',497 id: bob.addressRaw,498 },499 },500 ]},501 },387 },502 };388 50_000_000_000_000_000n,389 ],390 ];503391504 const currencies: any = [392 const feeItem = 0;505 [393 const destWeight = 500000000000;506 {507 NativeAssetId: 'Parent',508 },509 50_000_000_000_000_000n,510 ],511 ];512394513 const feeItem = 0;395 await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, destWeight);514 const destWeight = 500000000000;515396516 const tx = api.tx.xTokens.transferMulticurrencies(currencies, feeItem, destination, destWeight);397 balanceBobFinal = await helper.balance.getSubstrate(bob.address);517 const events = await executeTransaction(api, bob, tx);518 const result = getGenericResult(events);519 expect(result.success).to.be.true;520521 [balanceBobFinal] = await getBalance(api, [bob.address]);522 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);523524 }, uniqueApiOptions);525 });399 });526527});400});528401tests/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, describeXCM, 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';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}6364describeXCM('[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;76 let balanceQuartzForeignTokenFinal: bigint;47 let balanceQuartzForeignTokenFinal: bigint;774878 before(async () => {49 before(async () => {79 console.log('hey babe');50 await usingPlaygrounds(async (helper, privateKey) => {80 await usingApi(async (api, privateKeyWrapper) => {81 const keyringSr25519 = new Keyring({type: 'sr25519'});51 alice = await privateKey('//Alice');8283 alice = privateKeyWrapper('//Alice');84 randomAccount = generateKeyringPair(keyringSr25519);52 [randomAccount] = await helper.arrange.createAccounts([0n], alice);85 });53 });865487 // Karura side55 await usingKaruraPlaygrounds(karuraUrl, async (helper) => {88 await usingApi(89 async (api) => {90 const destination = {91 V0: {92 X2: [93 'Parent',94 {95 Parachain: QUARTZ_CHAIN,96 },97 ],98 },99 };100101 const metadata = {102 name: 'QTZ',103 symbol: 'QTZ',104 decimals: 18,105 minimalBalance: 1,106 };107108 const tx = api.tx.assetRegistry.registerForeignAsset(destination, metadata);109 const sudoTx = api.tx.sudo.sudo(tx as any);110 const events = await submitTransactionAsync(alice, sudoTx);111 const result = getGenericResult(events);112 expect(result.success).to.be.true;113114 const tx1 = api.tx.balances.transfer(randomAccount.address, 10000000000000n);115 const events1 = await submitTransactionAsync(alice, tx1);116 const result1 = getGenericResult(events1);117 expect(result1.success).to.be.true;118119 [balanceKaruraTokenInit] = await getBalance(api, [randomAccount.address]);120 {121 const {free} = (await api.query.tokens.accounts(randomAccount.addressRaw, {ForeignAsset: 0})).toJSON() as any;122 balanceQuartzForeignTokenInit = BigInt(free);123 }124 },125 karuraOptions(),126 );127128 // Quartz side129 await usingApi(async (api) => {130 const tx0 = api.tx.balances.transfer(randomAccount.address, 10n * TRANSFER_AMOUNT);131 const events0 = await submitTransactionAsync(alice, tx0);132 const result0 = getGenericResult(events0);133 expect(result0.success).to.be.true;134135 [balanceQuartzTokenInit] = await getBalance(api, [randomAccount.address]);136 });137 });138139 it('Should connect and send QTZ to Karura', async () => {140141 // Quartz side142 await usingApi(async (api) => {143144 const destination = {56 const destination = {145 V0: {57 V0: {146 X2: [58 X2: [147 'Parent',59 'Parent',148 {60 {149 Parachain: KARURA_CHAIN,61 Parachain: QUARTZ_CHAIN,150 },62 },151 ],63 ],152 },64 },153 };65 };15466155 const beneficiary = {67 const metadata = {156 V0: {68 name: 'QTZ',157 X1: {69 symbol: 'QTZ',158 AccountId32: {159 network: 'Any',160 id: randomAccount.addressRaw,70 decimals: 18,161 },71 minimalBalance: 1n,162 },163 },164 };72 };16573166 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 = {167 V1: [88 V0: {89 X2: [90 'Parent',168 {91 {169 id: {92 Parachain: KARURA_CHAIN,170 Concrete: {171 parents: 0,172 interior: 'Here',173 },174 },175 fun: {176 Fungible: TRANSFER_AMOUNT,177 },178 },93 },179 ],94 ],180 };95 },96 };18197182 const feeAssetItem = 0;98 const beneficiary = {99 V0: {100 X1: {101 AccountId32: {102 network: 'Any',103 id: randomAccount.addressRaw,104 },105 },106 },107 };183108184 const weightLimit = {109 const assets = {185 Limited: 5000000000,110 V1: [111 {112 id: {113 Concrete: {114 parents: 0,186 };115 interior: 'Here',116 },117 },118 fun: {119 Fungible: TRANSFER_AMOUNT,120 },121 },122 ],123 };187124188 const tx = api.tx.polkadotXcm.limitedReserveTransferAssets(destination, beneficiary, assets, feeAssetItem, weightLimit);125 const feeAssetItem = 0;189 const events = await submitTransactionAsync(randomAccount, tx);190 const result = getGenericResult(events);126 const weightLimit = 5000000000;191 expect(result.success).to.be.true;192127193 [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);194130195 const qtzFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;131 const qtzFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;196 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));197 expect(qtzFees > 0n).to.be.true;133 expect(qtzFees > 0n).to.be.true;198 });199134200 // Karura side135 await usingKaruraPlaygrounds(karuraUrl, async (helper) => {201 await usingApi(202 async (api) => {203 await waitNewBlocks(api, 3);136 await helper.wait.newBlocks(3);204 const {free} = (await api.query.tokens.accounts(randomAccount.addressRaw, {ForeignAsset: 0})).toJSON() as any;137 balanceQuartzForeignTokenMiddle = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});205 balanceQuartzForeignTokenMiddle = BigInt(free);138 balanceKaruraTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);206139207 [balanceKaruraTokenMiddle] = await getBalance(api, [randomAccount.address]);140 const karFees = balanceKaruraTokenInit - balanceKaruraTokenMiddle;141 const qtzIncomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenInit;208142209 const karFees = balanceKaruraTokenInit - balanceKaruraTokenMiddle;143 console.log(210 const qtzIncomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenInit;211212 console.log(213 '[Quartz -> Karura] transaction fees on Karura: %s KAR',144 '[Quartz -> Karura] transaction fees on Karura: %s KAR',214 bigIntToDecimals(karFees, KARURA_DECIMALS),145 helper.util.bigIntToDecimals(karFees, KARURA_DECIMALS),215 );146 );216 console.log('[Quartz -> Karura] income %s QTZ', bigIntToDecimals(qtzIncomeTransfer));147 console.log('[Quartz -> Karura] income %s QTZ', helper.util.bigIntToDecimals(qtzIncomeTransfer));217 expect(karFees == 0n).to.be.true;148 expect(karFees == 0n).to.be.true;218 expect(qtzIncomeTransfer == TRANSFER_AMOUNT).to.be.true;149 expect(qtzIncomeTransfer == TRANSFER_AMOUNT).to.be.true;219 },150 });220 karuraOptions(),221 );222 });151 });223152224 it('Should connect to Karura and send QTZ back', async () => {153 itSub('Should connect to Karura and send QTZ back', async ({helper}) => {225226 // Karura side154 await usingKaruraPlaygrounds(karuraUrl, async (helper) => {227 await usingApi(228 async (api) => {229 const destination = {155 const destination = {230 V1: {156 V1: {231 parents: 1,157 parents: 1,232 interior: {158 interior: {233 X2: [159 X2: [234 {Parachain: QUARTZ_CHAIN},160 {Parachain: QUARTZ_CHAIN},235 {161 {236 AccountId32: {162 AccountId32: {237 network: 'Any',163 network: 'Any',238 id: randomAccount.addressRaw,164 id: randomAccount.addressRaw,239 },240 },165 },241 ],166 },242 },167 ],243 },168 },244 };169 },170 };245171246 const id = {172 const id = {247 ForeignAsset: 0,173 ForeignAsset: 0,248 };174 };249175250 const destWeight = 50000000;176 const destWeight = 50000000;251177252 const tx = api.tx.xTokens.transfer(id as any, TRANSFER_AMOUNT, destination, destWeight);178 await helper.xTokens.transfer(randomAccount, id, TRANSFER_AMOUNT, destination, destWeight);253 const events = await submitTransactionAsync(randomAccount, tx);179 balanceKaruraTokenFinal = await helper.balance.getSubstrate(randomAccount.address);254 const result = getGenericResult(events);180 balanceQuartzForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);255 expect(result.success).to.be.true;256181257 [balanceKaruraTokenFinal] = await getBalance(api, [randomAccount.address]);182 const karFees = balanceKaruraTokenMiddle - balanceKaruraTokenFinal;258 {183 const qtzOutcomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenFinal;259 const {free} = (await api.query.tokens.accounts(randomAccount.addressRaw, id)).toJSON() as any;260 balanceQuartzForeignTokenFinal = BigInt(free);261 }262184263 const karFees = balanceKaruraTokenMiddle - balanceKaruraTokenFinal;185 console.log(186 '[Karura -> Quartz] transaction fees on Karura: %s KAR',264 const qtzOutcomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenFinal;187 helper.util.bigIntToDecimals(karFees, KARURA_DECIMALS),188 );189 console.log('[Karura -> Quartz] outcome %s QTZ', helper.util.bigIntToDecimals(qtzOutcomeTransfer));265190266 console.log(191 expect(karFees > 0).to.be.true;267 '[Karura -> Quartz] transaction fees on Karura: %s KAR',268 bigIntToDecimals(karFees, KARURA_DECIMALS),192 expect(qtzOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;269 );270 console.log('[Karura -> Quartz] outcome %s QTZ', bigIntToDecimals(qtzOutcomeTransfer));193 });271194272 expect(karFees > 0).to.be.true;195 await helper.wait.newBlocks(3);273 expect(qtzOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;274 },275 karuraOptions(),276 );277196278 // Quartz side197 balanceQuartzTokenFinal = await helper.balance.getSubstrate(randomAccount.address);279 await usingApi(async (api) => {198 const actuallyDelivered = balanceQuartzTokenFinal - balanceQuartzTokenMiddle;280 await waitNewBlocks(api, 3);199 expect(actuallyDelivered > 0).to.be.true;281200282 [balanceQuartzTokenFinal] = await getBalance(api, [randomAccount.address]);201 console.log('[Karura -> Quartz] actually delivered %s QTZ', helper.util.bigIntToDecimals(actuallyDelivered));283 const actuallyDelivered = balanceQuartzTokenFinal - balanceQuartzTokenMiddle;284 expect(actuallyDelivered > 0).to.be.true;285202286 console.log('[Karura -> Quartz] actually delivered %s QTZ', bigIntToDecimals(actuallyDelivered));203 const qtzFees = TRANSFER_AMOUNT - actuallyDelivered;287288 const qtzFees = TRANSFER_AMOUNT - actuallyDelivered;289 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));290 expect(qtzFees == 0n).to.be.true;205 expect(qtzFees == 0n).to.be.true;291 });292 });206 });293});207});294208297 let alice: IKeyringPair;211 let alice: IKeyringPair;298212299 before(async () => {213 before(async () => {300 await usingApi(async (api, privateKeyWrapper) => {214 await usingPlaygrounds(async (_helper, privateKey) => {301 alice = privateKeyWrapper('//Alice');215 alice = await privateKey('//Alice');302 });216 });303 });217 });304218305 it('Quartz rejects tokens from the Relay', async () => {219 itSub('Quartz rejects tokens from the Relay', async ({helper}) => {306 await usingApi(async (api) => {220 await usingRelayPlaygrounds(relayUrl, async (helper) => {307 const destination = {221 const destination = {308 V1: {222 V1: {309 parents: 0,223 parents: 0,342 };256 };343257344 const feeAssetItem = 0;258 const feeAssetItem = 0;259 const weightLimit = 5_000_000_000;345260346 const weightLimit = {261 await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, weightLimit);347 Limited: 5_000_000_000,348 };262 });349263350 const tx = api.tx.xcmPallet.limitedReserveTransferAssets(destination, beneficiary, assets, feeAssetItem, weightLimit);264 const maxWaitBlocks = 3;351 const events = await submitTransactionAsync(alice, tx);352 const result = getGenericResult(events);353 expect(result.success).to.be.true;354 }, relayOptions());355265356 await usingApi(async api => {266 const dmpQueueExecutedDownward = await helper.wait.event(maxWaitBlocks, 'dmpQueue', 'ExecutedDownward');357 const maxWaitBlocks = 3;358 const dmpQueueExecutedDownward = await waitEvent(359 api,360 maxWaitBlocks,361 'dmpQueue',362 'ExecutedDownward',363 );364267365 expect(268 expect(366 dmpQueueExecutedDownward != null,269 dmpQueueExecutedDownward != null,367 '[Relay] dmpQueue.ExecutedDownward event is expected',270 '[Relay] dmpQueue.ExecutedDownward event is expected',368 ).to.be.true;271 ).to.be.true;369272370 const event = dmpQueueExecutedDownward!.event;273 const event = dmpQueueExecutedDownward!.event;371 const outcome = event.data[1] as XcmV2TraitsOutcome;274 const outcome = event.data[1] as XcmV2TraitsOutcome;372275373 expect(276 expect(374 outcome.isIncomplete,277 outcome.isIncomplete,375 '[Relay] The outcome of the XCM should be `Incomplete`',278 '[Relay] The outcome of the XCM should be `Incomplete`',376 ).to.be.true;279 ).to.be.true;377280378 const incomplete = outcome.asIncomplete;281 const incomplete = outcome.asIncomplete;379 expect(282 expect(380 incomplete[1].toString() == 'AssetNotFound',283 incomplete[1].toString() == 'AssetNotFound',381 '[Relay] The XCM error should be `AssetNotFound`',284 '[Relay] The XCM error should be `AssetNotFound`',382 ).to.be.true;285 ).to.be.true;383 });384 });286 });385287386 it('Quartz rejects KAR tokens from Karura', async () => {288 itSub('Quartz rejects KAR tokens from Karura', async ({helper}) => {387 await usingApi(async (api) => {289 await usingKaruraPlaygrounds(karuraUrl, async (helper) => {388 const destination = {290 const destination = {389 V1: {291 V1: {390 parents: 1,292 parents: 1,408310409 const destWeight = 50000000;311 const destWeight = 50000000;410312411 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);412 const events = await submitTransactionAsync(alice, tx);314 });413 const result = getGenericResult(events);414 expect(result.success).to.be.true;415 }, karuraOptions());416315417 await usingApi(async api => {316 const maxWaitBlocks = 3;418 const maxWaitBlocks = 3;419 const xcmpQueueFailEvent = await waitEvent(api, maxWaitBlocks, 'xcmpQueue', 'Fail');420317421 expect(318 const xcmpQueueFailEvent = await helper.wait.event(maxWaitBlocks, 'xcmpQueue', 'Fail');422 xcmpQueueFailEvent != null,423 '[Karura] xcmpQueue.FailEvent event is expected',424 ).to.be.true;425319426 const event = xcmpQueueFailEvent!.event;320 expect(321 xcmpQueueFailEvent != null,427 const outcome = event.data[1] as XcmV2TraitsError;322 '[Karura] xcmpQueue.FailEvent event is expected',323 ).to.be.true;428324429 expect(325 const event = xcmpQueueFailEvent!.event;326 const outcome = event.data[1] as XcmV2TraitsError;327328 expect(430 outcome.isUntrustedReserveLocation,329 outcome.isUntrustedReserveLocation,431 '[Karura] The XCM error should be `UntrustedReserveLocation`',330 '[Karura] The XCM error should be `UntrustedReserveLocation`',432 ).to.be.true;331 ).to.be.true;433 });434 });332 });435});333});436334437describeXCM('[XCM] Integration test: Exchanging QTZ with Moonriver', () => {335describeXCM('[XCM] Integration test: Exchanging QTZ with Moonriver', () => {438 // Quartz constants336 // Quartz constants439 let quartzAlice: IKeyringPair;337 let quartzDonor: IKeyringPair;440 let quartzAssetLocation;338 let quartzAssetLocation;441339442 let randomAccountQuartz: IKeyringPair;340 let randomAccountQuartz: IKeyringPair;445 // Moonriver constants343 // Moonriver constants446 let assetId: string;344 let assetId: string;447345448 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;346 const councilVotingThreshold = 2;458 const technicalCommitteeThreshold = 2;347 const technicalCommitteeThreshold = 2;459 const votingPeriod = 3;348 const votingPeriod = 3;464 symbol: 'xcQTZ',353 symbol: 'xcQTZ',465 decimals: 18,354 decimals: 18,466 isFrozen: false,355 isFrozen: false,467 minimalBalance: 1,356 minimalBalance: 1n,468 };357 };469358470 let balanceQuartzTokenInit: bigint;359 let balanceQuartzTokenInit: bigint;478 let balanceMovrTokenFinal: bigint;367 let balanceMovrTokenFinal: bigint;479368480 before(async () => {369 before(async () => {481 await usingApi(async (api, privateKeyWrapper) => {370 await usingPlaygrounds(async (helper, privateKey) => {482 const keyringEth = new Keyring({type: 'ethereum'});371 quartzDonor = await privateKey('//Alice');483 const keyringSr25519 = new Keyring({type: 'sr25519'});372 [randomAccountQuartz] = await helper.arrange.createAccounts([0n], quartzDonor);484373485 quartzAlice = privateKeyWrapper('//Alice');486 randomAccountQuartz = generateKeyringPair(keyringSr25519);487 randomAccountMoonriver = generateKeyringPair(keyringEth);488489 balanceForeignQtzTokenInit = 0n;374 balanceForeignQtzTokenInit = 0n;490 });375 });491376492 await usingApi(377 await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {493 async (api) => {378 const alithAccount = helper.account.alithAccount();379 const baltatharAccount = helper.account.baltatharAccount();380 const dorothyAccount = helper.account.dorothyAccount();494381495 // >>> Sponsoring Dorothy >>>382 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 <<<503383504 const sourceLocation: MultiLocation = api.createType(384 // >>> Sponsoring Dorothy >>>505 'MultiLocation',385 console.log('Sponsoring Dorothy.......');506 {386 await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);507 parents: 1,387 console.log('Sponsoring Dorothy.......DONE');508 interior: {X1: {Parachain: QUARTZ_CHAIN}},388 // <<< Sponsoring Dorothy <<<509 },510 );511389512 quartzAssetLocation = {XCM: sourceLocation};390 quartzAssetLocation = {391 XCM: {392 parents: 1,393 interior: {X1: {Parachain: QUARTZ_CHAIN}},513 const existentialDeposit = 1;394 },395 };396 const existentialDeposit = 1n;514 const isSufficient = true;397 const isSufficient = true;515 const unitsPerSecond = '1';398 const unitsPerSecond = 1n;516 const numAssetsWeightHint = 0;399 const numAssetsWeightHint = 0;517400518 const registerTx = api.tx.assetManager.registerForeignAsset(401 const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({519 quartzAssetLocation,402 location: quartzAssetLocation,520 quartzAssetMetadata,403 metadata: quartzAssetMetadata,521 existentialDeposit,404 existentialDeposit,522 isSufficient,405 isSufficient,523 );406 unitsPerSecond,524 console.log('Encoded proposal for registerAsset is %s', registerTx.method.toHex() || '');407 numAssetsWeightHint,408 });409 const proposalHash = blake2AsHex(encodedProposal);525410526 const setUnitsTx = api.tx.assetManager.setAssetUnitsPerSecond(411 console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);527 quartzAssetLocation,528 unitsPerSecond,529 numAssetsWeightHint,412 console.log('Encoded length %d', encodedProposal.length);530 );531 console.log('Encoded proposal for setAssetUnitsPerSecond is %s', setUnitsTx.method.toHex() || '');413 console.log('Encoded proposal hash for batch utility after schedule is %s', proposalHash);532414533 const batchCall = api.tx.utility.batchAll([registerTx, setUnitsTx]);415 // >>> Note motion preimage >>>416 console.log('Note motion preimage.......');417 await helper.democracy.notePreimage(baltatharAccount, encodedProposal);534 console.log('Encoded proposal for batchCall is %s', batchCall.method.toHex() || '');418 console.log('Note motion preimage.......DONE');419 // <<< Note motion preimage <<<535420536 // >>> Note motion preimage >>>421 // >>> Propose external motion through council >>>537 console.log('Note motion preimage.......');422 console.log('Propose external motion through council.......');538 const encodedProposal = batchCall?.method.toHex() || '';423 const externalMotion = helper.democracy.externalProposeMajority(proposalHash);539 const proposalHash = blake2AsHex(encodedProposal);424 const encodedMotion = externalMotion?.method.toHex() || '';540 console.log('Encoded proposal for batch utility after schedule is %s', encodedProposal);425 const motionHash = blake2AsHex(encodedMotion);541 console.log('Encoded proposal hash for batch utility after schedule is %s', proposalHash);426 console.log('Motion hash is %s', motionHash);542 console.log('Encoded length %d', encodedProposal.length);543427544 const tx1 = api.tx.democracy.notePreimage(encodedProposal);428 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 <<<550429551 // >>> Propose external motion through council >>>430 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(431 await helper.collective.council.vote(dorothyAccount, motionHash, councilProposalIdx, true);555 councilVotingThreshold,556 externalMotion,557 externalMotion.encodedLength,558 );559 const events2 = await submitTransactionAsync(baltatharAccount, tx2);432 await helper.collective.council.vote(baltatharAccount, motionHash, councilProposalIdx, true);560 const result2 = getGenericResult(events2);561 expect(result2.success).to.be.true;562433563 const encodedMotion = externalMotion?.method.toHex() || '';434 await helper.collective.council.close(dorothyAccount, motionHash, councilProposalIdx, 1_000_000_000, externalMotion.encodedLength);564 const motionHash = blake2AsHex(encodedMotion);435 console.log('Propose external motion through council.......DONE');565 console.log('Motion hash is %s', motionHash);436 // <<< Propose external motion through council <<<566437567 const tx3 = api.tx.councilCollective.vote(motionHash, 0, true);438 // >>> Fast track proposal through technical committee >>>568 {569 const events3 = await submitTransactionAsync(dorothyAccount, tx3);439 console.log('Fast track proposal through technical committee.......');570 const result3 = getGenericResult(events3);440 const fastTrack = helper.democracy.fastTrack(proposalHash, votingPeriod, delayPeriod);571 expect(result3.success).to.be.true;572 }441 const encodedFastTrack = fastTrack?.method.toHex() || '';573 {574 const events3 = await submitTransactionAsync(baltatharAccount, tx3);575 const result3 = getGenericResult(events3);442 const fastTrackHash = blake2AsHex(encodedFastTrack);576 expect(result3.success).to.be.true;443 console.log('FastTrack hash is %s', fastTrackHash);577 }578444579 const tx4 = api.tx.councilCollective.close(motionHash, 0, 1_000_000_000, externalMotion.encodedLength);445 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 <<<585446586 // >>> Fast track proposal through technical committee >>>447 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(448 await helper.collective.techCommittee.vote(baltatharAccount, fastTrackHash, techProposalIdx, true);590 technicalCommitteeThreshold,591 fastTrack,592 fastTrack.encodedLength,593 );594 const events5 = await submitTransactionAsync(alithAccount, tx5);449 await helper.collective.techCommittee.vote(alithAccount, fastTrackHash, techProposalIdx, true);595 const result5 = getGenericResult(events5);596 expect(result5.success).to.be.true;597450598 const encodedFastTrack = fastTrack?.method.toHex() || '';451 await helper.collective.techCommittee.close(baltatharAccount, fastTrackHash, techProposalIdx, 1_000_000_000, fastTrack.encodedLength);599 const fastTrackHash = blake2AsHex(encodedFastTrack);452 console.log('Fast track proposal through technical committee.......DONE');600 console.log('FastTrack hash is %s', fastTrackHash);453 // <<< Fast track proposal through technical committee <<<601454602 const proposalIdx = Number(await api.query.techCommitteeCollective.proposalCount()) - 1;455 // >>> Referendum voting >>>603 const tx6 = api.tx.techCommitteeCollective.vote(fastTrackHash, proposalIdx, true);456 console.log('Referendum voting.......');604 {457 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;458 balance: 10_000_000_000_000_000_000n,608 }459 vote: {aye: true, conviction: 1},609 {610 const events6 = await submitTransactionAsync(alithAccount, tx6);611 const result6 = getGenericResult(events6);460 });612 expect(result6.success).to.be.true;461 console.log('Referendum voting.......DONE');613 }462 // <<< Referendum voting <<<614463615 const tx7 = api.tx.techCommitteeCollective464 // >>> Acquire Quartz AssetId Info on Moonriver >>>616 .close(fastTrackHash, proposalIdx, 1_000_000_000, fastTrack.encodedLength);617 const events7 = await submitTransactionAsync(baltatharAccount, tx7);465 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 <<<622466623 // >>> Referendum voting >>>467 // 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}}},468 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 <<<634469635 // >>> Acquire Quartz AssetId Info on Moonriver >>>470 assetId = (await helper.assetManager.assetTypeId(quartzAssetLocation)).toString();636 console.log('Acquire Quartz AssetId Info on Moonriver.......');637471638 // Wait for the democracy execute472 console.log('QTZ asset ID is %s', assetId);473 console.log('Acquire Quartz AssetId Info on Moonriver.......DONE');639 await waitNewBlocks(api, 5);474 // >>> Acquire Quartz AssetId Info on Moonriver >>>640475641 assetId = (await api.query.assetManager.assetTypeId({476 // >>> Sponsoring random Account >>>477 console.log('Sponsoring random Account.......');642 XCM: sourceLocation,478 await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonriver.address, 11_000_000_000_000_000_000n);643 })).toString();479 console.log('Sponsoring random Account.......DONE');480 // <<< Sponsoring random Account <<<644481645 console.log('QTZ asset ID is %s', assetId);482 balanceMovrTokenInit = await helper.balance.getEthereum(randomAccountMoonriver.address);646 console.log('Acquire Quartz AssetId Info on Moonriver.......DONE');647 // >>> Acquire Quartz AssetId Info on Moonriver >>>483 });648484649 // >>> Sponsoring random Account >>>485 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);486 await helper.balance.transferToSubstrate(quartzDonor, randomAccountQuartz.address, 10n * TRANSFER_AMOUNT);665 const events0 = await submitTransactionAsync(quartzAlice, tx0);487 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 });488 });671 });489 });672490673 it('Should connect and send QTZ to Moonriver', async () => {491 itSub('Should connect and send QTZ to Moonriver', async ({helper}) => {674 await usingApi(async (api) => {675 const currencyId = {492 const currencyId = {676 NativeAssetId: 'Here',493 NativeAssetId: 'Here',677 };494 };678 const dest = {495 const dest = {679 V1: {496 V1: {680 parents: 1,497 parents: 1,681 interior: {498 interior: {682 X2: [499 X2: [683 {Parachain: MOONRIVER_CHAIN},500 {Parachain: MOONRIVER_CHAIN},684 {AccountKey20: {network: 'Any', key: randomAccountMoonriver.address}},501 {AccountKey20: {network: 'Any', key: randomAccountMoonriver.address}},685 ],502 ],686 },687 },503 },688 };504 },689 const amount = TRANSFER_AMOUNT;505 };506 const amount = TRANSFER_AMOUNT;690 const destWeight = 850000000;507 const destWeight = 850000000;691508692 const tx = api.tx.xTokens.transfer(currencyId, amount, dest, destWeight);509 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;696510697 [balanceQuartzTokenMiddle] = await getBalance(api, [randomAccountQuartz.address]);511 balanceQuartzTokenMiddle = await helper.balance.getSubstrate(randomAccountQuartz.address);698 expect(balanceQuartzTokenMiddle < balanceQuartzTokenInit).to.be.true;512 expect(balanceQuartzTokenMiddle < balanceQuartzTokenInit).to.be.true;699513700 const transactionFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;514 const transactionFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;701 console.log('[Quartz -> Moonriver] transaction fees on Quartz: %s QTZ', bigIntToDecimals(transactionFees));515 console.log('[Quartz -> Moonriver] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(transactionFees));702 expect(transactionFees > 0).to.be.true;516 expect(transactionFees > 0).to.be.true;703 });704517705 await usingApi(518 await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {706 async (api) => {707 await waitNewBlocks(api, 3);519 await helper.wait.newBlocks(3);708520709 [balanceMovrTokenMiddle] = await getBalance(api, [randomAccountMoonriver.address]);521 balanceMovrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonriver.address);710522711 const movrFees = balanceMovrTokenInit - balanceMovrTokenMiddle;523 const movrFees = balanceMovrTokenInit - balanceMovrTokenMiddle;712 console.log('[Quartz -> Moonriver] transaction fees on Moonriver: %s MOVR',bigIntToDecimals(movrFees));524 console.log('[Quartz -> Moonriver] transaction fees on Moonriver: %s MOVR',helper.util.bigIntToDecimals(movrFees));713 expect(movrFees == 0n).to.be.true;525 expect(movrFees == 0n).to.be.true;714526715 const qtzRandomAccountAsset = (527 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;528 const qtzIncomeTransfer = balanceForeignQtzTokenMiddle - balanceForeignQtzTokenInit;721 console.log('[Quartz -> Moonriver] income %s QTZ', bigIntToDecimals(qtzIncomeTransfer));529 console.log('[Quartz -> Moonriver] income %s QTZ', helper.util.bigIntToDecimals(qtzIncomeTransfer));722 expect(qtzIncomeTransfer == TRANSFER_AMOUNT).to.be.true;530 expect(qtzIncomeTransfer == TRANSFER_AMOUNT).to.be.true;723 },531 });724 moonriverOptions(),725 );726 });532 });727533728 it('Should connect to Moonriver and send QTZ back', async () => {534 itSub('Should connect to Moonriver and send QTZ back', async ({helper}) => {729 await usingApi(535 await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {730 async (api) => {731 const asset = {536 const asset = {732 V1: {537 V1: {733 id: {538 id: {734 Concrete: {539 Concrete: {735 parents: 1,540 parents: 1,736 interior: {541 interior: {737 X1: {Parachain: QUARTZ_CHAIN},542 X1: {Parachain: QUARTZ_CHAIN},738 },739 },543 },740 },544 },741 fun: {742 Fungible: TRANSFER_AMOUNT,743 },744 },545 },745 };546 fun: {746 const destination = {747 V1: {748 parents: 1,547 Fungible: TRANSFER_AMOUNT,749 interior: {750 X2: [751 {Parachain: QUARTZ_CHAIN},752 {AccountId32: {network: 'Any', id: randomAccountQuartz.addressRaw}},753 ],754 },755 },548 },756 };549 },550 };757 const destWeight = 50000000;551 const destination = {552 V1: {553 parents: 1,554 interior: {555 X2: [556 {Parachain: QUARTZ_CHAIN},557 {AccountId32: {network: 'Any', id: randomAccountQuartz.addressRaw}},558 ],559 },560 },561 };562 const destWeight = 50000000;758563759 const tx = api.tx.xTokens.transferMultiasset(asset, destination, destWeight);564 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;763565764 [balanceMovrTokenFinal] = await getBalance(api, [randomAccountMoonriver.address]);566 balanceMovrTokenFinal = await helper.balance.getEthereum(randomAccountMoonriver.address);765567766 const movrFees = balanceMovrTokenMiddle - balanceMovrTokenFinal;568 const movrFees = balanceMovrTokenMiddle - balanceMovrTokenFinal;767 console.log('[Moonriver -> Quartz] transaction fees on Moonriver: %s MOVR', bigIntToDecimals(movrFees));569 console.log('[Moonriver -> Quartz] transaction fees on Moonriver: %s MOVR', helper.util.bigIntToDecimals(movrFees));768 expect(movrFees > 0).to.be.true;570 expect(movrFees > 0).to.be.true;769571770 const qtzRandomAccountAsset = (572 const qtzRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonriver.address);771 await api.query.assets.account(assetId, randomAccountMoonriver.address)772 ).toJSON()! as any;773573774 expect(qtzRandomAccountAsset).to.be.null;574 expect(qtzRandomAccountAsset).to.be.null;775575776 balanceForeignQtzTokenFinal = 0n;576 balanceForeignQtzTokenFinal = 0n;777577778 const qtzOutcomeTransfer = balanceForeignQtzTokenMiddle - balanceForeignQtzTokenFinal;578 const qtzOutcomeTransfer = balanceForeignQtzTokenMiddle - balanceForeignQtzTokenFinal;779 console.log('[Quartz -> Moonriver] outcome %s QTZ', bigIntToDecimals(qtzOutcomeTransfer));579 console.log('[Quartz -> Moonriver] outcome %s QTZ', helper.util.bigIntToDecimals(qtzOutcomeTransfer));780 expect(qtzOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;580 expect(qtzOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;781 },581 });782 moonriverOptions(),783 );784582785 await usingApi(async (api) => {583 await helper.wait.newBlocks(3);786 await waitNewBlocks(api, 3);787584788 [balanceQuartzTokenFinal] = await getBalance(api, [randomAccountQuartz.address]);585 balanceQuartzTokenFinal = await helper.balance.getSubstrate(randomAccountQuartz.address);789 const actuallyDelivered = balanceQuartzTokenFinal - balanceQuartzTokenMiddle;586 const actuallyDelivered = balanceQuartzTokenFinal - balanceQuartzTokenMiddle;790 expect(actuallyDelivered > 0).to.be.true;587 expect(actuallyDelivered > 0).to.be.true;791588792 console.log('[Moonriver -> Quartz] actually delivered %s QTZ', bigIntToDecimals(actuallyDelivered));589 console.log('[Moonriver -> Quartz] actually delivered %s QTZ', helper.util.bigIntToDecimals(actuallyDelivered));793590794 const qtzFees = TRANSFER_AMOUNT - actuallyDelivered;591 const qtzFees = TRANSFER_AMOUNT - actuallyDelivered;795 console.log('[Moonriver -> Quartz] transaction fees on Quartz: %s QTZ', bigIntToDecimals(qtzFees));592 console.log('[Moonriver -> Quartz] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees));796 expect(qtzFees == 0n).to.be.true;593 expect(qtzFees == 0n).to.be.true;797 });798 });594 });799});595});800596tests/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, describeXCM, 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';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}6364describeXCM('[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;76 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 = await 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});293212296 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 = await 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});435338436describeXCM('[XCM] Integration test: Exchanging UNQ with Moonbeam', () => {339describeXCM('[XCM] Integration test: Exchanging UNQ with Moonbeam', () => {437438 // Unique constants340 // Unique constants439 let uniqueAlice: IKeyringPair;341 let uniqueDonor: IKeyringPair;440 let uniqueAssetLocation;342 let uniqueAssetLocation;441343442 let randomAccountUnique: IKeyringPair;344 let randomAccountUnique: IKeyringPair;445 // Moonbeam constants347 // Moonbeam constants446 let assetId: string;348 let assetId: string;447349448 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;350 const councilVotingThreshold = 2;458 const technicalCommitteeThreshold = 2;351 const technicalCommitteeThreshold = 2;459 const votingPeriod = 3;352 const votingPeriod = 3;464 symbol: 'xcUNQ',357 symbol: 'xcUNQ',465 decimals: 18,358 decimals: 18,466 isFrozen: false,359 isFrozen: false,467 minimalBalance: 1,360 minimalBalance: 1n,468 };361 };469362470 let balanceUniqueTokenInit: bigint;363 let balanceUniqueTokenInit: bigint;478 let balanceGlmrTokenFinal: bigint;371 let balanceGlmrTokenFinal: bigint;479372480 before(async () => {373 before(async () => {481 await usingApi(async (api, privateKeyWrapper) => {374 await usingPlaygrounds(async (helper, privateKey) => {482 const keyringEth = new Keyring({type: 'ethereum'});375 uniqueDonor = await privateKey('//Alice');483 const keyringSr25519 = new Keyring({type: 'sr25519'});376 [randomAccountUnique] = await helper.arrange.createAccounts([0n], uniqueDonor);484377485 uniqueAlice = privateKeyWrapper('//Alice');486 randomAccountUnique = generateKeyringPair(keyringSr25519);487 randomAccountMoonbeam = generateKeyringPair(keyringEth);488489 balanceForeignUnqTokenInit = 0n;378 balanceForeignUnqTokenInit = 0n;490 });379 });491380492 await usingApi(381 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {493 async (api) => {382 const alithAccount = helper.account.alithAccount();383 const baltatharAccount = helper.account.baltatharAccount();384 const dorothyAccount = helper.account.dorothyAccount();494385495 // >>> Sponsoring Dorothy >>>386 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 <<<503387504 const sourceLocation: MultiLocation = api.createType(388 // >>> Sponsoring Dorothy >>>505 'MultiLocation',389 console.log('Sponsoring Dorothy.......');506 {390 await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);507 parents: 1,391 console.log('Sponsoring Dorothy.......DONE');508 interior: {X1: {Parachain: UNIQUE_CHAIN}},392 // <<< Sponsoring Dorothy <<<509 },510 );511393512 uniqueAssetLocation = {XCM: sourceLocation};394 uniqueAssetLocation = {395 XCM: {396 parents: 1,397 interior: {X1: {Parachain: UNIQUE_CHAIN}},513 const existentialDeposit = 1;398 },399 };400 const existentialDeposit = 1n;514 const isSufficient = true;401 const isSufficient = true;515 const unitsPerSecond = '1';402 const unitsPerSecond = 1n;516 const numAssetsWeightHint = 0;403 const numAssetsWeightHint = 0;517404518 const registerTx = api.tx.assetManager.registerForeignAsset(405 const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({519 uniqueAssetLocation,406 location: uniqueAssetLocation,520 uniqueAssetMetadata,407 metadata: uniqueAssetMetadata,521 existentialDeposit,408 existentialDeposit,522 isSufficient,409 isSufficient,523 );410 unitsPerSecond,524 console.log('Encoded proposal for registerAsset is %s', registerTx.method.toHex() || '');411 numAssetsWeightHint,412 });413 const proposalHash = blake2AsHex(encodedProposal);525414526 const setUnitsTx = api.tx.assetManager.setAssetUnitsPerSecond(415 console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);527 uniqueAssetLocation,528 unitsPerSecond,529 numAssetsWeightHint,416 console.log('Encoded length %d', encodedProposal.length);530 );531 console.log('Encoded proposal for setAssetUnitsPerSecond is %s', setUnitsTx.method.toHex() || '');417 console.log('Encoded proposal hash for batch utility after schedule is %s', proposalHash);532418533 const batchCall = api.tx.utility.batchAll([registerTx, setUnitsTx]);419 // >>> Note motion preimage >>>420 console.log('Note motion preimage.......');421 await helper.democracy.notePreimage(baltatharAccount, encodedProposal);534 console.log('Encoded proposal for batchCall is %s', batchCall.method.toHex() || '');422 console.log('Note motion preimage.......DONE');423 // <<< Note motion preimage <<<535424536 // >>> Note motion preimage >>>425 // >>> Propose external motion through council >>>537 console.log('Note motion preimage.......');426 console.log('Propose external motion through council.......');538 const encodedProposal = batchCall?.method.toHex() || '';427 const externalMotion = helper.democracy.externalProposeMajority(proposalHash);539 const proposalHash = blake2AsHex(encodedProposal);428 const encodedMotion = externalMotion?.method.toHex() || '';540 console.log('Encoded proposal for batch utility after schedule is %s', encodedProposal);429 const motionHash = blake2AsHex(encodedMotion);541 console.log('Encoded proposal hash for batch utility after schedule is %s', proposalHash);430 console.log('Motion hash is %s', motionHash);542 console.log('Encoded length %d', encodedProposal.length);543431544 const tx1 = api.tx.democracy.notePreimage(encodedProposal);432 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 <<<550433551 // >>> Propose external motion through council >>>434 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(435 await helper.collective.council.vote(dorothyAccount, motionHash, councilProposalIdx, true);555 councilVotingThreshold,556 externalMotion,557 externalMotion.encodedLength,558 );559 const events2 = await submitTransactionAsync(baltatharAccount, tx2);436 await helper.collective.council.vote(baltatharAccount, motionHash, councilProposalIdx, true);560 const result2 = getGenericResult(events2);561 expect(result2.success).to.be.true;562437563 const encodedMotion = externalMotion?.method.toHex() || '';438 await helper.collective.council.close(dorothyAccount, motionHash, councilProposalIdx, 1_000_000_000, externalMotion.encodedLength);564 const motionHash = blake2AsHex(encodedMotion);439 console.log('Propose external motion through council.......DONE');565 console.log('Motion hash is %s', motionHash);440 // <<< Propose external motion through council <<<566441567 const tx3 = api.tx.councilCollective.vote(motionHash, 0, true);442 // >>> Fast track proposal through technical committee >>>568 {569 const events3 = await submitTransactionAsync(dorothyAccount, tx3);443 console.log('Fast track proposal through technical committee.......');570 const result3 = getGenericResult(events3);444 const fastTrack = helper.democracy.fastTrack(proposalHash, votingPeriod, delayPeriod);571 expect(result3.success).to.be.true;572 }445 const encodedFastTrack = fastTrack?.method.toHex() || '';573 {574 const events3 = await submitTransactionAsync(baltatharAccount, tx3);575 const result3 = getGenericResult(events3);446 const fastTrackHash = blake2AsHex(encodedFastTrack);576 expect(result3.success).to.be.true;447 console.log('FastTrack hash is %s', fastTrackHash);577 }578448579 const tx4 = api.tx.councilCollective.close(motionHash, 0, 1_000_000_000, externalMotion.encodedLength);449 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 <<<585450586 // >>> Fast track proposal through technical committee >>>451 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(452 await helper.collective.techCommittee.vote(baltatharAccount, fastTrackHash, techProposalIdx, true);590 technicalCommitteeThreshold,591 fastTrack,592 fastTrack.encodedLength,593 );594 const events5 = await submitTransactionAsync(alithAccount, tx5);453 await helper.collective.techCommittee.vote(alithAccount, fastTrackHash, techProposalIdx, true);595 const result5 = getGenericResult(events5);596 expect(result5.success).to.be.true;597454598 const encodedFastTrack = fastTrack?.method.toHex() || '';455 await helper.collective.techCommittee.close(baltatharAccount, fastTrackHash, techProposalIdx, 1_000_000_000, fastTrack.encodedLength);599 const fastTrackHash = blake2AsHex(encodedFastTrack);456 console.log('Fast track proposal through technical committee.......DONE');600 console.log('FastTrack hash is %s', fastTrackHash);457 // <<< Fast track proposal through technical committee <<<601458602 const proposalIdx = Number(await api.query.techCommitteeCollective.proposalCount()) - 1;459 // >>> Referendum voting >>>603 const tx6 = api.tx.techCommitteeCollective.vote(fastTrackHash, proposalIdx, true);460 console.log('Referendum voting.......');604 {461 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;462 balance: 10_000_000_000_000_000_000n,608 }463 vote: {aye: true, conviction: 1},609 {610 const events6 = await submitTransactionAsync(alithAccount, tx6);611 const result6 = getGenericResult(events6);464 });612 expect(result6.success).to.be.true;465 console.log('Referendum voting.......DONE');613 }466 // <<< Referendum voting <<<614467615 const tx7 = api.tx.techCommitteeCollective468 // >>> Acquire Unique AssetId Info on Moonbeam >>>616 .close(fastTrackHash, proposalIdx, 1_000_000_000, fastTrack.encodedLength);617 const events7 = await submitTransactionAsync(baltatharAccount, tx7);469 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 <<<622470623 // >>> Referendum voting >>>471 // 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}}},472 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 <<<634473635 // >>> Acquire Unique AssetId Info on Moonbeam >>>474 assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString();636 console.log('Acquire Unique AssetId Info on Moonbeam.......');637475638 // Wait for the democracy execute476 console.log('UNQ asset ID is %s', assetId);477 console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');639 await waitNewBlocks(api, 5);478 // >>> Acquire Unique AssetId Info on Moonbeam >>>640479641 assetId = (await api.query.assetManager.assetTypeId({480 // >>> Sponsoring random Account >>>481 console.log('Sponsoring random Account.......');642 XCM: sourceLocation,482 await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);643 })).toString();483 console.log('Sponsoring random Account.......DONE');484 // <<< Sponsoring random Account <<<644485645 console.log('UNQ asset ID is %s', assetId);486 balanceGlmrTokenInit = await helper.balance.getEthereum(randomAccountMoonbeam.address);646 console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');647 // >>> Acquire Unique AssetId Info on Moonbeam >>>487 });648488649 // >>> Sponsoring random Account >>>489 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);490 await helper.balance.transferToSubstrate(uniqueDonor, randomAccountUnique.address, 10n * TRANSFER_AMOUNT);665 const events0 = await submitTransactionAsync(uniqueAlice, tx0);491 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 });492 });671 });493 });672494673 it('Should connect and send UNQ to Moonbeam', async () => {495 itSub('Should connect and send UNQ to Moonbeam', async ({helper}) => {674 await usingApi(async (api) => {675 const currencyId = {496 const currencyId = {676 NativeAssetId: 'Here',497 NativeAssetId: 'Here',677 };498 };678 const dest = {499 const dest = {679 V1: {500 V1: {680 parents: 1,501 parents: 1,681 interior: {502 interior: {682 X2: [503 X2: [683 {Parachain: MOONBEAM_CHAIN},504 {Parachain: MOONBEAM_CHAIN},684 {AccountKey20: {network: 'Any', key: randomAccountMoonbeam.address}},505 {AccountKey20: {network: 'Any', key: randomAccountMoonbeam.address}},685 ],506 ],686 },687 },507 },688 };508 },689 const amount = TRANSFER_AMOUNT;509 };510 const amount = TRANSFER_AMOUNT;690 const destWeight = 850000000;511 const destWeight = 850000000;691512692 const tx = api.tx.xTokens.transfer(currencyId, amount, dest, destWeight);513 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;696514697 [balanceUniqueTokenMiddle] = await getBalance(api, [randomAccountUnique.address]);515 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccountUnique.address);698 expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true;516 expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true;699517700 const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;518 const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;701 console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', bigIntToDecimals(transactionFees));519 console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(transactionFees));702 expect(transactionFees > 0).to.be.true;520 expect(transactionFees > 0).to.be.true;703 });704521705 await usingApi(522 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {706 async (api) => {707 await waitNewBlocks(api, 3);523 await helper.wait.newBlocks(3);708524709 [balanceGlmrTokenMiddle] = await getBalance(api, [randomAccountMoonbeam.address]);525 balanceGlmrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonbeam.address);710526711 const glmrFees = balanceGlmrTokenInit - balanceGlmrTokenMiddle;527 const glmrFees = balanceGlmrTokenInit - balanceGlmrTokenMiddle;712 console.log('[Unique -> Moonbeam] transaction fees on Moonbeam: %s GLMR', bigIntToDecimals(glmrFees));528 console.log('[Unique -> Moonbeam] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));713 expect(glmrFees == 0n).to.be.true;529 expect(glmrFees == 0n).to.be.true;714530715 const unqRandomAccountAsset = (531 balanceForeignUnqTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonbeam.address))!;716 await api.query.assets.account(assetId, randomAccountMoonbeam.address)717 ).toJSON()! as any;718532719 balanceForeignUnqTokenMiddle = BigInt(unqRandomAccountAsset['balance']);533 const unqIncomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenInit;720 const unqIncomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenInit;721 console.log('[Unique -> Moonbeam] income %s UNQ', bigIntToDecimals(unqIncomeTransfer));534 console.log('[Unique -> Moonbeam] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));722 expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;535 expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;723 },536 });724 moonbeamOptions(),725 );726 });537 });727538728 it('Should connect to Moonbeam and send UNQ back', async () => {539 itSub('Should connect to Moonbeam and send UNQ back', async ({helper}) => {729 await usingApi(540 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {730 async (api) => {731 const asset = {541 const asset = {732 V1: {542 V1: {733 id: {543 id: {734 Concrete: {544 Concrete: {735 parents: 1,545 parents: 1,736 interior: {546 interior: {737 X1: {Parachain: UNIQUE_CHAIN},547 X1: {Parachain: UNIQUE_CHAIN},738 },739 },548 },740 },549 },741 fun: {742 Fungible: TRANSFER_AMOUNT,743 },744 },550 },745 };551 fun: {746 const destination = {747 V1: {748 parents: 1,552 Fungible: TRANSFER_AMOUNT,749 interior: {750 X2: [751 {Parachain: UNIQUE_CHAIN},752 {AccountId32: {network: 'Any', id: randomAccountUnique.addressRaw}},753 ],754 },755 },553 },756 };554 },555 };757 const destWeight = 50000000;556 const destination = {557 V1: {558 parents: 1,559 interior: {560 X2: [561 {Parachain: UNIQUE_CHAIN},562 {AccountId32: {network: 'Any', id: randomAccountUnique.addressRaw}},563 ],564 },565 },566 };567 const destWeight = 50000000;758568759 const tx = api.tx.xTokens.transferMultiasset(asset, destination, destWeight);569 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;763570764 [balanceGlmrTokenFinal] = await getBalance(api, [randomAccountMoonbeam.address]);571 balanceGlmrTokenFinal = await helper.balance.getEthereum(randomAccountMoonbeam.address);765572766 const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal;573 const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal;767 console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', bigIntToDecimals(glmrFees));574 console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));768 expect(glmrFees > 0).to.be.true;575 expect(glmrFees > 0).to.be.true;769576770 const unqRandomAccountAsset = (577 const unqRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonbeam.address);771 await api.query.assets.account(assetId, randomAccountMoonbeam.address)772 ).toJSON()! as any;773578774 expect(unqRandomAccountAsset).to.be.null;579 expect(unqRandomAccountAsset).to.be.null;580 581 balanceForeignUnqTokenFinal = 0n;775582776 balanceForeignUnqTokenFinal = 0n;583 const unqOutcomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenFinal;584 console.log('[Unique -> Moonbeam] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));585 expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;586 });777587778 const unqOutcomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenFinal;588 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 );784589785 await usingApi(async (api) => {590 balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccountUnique.address);786 await waitNewBlocks(api, 3);591 const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;592 expect(actuallyDelivered > 0).to.be.true;787593788 [balanceUniqueTokenFinal] = await getBalance(api, [randomAccountUnique.address]);594 console.log('[Moonbeam -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));789 const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;790 expect(actuallyDelivered > 0).to.be.true;791595792 console.log('[Moonbeam -> Unique] actually delivered %s UNQ', bigIntToDecimals(actuallyDelivered));596 const unqFees = TRANSFER_AMOUNT - actuallyDelivered;793794 const unqFees = TRANSFER_AMOUNT - actuallyDelivered;795 console.log('[Moonbeam -> Unique] transaction fees on Unique: %s UNQ', bigIntToDecimals(unqFees));597 console.log('[Moonbeam -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));796 expect(unqFees == 0n).to.be.true;598 expect(unqFees == 0n).to.be.true;797 });798 });599 });799});600});800601