difftreelog
Merge pull request #1024 from UniqueNetwork/refactor/foreign-assets-as-thin-proxy
in: master
Refactor/foreign assets as thin proxy
50 files changed
.envdiffbeforeafterboth161617KUSAMA_MAINNET_BRANCH=release-v1.0.017KUSAMA_MAINNET_BRANCH=release-v1.0.018STATEMINE_BUILD_BRANCH=release-parachains-v943018STATEMINE_BUILD_BRANCH=release-parachains-v943019KARURA_BUILD_BRANCH=release-karura-2.21.019KARURA_BUILD_BRANCH=xnft-poc20MOONRIVER_BUILD_BRANCH=runtime-250020MOONRIVER_BUILD_BRANCH=runtime-250021SHIDEN_BUILD_BRANCH=v5.18.021SHIDEN_BUILD_BRANCH=v5.18.022QUARTZ_MAINNET_BRANCH=release-v1001006322QUARTZ_MAINNET_BRANCH=release-v10010063.github/workflows/ci-develop.ymldiffbeforeafterboth36 uses: ./.github/workflows/xcm.yml36 uses: ./.github/workflows/xcm.yml37 secrets: inherit37 secrets: inherit38 3839 xnft:40 if: github.event.pull_request.draft == false && contains(github.event.pull_request.labels.*.name, 'CI-xnft')41 uses: ./.github/workflows/xnft.yml42 secrets: inherit43 39 collator-selection:44 collator-selection:40 if: github.event.pull_request.draft == false && contains(github.event.pull_request.labels.*.name, 'CI-collator-selection')45 if: github.event.pull_request.draft == false && contains(github.event.pull_request.labels.*.name, 'CI-collator-selection').github/workflows/ci-master.ymldiffbeforeafterboth32 uses: ./.github/workflows/xcm.yml32 uses: ./.github/workflows/xcm.yml33 secrets: inherit # pass all secrets from initial workflow to nested33 secrets: inherit # pass all secrets from initial workflow to nested34 3435 xnft:36 uses: ./.github/workflows/xnft.yml37 secrets: inherit # pass all secrets from initial workflow to nested38 35 collator-selection:39 collator-selection:36 uses: ./.github/workflows/collator-selection.yml40 uses: ./.github/workflows/collator-selection.yml.github/workflows/xnft.ymldiffbeforeafterbothno changes
Cargo.lockdiffbeforeafterboth256725672568[[package]]2568[[package]]2569name = "data-encoding"2569name = "data-encoding"2570version = "2.4.0"2570version = "2.5.0"2571source = "registry+https://github.com/rust-lang/crates.io-index"2571source = "registry+https://github.com/rust-lang/crates.io-index"2572checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"2572checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"257325732574[[package]]2574[[package]]2575name = "data-encoding-macro"2575name = "data-encoding-macro"2576version = "0.1.13"2576version = "0.1.14"2577source = "registry+https://github.com/rust-lang/crates.io-index"2577source = "registry+https://github.com/rust-lang/crates.io-index"2578checksum = "c904b33cc60130e1aeea4956ab803d08a3f4a0ca82d64ed757afac3891f2bb99"2578checksum = "20c01c06f5f429efdf2bae21eb67c28b3df3cf85b7dd2d8ef09c0838dac5d33e"2579dependencies = [2579dependencies = [2580 "data-encoding",2580 "data-encoding",2581 "data-encoding-macro-internal",2581 "data-encoding-macro-internal",2582]2582]258325832584[[package]]2584[[package]]2585name = "data-encoding-macro-internal"2585name = "data-encoding-macro-internal"2586version = "0.1.11"2586version = "0.1.12"2587source = "registry+https://github.com/rust-lang/crates.io-index"2587source = "registry+https://github.com/rust-lang/crates.io-index"2588checksum = "8fdf3fce3ce863539ec1d7fd1b6dcc3c645663376b43ed376bbf887733e4f772"2588checksum = "0047d07f2c89b17dd631c80450d69841a6b5d7fb17278cbc43d7e4cfcf2576f3"2589dependencies = [2589dependencies = [2590 "data-encoding",2590 "data-encoding",2591 "syn 1.0.109",2591 "syn 1.0.109",467846784679[[package]]4679[[package]]4680name = "if-addrs"4680name = "if-addrs"4681version = "0.7.0"4681version = "0.10.2"4682source = "registry+https://github.com/rust-lang/crates.io-index"4682source = "registry+https://github.com/rust-lang/crates.io-index"4683checksum = "cbc0fa01ffc752e9dbc72818cdb072cd028b86be5e09dd04c5a643704fe101a9"4683checksum = "cabb0019d51a643781ff15c9c8a3e5dedc365c47211270f4e8f82812fedd8f0a"4684dependencies = [4684dependencies = [4685 "libc",4685 "libc",4686 "winapi",4686 "windows-sys 0.48.0",4687]4687]468846884689[[package]]4689[[package]]4690name = "if-watch"4690name = "if-watch"4691version = "3.1.0"4691version = "3.2.0"4692source = "registry+https://github.com/rust-lang/crates.io-index"4692source = "registry+https://github.com/rust-lang/crates.io-index"4693checksum = "bbb892e5777fe09e16f3d44de7802f4daa7267ecbe8c466f19d94e25bb0c303e"4693checksum = "d6b0422c86d7ce0e97169cc42e04ae643caf278874a7a3c87b8150a220dc7e1e"4694dependencies = [4694dependencies = [4695 "async-io 1.13.0",4695 "async-io 2.2.0",4696 "core-foundation",4696 "core-foundation",4697 "fnv",4697 "fnv",4698 "futures",4698 "futures",6557 "impl-trait-for-tuples",6557 "impl-trait-for-tuples",6558 "log",6558 "log",6559 "num_enum",6559 "num_enum",6560 "orml-tokens",6561 "orml-traits",6560 "orml-traits",6562 "orml-vesting",6561 "orml-vesting",6563 "orml-xcm-support",6562 "orml-xcm-support",6705 "num-traits",6704 "num-traits",6706]6705]67076708[[package]]6709name = "orml-tokens"6710version = "0.5.0-dev"6711source = "git+https://github.com/uniquenetwork/open-runtime-module-library?branch=unique-polkadot-v1.3.0#6f36a9e79b75421ac56b259fd47d03db6c3e1603"6712dependencies = [6713 "frame-support",6714 "frame-system",6715 "log",6716 "orml-traits",6717 "parity-scale-codec",6718 "scale-info",6719 "serde",6720 "sp-arithmetic",6721 "sp-runtime",6722 "sp-std",6723]672467066725[[package]]6707[[package]]6726name = "orml-traits"6708name = "orml-traits"6727version = "0.5.0-dev"6709version = "0.6.1"6728source = "git+https://github.com/uniquenetwork/open-runtime-module-library?branch=unique-polkadot-v1.3.0#6f36a9e79b75421ac56b259fd47d03db6c3e1603"6710source = "git+https://github.com/uniquenetwork/open-runtime-module-library?branch=unique-polkadot-v1.3.0#99604b000be6252597cfcb82eee82554f6a07a0e"6729dependencies = [6711dependencies = [6730 "frame-support",6712 "frame-support",6731 "impl-trait-for-tuples",6713 "impl-trait-for-tuples",674467266745[[package]]6727[[package]]6746name = "orml-utilities"6728name = "orml-utilities"6747version = "0.5.0-dev"6729version = "0.6.1"6748source = "git+https://github.com/uniquenetwork/open-runtime-module-library?branch=unique-polkadot-v1.3.0#6f36a9e79b75421ac56b259fd47d03db6c3e1603"6730source = "git+https://github.com/uniquenetwork/open-runtime-module-library?branch=unique-polkadot-v1.3.0#99604b000be6252597cfcb82eee82554f6a07a0e"6749dependencies = [6731dependencies = [6750 "frame-support",6732 "frame-support",6751 "parity-scale-codec",6733 "parity-scale-codec",675967416760[[package]]6742[[package]]6761name = "orml-vesting"6743name = "orml-vesting"6762version = "0.5.0-dev"6744version = "0.6.1"6763source = "git+https://github.com/uniquenetwork/open-runtime-module-library?branch=unique-polkadot-v1.3.0#6f36a9e79b75421ac56b259fd47d03db6c3e1603"6745source = "git+https://github.com/uniquenetwork/open-runtime-module-library?branch=unique-polkadot-v1.3.0#99604b000be6252597cfcb82eee82554f6a07a0e"6764dependencies = [6746dependencies = [6765 "frame-support",6747 "frame-support",6766 "frame-system",6748 "frame-system",677467566775[[package]]6757[[package]]6776name = "orml-xcm-support"6758name = "orml-xcm-support"6777version = "0.5.0-dev"6759version = "0.6.1"6778source = "git+https://github.com/uniquenetwork/open-runtime-module-library?branch=unique-polkadot-v1.3.0#6f36a9e79b75421ac56b259fd47d03db6c3e1603"6760source = "git+https://github.com/uniquenetwork/open-runtime-module-library?branch=unique-polkadot-v1.3.0#99604b000be6252597cfcb82eee82554f6a07a0e"6779dependencies = [6761dependencies = [6780 "frame-support",6762 "frame-support",6781 "orml-traits",6763 "orml-traits",678867706789[[package]]6771[[package]]6790name = "orml-xtokens"6772name = "orml-xtokens"6791version = "0.5.0-dev"6773version = "0.6.1"6792source = "git+https://github.com/uniquenetwork/open-runtime-module-library?branch=unique-polkadot-v1.3.0#6f36a9e79b75421ac56b259fd47d03db6c3e1603"6774source = "git+https://github.com/uniquenetwork/open-runtime-module-library?branch=unique-polkadot-v1.3.0#99604b000be6252597cfcb82eee82554f6a07a0e"6793dependencies = [6775dependencies = [6794 "cumulus-primitives-core",6776 "cumulus-primitives-core",6795 "frame-support",6777 "frame-support",7446 "frame-support",7428 "frame-support",7447 "frame-system",7429 "frame-system",7448 "log",7430 "log",7449 "orml-tokens",7450 "pallet-balances",7431 "pallet-balances",7451 "pallet-common",7432 "pallet-common",7452 "pallet-fungible",7433 "pallet-fungible",10150 "impl-trait-for-tuples",10131 "impl-trait-for-tuples",10151 "log",10132 "log",10152 "num_enum",10133 "num_enum",10153 "orml-tokens",10154 "orml-traits",10134 "orml-traits",10155 "orml-vesting",10135 "orml-vesting",10156 "orml-xcm-support",10136 "orml-xcm-support",136951367513696[[package]]13676[[package]]13697name = "spinners"13677name = "spinners"13698version = "4.1.0"13678version = "4.1.1"13699source = "registry+https://github.com/rust-lang/crates.io-index"13679source = "registry+https://github.com/rust-lang/crates.io-index"13700checksum = "08615eea740067d9899969bc2891c68a19c315cb1f66640af9a9ecb91b13bcab"13680checksum = "a0ef947f358b9c238923f764c72a4a9d42f2d637c46e059dbd319d6e7cfb4f82"13701dependencies = [13681dependencies = [13702 "lazy_static",13682 "lazy_static",13703 "maplit",13683 "maplit",15028 "impl-trait-for-tuples",15008 "impl-trait-for-tuples",15029 "log",15009 "log",15030 "num_enum",15010 "num_enum",15031 "orml-tokens",15032 "orml-traits",15011 "orml-traits",15033 "orml-vesting",15012 "orml-vesting",15034 "orml-xcm-support",15013 "orml-xcm-support",Cargo.tomldiffbeforeafterboth199try-runtime-cli = "0.35.0"199try-runtime-cli = "0.35.0"200200201# ORML201# ORML202orml-tokens = { default-features = false, git = "https://github.com/uniquenetwork/open-runtime-module-library", branch = "unique-polkadot-v1.3.0" }203orml-traits = { default-features = false, git = "https://github.com/uniquenetwork/open-runtime-module-library", branch = "unique-polkadot-v1.3.0" }202orml-traits = { default-features = false, git = "https://github.com/uniquenetwork/open-runtime-module-library", branch = "unique-polkadot-v1.3.0" }204orml-vesting = { default-features = false, git = "https://github.com/uniquenetwork/open-runtime-module-library", branch = "unique-polkadot-v1.3.0" }203orml-vesting = { default-features = false, git = "https://github.com/uniquenetwork/open-runtime-module-library", branch = "unique-polkadot-v1.3.0" }205orml-xcm-support = { default-features = false, git = "https://github.com/uniquenetwork/open-runtime-module-library", branch = "unique-polkadot-v1.3.0" }204orml-xcm-support = { default-features = false, git = "https://github.com/uniquenetwork/open-runtime-module-library", branch = "unique-polkadot-v1.3.0" }js-packages/playgrounds/types.tsdiffbeforeafterboth160 External = 1,160 External = 1,161 /// Supports ERC721Metadata161 /// Supports ERC721Metadata162 Erc721metadata = 64,162 Erc721metadata = 64,163 /// Tokens in foreign collections can be transferred, but not burnt163 /// A collection of foreign assets164 Foreign = 128,164 Foreign = 128,165}165}166166js-packages/playgrounds/types.xcm.tsdiffbeforeafterboth28 },28 },29}29}303031export interface IForeignAssetMetadata {32 name?: number | Uint8Array,33 symbol?: string,34 decimals?: number,35 minimalBalance?: bigint,36}js-packages/playgrounds/unique.xcm.tsdiffbeforeafterboth2import type {IKeyringPair} from '@polkadot/types/types';2import type {IKeyringPair} from '@polkadot/types/types';3import {ChainHelperBase, EthereumBalanceGroup, HelperGroup, SubstrateBalanceGroup, UniqueHelper} from './unique.js';3import {ChainHelperBase, EthereumBalanceGroup, HelperGroup, SubstrateBalanceGroup, UniqueHelper} from './unique.js';4import type {ILogger, TSigner, TSubstrateAccount} from './types.js';4import type {ILogger, TSigner, TSubstrateAccount} from './types.js';5import type {AcalaAssetMetadata, DemocracyStandardAccountVote, IForeignAssetMetadata, MoonbeamAssetInfo} from './types.xcm.js';5import type {AcalaAssetMetadata, DemocracyStandardAccountVote, MoonbeamAssetInfo} from './types.xcm.js';66778export class XcmChainHelper extends ChainHelperBase {8export class XcmChainHelper extends ChainHelperBase {104}104}105105106export class ForeignAssetsGroup extends HelperGroup<UniqueHelper> {106export class ForeignAssetsGroup extends HelperGroup<UniqueHelper> {107 async register(signer: TSigner, ownerAddress: TSubstrateAccount, location: any, metadata: IForeignAssetMetadata) {107 async register(signer: TSigner, assetId: any, name: string, tokenPrefix: string, mode: 'NFT' | { Fungible: number }) {108 await this.helper.executeExtrinsic(108 await this.helper.executeExtrinsic(109 signer,109 signer,110 'api.tx.foreignAssets.registerForeignAsset',110 'api.tx.foreignAssets.forceRegisterForeignAsset',111 [ownerAddress, location, metadata],111 [{V3: assetId}, this.helper.util.str2vec(name), tokenPrefix, mode],112 true,112 true,113 );113 );114 }114 }115115116 async update(signer: TSigner, foreignAssetId: number, location: any, metadata: IForeignAssetMetadata) {116 async foreignCollectionId(assetId: any) {117 await this.helper.executeExtrinsic(117 return (await this.helper.callRpc('api.query.foreignAssets.foreignAssetToCollection', [assetId])).toJSON();118 signer,119 'api.tx.foreignAssets.updateForeignAsset',120 [foreignAssetId, location, metadata],121 true,122 );123 }118 }124}119}256 await this.helper.executeExtrinsic(signer, 'api.tx.assets.mint', [assetId, beneficiary, amount], true);251 await this.helper.executeExtrinsic(signer, 'api.tx.assets.mint', [assetId, beneficiary, amount], true);257 }252 }253254 async assetInfo(assetId: number | bigint) {255 return (await this.helper.callRpc('api.query.assets.asset', [assetId])).toJSON();256 }258257259 async account(assetId: string | number | bigint, address: string) {258 async account(assetId: string | number | bigint, address: string) {260 const accountAsset = (259 const accountAsset = (js-packages/tests/eth/api/CollectionHelpers.soldiffbeforeafterboth132type CollectionFlags is uint8;132type CollectionFlags is uint8;133133134library CollectionFlagsLib {134library CollectionFlagsLib {135 /// Tokens in foreign collections can be transferred, but not burnt135 /// A collection of foreign assets136 CollectionFlags constant foreignField = CollectionFlags.wrap(128);136 CollectionFlags constant foreignField = CollectionFlags.wrap(128);137 /// Supports ERC721Metadata137 /// Supports ERC721Metadata138 CollectionFlags constant erc721metadataField = CollectionFlags.wrap(64);138 CollectionFlags constant erc721metadataField = CollectionFlags.wrap(64);js-packages/tests/pallet-presence.test.tsdiffbeforeafterboth47 'nonfungible',47 'nonfungible',48 'charging',48 'charging',49 'configuration',49 'configuration',50 'tokens',51 'xtokens',50 'xtokens',52 'maintenance',51 'maintenance',53];52];js-packages/tests/xcm/xcm.types.tsdiffbeforeafterboth506 });506 });507 }507 }508509 async registerRelayNativeTokenOnUnique(alice: IKeyringPair) {510 return await usingPlaygrounds(async (helper) => {511 const relayLocation = {512 parents: 1,513 interior: 'Here',514 };515 const relayAssetId = {Concrete: relayLocation};516517 const relayCollectionId = await helper.foreignAssets.foreignCollectionId(relayAssetId);518 if(relayCollectionId == null) {519 const name = 'Relay Tokens';520 const tokenPrefix = 'xDOT';521 const decimals = 10;522 await helper.getSudo().foreignAssets.register(alice, relayAssetId, name, tokenPrefix, {Fungible: decimals});523524 return await helper.foreignAssets.foreignCollectionId(relayAssetId);525 } else {526 console.log('Relay foreign collection is already registered');527 return relayCollectionId;528 }529 });530 }508}531}509532js-packages/tests/xcm/xcmOpal.test.tsdiffbeforeafterboth17import type {IKeyringPair} from '@polkadot/types/types';17import type {IKeyringPair} from '@polkadot/types/types';18import config from '../config.js';18import config from '../config.js';19import {itSub, expect, describeXCM, usingPlaygrounds, usingWestmintPlaygrounds, usingRelayPlaygrounds} from '../util/index.js';19import {itSub, expect, describeXCM, usingPlaygrounds, usingWestmintPlaygrounds, usingRelayPlaygrounds} from '../util/index.js';20import {XcmTestHelper} from './xcm.types.js';202121const STATEMINE_CHAIN = +(process.env.RELAY_WESTMINT_ID || 1000);22const STATEMINE_CHAIN = +(process.env.RELAY_WESTMINT_ID || 1000);22const UNIQUE_CHAIN = +(process.env.RELAY_OPAL_ID || 2095);23const UNIQUE_CHAIN = +(process.env.RELAY_OPAL_ID || 2095);25const westmintUrl = config.westmintUrl;26const westmintUrl = config.westmintUrl;262727const STATEMINE_PALLET_INSTANCE = 50;28const STATEMINE_PALLET_INSTANCE = 50;28const ASSET_ID = 100;29const USDT_ASSET_ID = 100;29const ASSET_METADATA_DECIMALS = 18;30const USDT_ASSET_METADATA_DECIMALS = 18;30const ASSET_METADATA_NAME = 'USDT';31const USDT_ASSET_METADATA_NAME = 'USDT';31const ASSET_METADATA_DESCRIPTION = 'USDT';32const USDT_ASSET_METADATA_DESCRIPTION = 'USDT';32const ASSET_METADATA_MINIMAL_BALANCE = 1n;33const USDT_ASSET_METADATA_MINIMAL_BALANCE = 1n;333434const RELAY_DECIMALS = 12;35const RELAY_DECIMALS = 12;35const WESTMINT_DECIMALS = 12;36const WESTMINT_DECIMALS = 12;363737const TRANSFER_AMOUNT = 1_000_000_000_000_000_000n;38const TRANSFER_AMOUNT = 1_000_000_000_000_000_000n;383939// 10,000.00 (ten thousands) USDT40// 10,000.00 (ten thousands) USDT40const ASSET_AMOUNT = 1_000_000_000_000_000_000_000n;41const USDT_ASSET_AMOUNT = 1_000_000_000_000_000_000_000n;4243const testHelper = new XcmTestHelper('opal');414442describeXCM('[XCM] Integration test: Exchanging USDT with Westmint', () => {45describeXCM('[XCM] Integration test: Exchanging USDT with Westmint', () => {43 let alice: IKeyringPair;46 let alice: IKeyringPair;57 let balanceBobRelayTokenBefore: bigint;60 let balanceBobRelayTokenBefore: bigint;58 let balanceBobRelayTokenAfter: bigint;61 let balanceBobRelayTokenAfter: bigint;596263 let usdtCollectionId: number;64 let relayCollectionId: number;606561 before(async () => {66 before(async () => {62 await usingPlaygrounds(async (_helper, privateKey) => {67 await usingPlaygrounds(async (_helper, privateKey) => {63 alice = await privateKey('//Alice');68 alice = await privateKey('//Alice');64 bob = await privateKey('//Bob'); // funds donor69 bob = await privateKey('//Bob'); // funds donor7071 relayCollectionId = await testHelper.registerRelayNativeTokenOnUnique(alice);65 });72 });667367 await usingWestmintPlaygrounds(westmintUrl, async (helper) => {74 await usingWestmintPlaygrounds(westmintUrl, async (helper) => {68 // 350.00 (three hundred fifty) DOT69 const fundingAmount = 3_500_000_000_000n;75 const assetInfo = await helper.assets.assetInfo(USDT_ASSET_ID);7076 if(assetInfo == null) {71 await helper.assets.create(alice, ASSET_ID, alice.address, ASSET_METADATA_MINIMAL_BALANCE);77 await helper.assets.create(78 alice,79 USDT_ASSET_ID,80 alice.address,81 USDT_ASSET_METADATA_MINIMAL_BALANCE,82 );72 await helper.assets.setMetadata(alice, ASSET_ID, ASSET_METADATA_NAME, ASSET_METADATA_DESCRIPTION, ASSET_METADATA_DECIMALS);83 await helper.assets.setMetadata(84 alice,85 USDT_ASSET_ID,86 USDT_ASSET_METADATA_NAME,87 USDT_ASSET_METADATA_DESCRIPTION,88 USDT_ASSET_METADATA_DECIMALS,89 );90 } else {91 console.log('The USDT asset is already registered on AssetHub');92 }9373 await helper.assets.mint(alice, ASSET_ID, alice.address, ASSET_AMOUNT);94 await helper.assets.mint(95 alice,96 USDT_ASSET_ID,97 alice.address,98 USDT_ASSET_AMOUNT,99 );100101 const sovereignFundingAmount = 3_500_000_000n;7410275 // funding parachain sovereing account (Parachain: 2095)103 // funding parachain sovereing account (Parachain: 2095)76 const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(UNIQUE_CHAIN);104 const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(UNIQUE_CHAIN);77 await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, fundingAmount);105 await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, sovereignFundingAmount);78 });106 });798010781 await usingPlaygrounds(async (helper) => {108 await usingPlaygrounds(async (helper) => {82 const location = {109 const location = {83 V2: {84 parents: 1,110 parents: 1,85 interior: {X3: [111 interior: {X3: [86 {112 {90 PalletInstance: STATEMINE_PALLET_INSTANCE,116 PalletInstance: STATEMINE_PALLET_INSTANCE,91 },117 },92 {118 {93 GeneralIndex: ASSET_ID,119 GeneralIndex: USDT_ASSET_ID,94 },120 },95 ]},121 ]},96 },97 };122 };9899 const metadata =123 const assetId = {Concrete: location};100 {124101 name: ASSET_ID,125 if(await helper.foreignAssets.foreignCollectionId(assetId) == null) {102 symbol: ASSET_METADATA_NAME,126 const tokenPrefix = USDT_ASSET_METADATA_NAME;103 decimals: ASSET_METADATA_DECIMALS,104 minimalBalance: ASSET_METADATA_MINIMAL_BALANCE,105 };106 await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata);127 await helper.getSudo().foreignAssets.register(128 alice,129 assetId,130 USDT_ASSET_METADATA_NAME,131 tokenPrefix,132 {Fungible: USDT_ASSET_METADATA_DECIMALS},133 );134 } else {135 console.log('Foreign collection is already registered on Opal');136 }137107 balanceOpalBefore = await helper.balance.getSubstrate(alice.address);138 balanceOpalBefore = await helper.balance.getSubstrate(alice.address);139 usdtCollectionId = await helper.foreignAssets.foreignCollectionId(assetId);108 });140 });109110141189 PalletInstance: STATEMINE_PALLET_INSTANCE,220 PalletInstance: STATEMINE_PALLET_INSTANCE,190 },221 },191 {222 {192 GeneralIndex: ASSET_ID,223 GeneralIndex: USDT_ASSET_ID,193 },224 },194 ]},225 ]},195 },226 },221 // ensure that asset has been delivered251 // ensure that asset has been delivered222 await helper.wait.newBlocks(3);252 await helper.wait.newBlocks(3);223253224 // expext collection id will be with id 1225 const free = await helper.ft.getBalance(1, {Substrate: alice.address});254 const free = await helper.ft.getBalance(usdtCollectionId, {Substrate: alice.address});226255227 balanceOpalAfter = await helper.balance.getSubstrate(alice.address);256 balanceOpalAfter = await helper.balance.getSubstrate(alice.address);228257229 console.log(258 console.log(230 '[Westmint -> Opal] transaction fees on Opal: %s USDT',259 '[Westmint -> Opal] transaction fees on Opal: %s USDT',231 helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, ASSET_METADATA_DECIMALS),260 helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, USDT_ASSET_METADATA_DECIMALS),232 );261 );233 console.log(262 console.log(234 '[Westmint -> Opal] transaction fees on Opal: %s OPL',263 '[Westmint -> Opal] transaction fees on Opal: %s OPL',261290262 const currencies: [any, bigint][] = [291 const currencies: [any, bigint][] = [263 [292 [264 {265 ForeignAssetId: 0,266 },293 usdtCollectionId,267 //10_000_000_000_000_000n,268 TRANSFER_AMOUNT,294 TRANSFER_AMOUNT,269 ],295 ],270 [296 [271 {272 NativeAssetId: 'Parent',273 },297 relayCollectionId,274 400_000_000_000_000n,298 400_000_000_000_000n,275 ],299 ],276 ];300 ];288312289 // The USDT token never paid fees. Its amount not changed from begin value.313 // The USDT token never paid fees. Its amount not changed from begin value.290 // Also check that xcm transfer has been succeeded314 // Also check that xcm transfer has been succeeded291 expect((await helper.assets.account(ASSET_ID, alice.address))! == ASSET_AMOUNT).to.be.true;315 expect((await helper.assets.account(USDT_ASSET_ID, alice.address))! == USDT_ASSET_AMOUNT).to.be.true;292 });316 });293 });317 });294318295 itSub('Should connect and send Relay token to Unique', async ({helper}) => {319 itSub('Should connect and send Relay token to Unique', async ({helper}) => {296 const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n;320 const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n;297321298 balanceBobBefore = await helper.balance.getSubstrate(bob.address);322 balanceBobBefore = await helper.balance.getSubstrate(bob.address);299 balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});323 balanceBobRelayTokenBefore = await helper.ft.getBalance(relayCollectionId, {Substrate: bob.address});300324301 // Providing the relay currency to the unique sender account325 // Providing the relay currency to the unique sender account302 await usingRelayPlaygrounds(relayUrl, async (helper) => {326 await usingRelayPlaygrounds(relayUrl, async (helper) => {345 await helper.wait.newBlocks(3);369 await helper.wait.newBlocks(3);346370347 balanceBobAfter = await helper.balance.getSubstrate(bob.address);371 balanceBobAfter = await helper.balance.getSubstrate(bob.address);348 balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});372 balanceBobRelayTokenAfter = await helper.ft.getBalance(relayCollectionId, {Substrate: bob.address});349373350 const wndFee = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;374 const wndFee = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;351 console.log(375 console.log(383407384 const currencies: any = [408 const currencies: any = [385 [409 [386 {387 NativeAssetId: 'Parent',388 },410 relayCollectionId,389 50_000_000_000_000_000n,411 50_000_000_000_000_000n,390 ],412 ],391 ];413 ];js-packages/tests/xcm/xcmQuartz.test.tsdiffbeforeafterboth19import {DevUniqueHelper, Event} from '@unique/playgrounds/unique.dev.js';19import {DevUniqueHelper, Event} from '@unique/playgrounds/unique.dev.js';20import {STATEMINE_CHAIN, QUARTZ_CHAIN, KARURA_CHAIN, MOONRIVER_CHAIN, SHIDEN_CHAIN, STATEMINE_DECIMALS, KARURA_DECIMALS, QTZ_DECIMALS, RELAY_DECIMALS, SHIDEN_DECIMALS, karuraUrl, moonriverUrl, relayUrl, shidenUrl, statemineUrl} from './xcm.types.js';20import {STATEMINE_CHAIN, QUARTZ_CHAIN, KARURA_CHAIN, MOONRIVER_CHAIN, SHIDEN_CHAIN, STATEMINE_DECIMALS, KARURA_DECIMALS, QTZ_DECIMALS, RELAY_DECIMALS, SHIDEN_DECIMALS, karuraUrl, moonriverUrl, relayUrl, shidenUrl, statemineUrl} from './xcm.types.js';21import {hexToString} from '@polkadot/util';21import {hexToString} from '@polkadot/util';2222import {XcmTestHelper} from './xcm.types.js';23242325const STATEMINE_PALLET_INSTANCE = 50;24const STATEMINE_PALLET_INSTANCE = 50;393840const SAFE_XCM_VERSION = 2;39const SAFE_XCM_VERSION = 2;4041const testHelper = new XcmTestHelper('quartz');414242describeXCM('[XCM] Integration test: Exchanging USDT with Statemine', () => {43describeXCM('[XCM] Integration test: Exchanging USDT with Statemine', () => {43 let alice: IKeyringPair;44 let alice: IKeyringPair;57 let balanceBobRelayTokenBefore: bigint;58 let balanceBobRelayTokenBefore: bigint;58 let balanceBobRelayTokenAfter: bigint;59 let balanceBobRelayTokenAfter: bigint;596061 let usdtCollectionId: number;62 let relayCollectionId: number;606361 before(async () => {64 before(async () => {62 await usingPlaygrounds(async (helper, privateKey) => {65 await usingPlaygrounds(async (helper, privateKey) => {66 // Set the default version to wrap the first message to other chains.69 // Set the default version to wrap the first message to other chains.67 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);70 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);7172 relayCollectionId = await testHelper.registerRelayNativeTokenOnUnique(alice);68 });73 });697470 await usingRelayPlaygrounds(relayUrl, async (helper) => {75 await usingRelayPlaygrounds(relayUrl, async (helper) => {74 });79 });758076 await usingStateminePlaygrounds(statemineUrl, async (helper) => {81 await usingStateminePlaygrounds(statemineUrl, async (helper) => {77 const sovereignFundingAmount = 3_500_000_000n;82 const assetInfo = await helper.assets.assetInfo(USDT_ASSET_ID);7883 if(assetInfo == null) {79 await helper.assets.create(84 await helper.assets.create(80 alice,85 alice,81 USDT_ASSET_ID,86 USDT_ASSET_ID,89 USDT_ASSET_METADATA_DESCRIPTION,94 USDT_ASSET_METADATA_DESCRIPTION,90 USDT_ASSET_METADATA_DECIMALS,95 USDT_ASSET_METADATA_DECIMALS,91 );96 );97 } else {98 console.log('The USDT asset is already registered on AssetHub');99 }10092 await helper.assets.mint(101 await helper.assets.mint(93 alice,102 alice,96 USDT_ASSET_AMOUNT,105 USDT_ASSET_AMOUNT,97 );106 );107108 const sovereignFundingAmount = 3_500_000_000n;9810999 // funding parachain sovereing account on Statemine(t).110 // funding parachain sovereing account on Statemine(t).100 // The sovereign account should be created before any action111 // The sovereign account should be created before any action106117107 await usingPlaygrounds(async (helper) => {118 await usingPlaygrounds(async (helper) => {108 const location = {119 const location = {109 V2: {110 parents: 1,120 parents: 1,111 interior: {X3: [121 interior: {X3: [112 {122 {119 GeneralIndex: USDT_ASSET_ID,129 GeneralIndex: USDT_ASSET_ID,120 },130 },121 ]},131 ]},122 },123 };132 };124125 const metadata =133 const assetId = {Concrete: location};126 {134127 name: USDT_ASSET_ID,135 if(await helper.foreignAssets.foreignCollectionId(assetId) == null) {128 symbol: USDT_ASSET_METADATA_NAME,136 const tokenPrefix = USDT_ASSET_METADATA_NAME;129 decimals: USDT_ASSET_METADATA_DECIMALS,130 minimalBalance: USDT_ASSET_METADATA_MINIMAL_BALANCE,131 };132 await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata);137 await helper.getSudo().foreignAssets.register(alice, assetId, USDT_ASSET_METADATA_NAME, tokenPrefix, {Fungible: USDT_ASSET_METADATA_DECIMALS});138 } else {139 console.log('Foreign collection is already registered on Quartz');140 }141133 balanceQuartzBefore = await helper.balance.getSubstrate(alice.address);142 balanceQuartzBefore = await helper.balance.getSubstrate(alice.address);143 usdtCollectionId = await helper.foreignAssets.foreignCollectionId(assetId);134 });144 });135145136146248 // ensure that asset has been delivered258 // ensure that asset has been delivered249 await helper.wait.newBlocks(3);259 await helper.wait.newBlocks(3);250260251 // expext collection id will be with id 1252 const free = await helper.ft.getBalance(1, {Substrate: alice.address});261 const free = await helper.ft.getBalance(usdtCollectionId, {Substrate: alice.address});253262254 balanceQuartzAfter = await helper.balance.getSubstrate(alice.address);263 balanceQuartzAfter = await helper.balance.getSubstrate(alice.address);255264288 const relayFee = 400_000_000_000_000n;297 const relayFee = 400_000_000_000_000n;289 const currencies: [any, bigint][] = [298 const currencies: [any, bigint][] = [290 [299 [291 {292 ForeignAssetId: 0,293 },300 usdtCollectionId,294 TRANSFER_AMOUNT,301 TRANSFER_AMOUNT,295 ],302 ],296 [303 [297 {298 NativeAssetId: 'Parent',299 },304 relayCollectionId,300 relayFee,305 relayFee,301 ],306 ],302 ];307 ];321326322 itSub('Should connect and send Relay token to Quartz', async ({helper}) => {327 itSub('Should connect and send Relay token to Quartz', async ({helper}) => {323 balanceBobBefore = await helper.balance.getSubstrate(bob.address);328 balanceBobBefore = await helper.balance.getSubstrate(bob.address);324 balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});329 balanceBobRelayTokenBefore = await helper.ft.getBalance(relayCollectionId, {Substrate: bob.address});325330326 await usingRelayPlaygrounds(relayUrl, async (helper) => {331 await usingRelayPlaygrounds(relayUrl, async (helper) => {327 const destination = {332 const destination = {369 await helper.wait.newBlocks(3);374 await helper.wait.newBlocks(3);370375371 balanceBobAfter = await helper.balance.getSubstrate(bob.address);376 balanceBobAfter = await helper.balance.getSubstrate(bob.address);372 balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});377 balanceBobRelayTokenAfter = await helper.ft.getBalance(relayCollectionId, {Substrate: bob.address});373378374 const wndFeeOnQuartz = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;379 const wndFeeOnQuartz = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;375 const wndDiffOnQuartz = balanceBobRelayTokenAfter - balanceBobRelayTokenBefore;380 const wndDiffOnQuartz = balanceBobRelayTokenAfter - balanceBobRelayTokenBefore;409414410 const currencies: any = [415 const currencies: any = [411 [416 [412 {413 NativeAssetId: 'Parent',414 },417 relayCollectionId,415 TRANSFER_AMOUNT_RELAY,418 TRANSFER_AMOUNT_RELAY,416 ],419 ],417 ];420 ];1018 });1021 });101910221020 itSub('Should connect and send QTZ to Moonriver', async ({helper}) => {1023 itSub('Should connect and send QTZ to Moonriver', async ({helper}) => {1021 const currencyId = {1024 const currencyId = 0;1022 NativeAssetId: 'Here',1023 };1024 const dest = {1025 const dest = {1025 V2: {1026 V2: {1026 parents: 1,1027 parents: 1,js-packages/tests/xcm/xcmUnique.test.tsdiffbeforeafterboth20import {Event} from '@unique/playgrounds/unique.dev.js';20import {Event} from '@unique/playgrounds/unique.dev.js';21import {hexToString, nToBigInt} from '@polkadot/util';21import {hexToString, nToBigInt} from '@polkadot/util';22import {ACALA_CHAIN, ASTAR_CHAIN, MOONBEAM_CHAIN, POLKADEX_CHAIN, SAFE_XCM_VERSION, STATEMINT_CHAIN, UNIQUE_CHAIN, expectFailedToTransact, expectUntrustedReserveLocationFail, uniqueAssetId, uniqueVersionedMultilocation} from './xcm.types.js';22import {ACALA_CHAIN, ASTAR_CHAIN, MOONBEAM_CHAIN, POLKADEX_CHAIN, SAFE_XCM_VERSION, STATEMINT_CHAIN, UNIQUE_CHAIN, expectFailedToTransact, expectUntrustedReserveLocationFail, uniqueAssetId, uniqueVersionedMultilocation} from './xcm.types.js';2323import {XcmTestHelper} from './xcm.types.js';242425const STATEMINT_PALLET_INSTANCE = 50;25const STATEMINT_PALLET_INSTANCE = 50;262650const USDT_ASSET_METADATA_MINIMAL_BALANCE = 1n;50const USDT_ASSET_METADATA_MINIMAL_BALANCE = 1n;51const USDT_ASSET_AMOUNT = 10_000_000_000_000_000_000_000_000n;51const USDT_ASSET_AMOUNT = 10_000_000_000_000_000_000_000_000n;5253const testHelper = new XcmTestHelper('unique');525453describeXCM('[XCM] Integration test: Exchanging USDT with Statemint', () => {55describeXCM('[XCM] Integration test: Exchanging USDT with Statemint', () => {54 let alice: IKeyringPair;56 let alice: IKeyringPair;68 let balanceBobRelayTokenBefore: bigint;70 let balanceBobRelayTokenBefore: bigint;69 let balanceBobRelayTokenAfter: bigint;71 let balanceBobRelayTokenAfter: bigint;707273 let usdtCollectionId: number;74 let relayCollectionId: number;717572 before(async () => {76 before(async () => {73 await usingPlaygrounds(async (helper, privateKey) => {77 await usingPlaygrounds(async (helper, privateKey) => {77 // Set the default version to wrap the first message to other chains.81 // Set the default version to wrap the first message to other chains.78 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);82 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);8384 relayCollectionId = await testHelper.registerRelayNativeTokenOnUnique(alice);79 });85 });808681 await usingRelayPlaygrounds(relayUrl, async (helper) => {87 await usingRelayPlaygrounds(relayUrl, async (helper) => {85 });91 });869287 await usingStatemintPlaygrounds(statemintUrl, async (helper) => {93 await usingStatemintPlaygrounds(statemintUrl, async (helper) => {88 const sovereignFundingAmount = 3_500_000_000n;94 const assetInfo = await helper.assets.assetInfo(USDT_ASSET_ID);8995 if(assetInfo == null) {90 await helper.assets.create(96 await helper.assets.create(91 alice,97 alice,92 USDT_ASSET_ID,98 USDT_ASSET_ID,100 USDT_ASSET_METADATA_DESCRIPTION,106 USDT_ASSET_METADATA_DESCRIPTION,101 USDT_ASSET_METADATA_DECIMALS,107 USDT_ASSET_METADATA_DECIMALS,102 );108 );109 } else {110 console.log('The USDT asset is already registered on AssetHub');111 }112103 await helper.assets.mint(113 await helper.assets.mint(104 alice,114 alice,107 USDT_ASSET_AMOUNT,117 USDT_ASSET_AMOUNT,108 );118 );119120 const sovereignFundingAmount = 3_500_000_000n;109121110 // funding parachain sovereing account on Statemint.122 // funding parachain sovereing account on Statemint.111 // The sovereign account should be created before any action123 // The sovereign account should be created before any action117129118 await usingPlaygrounds(async (helper) => {130 await usingPlaygrounds(async (helper) => {119 const location = {131 const location = {120 V2: {121 parents: 1,132 parents: 1,122 interior: {X3: [133 interior: {X3: [123 {134 {130 GeneralIndex: USDT_ASSET_ID,141 GeneralIndex: USDT_ASSET_ID,131 },142 },132 ]},143 ]},133 },134 };144 };135136 const metadata =145 const assetId = {Concrete: location};137 {146138 name: USDT_ASSET_ID,147 if(await helper.foreignAssets.foreignCollectionId(assetId) == null) {139 symbol: USDT_ASSET_METADATA_NAME,148 const tokenPrefix = USDT_ASSET_METADATA_NAME;140 decimals: USDT_ASSET_METADATA_DECIMALS,141 minimalBalance: USDT_ASSET_METADATA_MINIMAL_BALANCE,142 };143 await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata);149 await helper.getSudo().foreignAssets.register(alice, assetId, USDT_ASSET_METADATA_NAME, tokenPrefix, {Fungible: USDT_ASSET_METADATA_DECIMALS});150 } else {151 console.log('Foreign collection is already registered on Unique');152 }153144 balanceUniqueBefore = await helper.balance.getSubstrate(alice.address);154 balanceUniqueBefore = await helper.balance.getSubstrate(alice.address);155 usdtCollectionId = await helper.foreignAssets.foreignCollectionId(assetId);145 });156 });146157147158259 // ensure that asset has been delivered270 // ensure that asset has been delivered260 await helper.wait.newBlocks(3);271 await helper.wait.newBlocks(3);261272262 // expext collection id will be with id 1263 const free = await helper.ft.getBalance(1, {Substrate: alice.address});273 const free = await helper.ft.getBalance(usdtCollectionId, {Substrate: alice.address});264274265 balanceUniqueAfter = await helper.balance.getSubstrate(alice.address);275 balanceUniqueAfter = await helper.balance.getSubstrate(alice.address);266276299 const relayFee = 400_000_000_000_000n;309 const relayFee = 400_000_000_000_000n;300 const currencies: [any, bigint][] = [310 const currencies: [any, bigint][] = [301 [311 [302 {303 ForeignAssetId: 0,304 },312 usdtCollectionId,305 TRANSFER_AMOUNT,313 TRANSFER_AMOUNT,306 ],314 ],307 [315 [308 {309 NativeAssetId: 'Parent',310 },316 relayCollectionId,311 relayFee,317 relayFee,312 ],318 ],313 ];319 ];332338333 itSub('Should connect and send Relay token to Unique', async ({helper}) => {339 itSub('Should connect and send Relay token to Unique', async ({helper}) => {334 balanceBobBefore = await helper.balance.getSubstrate(bob.address);340 balanceBobBefore = await helper.balance.getSubstrate(bob.address);335 balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});341 balanceBobRelayTokenBefore = await helper.ft.getBalance(relayCollectionId, {Substrate: bob.address});336342337 await usingRelayPlaygrounds(relayUrl, async (helper) => {343 await usingRelayPlaygrounds(relayUrl, async (helper) => {338 const destination = {344 const destination = {380 await helper.wait.newBlocks(3);386 await helper.wait.newBlocks(3);381387382 balanceBobAfter = await helper.balance.getSubstrate(bob.address);388 balanceBobAfter = await helper.balance.getSubstrate(bob.address);383 balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});389 balanceBobRelayTokenAfter = await helper.ft.getBalance(relayCollectionId, {Substrate: bob.address});384390385 const wndFeeOnUnique = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;391 const wndFeeOnUnique = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;386 const wndDiffOnUnique = balanceBobRelayTokenAfter - balanceBobRelayTokenBefore;392 const wndDiffOnUnique = balanceBobRelayTokenAfter - balanceBobRelayTokenBefore;420426421 const currencies: any = [427 const currencies: any = [422 [428 [423 {424 NativeAssetId: 'Parent',425 },429 relayCollectionId,426 TRANSFER_AMOUNT_RELAY,430 TRANSFER_AMOUNT_RELAY,427 ],431 ],428 ];432 ];1275 });1279 });127612801277 itSub('Should connect and send UNQ to Moonbeam', async ({helper}) => {1281 itSub('Should connect and send UNQ to Moonbeam', async ({helper}) => {1278 const currencyId = {1282 const currencyId = 0;1279 NativeAssetId: 'Here',1280 };1281 const dest = {1283 const dest = {1282 V2: {1284 V2: {1283 parents: 1,1285 parents: 1,node/cli/src/chain_spec.rsdiffbeforeafterboth167 .map(|k| (k, 1 << 100))167 .map(|k| (k, 1 << 100))168 .collect(),168 .collect(),169 },169 },170 tokens: TokensConfig { balances: vec![] },171 sudo: SudoConfig {170 sudo: SudoConfig {172 key: Some($root_key),171 key: Some($root_key),173 },172 },231 .map(|k| (k, 1 << 100))230 .map(|k| (k, 1 << 100))232 .collect(),231 .collect(),233 },232 },234 tokens: TokensConfig { balances: vec![] },235 sudo: SudoConfig {233 sudo: SudoConfig {236 key: Some($root_key),234 key: Some($root_key),237 },235 },pallets/balances-adapter/src/common.rsdiffbeforeafterboth334use frame_support::{ensure, fail, weights::Weight};4use frame_support::{5 ensure, fail,6 traits::tokens::{fungible::Mutate, Fortitude, Precision},7 weights::Weight,8};5use pallet_balances::{weights::SubstrateWeight as BalancesWeight, WeightInfo};9use pallet_balances::{weights::SubstrateWeight as BalancesWeight, WeightInfo};6use pallet_common::{CommonCollectionOperations, CommonWeightInfo, Error as CommonError};10use pallet_common::{11 erc::CrossAccountId, CommonCollectionOperations, CommonWeightInfo, Error as CommonError,12};7use up_data_structs::TokenId;13use up_data_structs::{budget::Budget, TokenId};8149use crate::{Config, NativeFungibleHandle, Pallet};15use crate::{Config, NativeFungibleHandle, Pallet};1016332 0338 0333 }339 }334340335 fn refungible_extensions(&self) -> Option<&dyn pallet_common::RefungibleExtensions<T>> {341 fn xcm_extensions(&self) -> Option<&dyn pallet_common::XcmExtensions<T>> {336 None342 Some(self)337 }343 }338344339 fn set_allowance_for_all(345 fn set_allowance_for_all(361 }367 }362}368}369370impl<T: Config> pallet_common::XcmExtensions<T> for NativeFungibleHandle<T> {371 fn create_item_internal(372 &self,373 _depositor: &<T>::CrossAccountId,374 to: <T>::CrossAccountId,375 data: up_data_structs::CreateItemData,376 _nesting_budget: &dyn Budget,377 ) -> Result<TokenId, sp_runtime::DispatchError> {378 match &data {379 up_data_structs::CreateItemData::Fungible(fungible_data) => {380 T::Mutate::mint_into(381 to.as_sub(),382 fungible_data383 .value384 .try_into()385 .map_err(|_| sp_runtime::ArithmeticError::Overflow)?,386 )?;387388 Ok(TokenId::default())389 }390 _ => {391 fail!(<CommonError<T>>::NotFungibleDataUsedToMintFungibleCollectionToken)392 }393 }394 }395396 fn transfer_item_internal(397 &self,398 _depositor: &<T>::CrossAccountId,399 from: &<T>::CrossAccountId,400 to: &<T>::CrossAccountId,401 token: TokenId,402 amount: u128,403 _nesting_budget: &dyn Budget,404 ) -> sp_runtime::DispatchResult {405 ensure!(406 token == TokenId::default(),407 <CommonError<T>>::FungibleItemsHaveNoId408 );409410 <Pallet<T>>::transfer(from, to, amount)411 .map(|_| ())412 .map_err(|post_info| post_info.error)413 }414415 fn burn_item_internal(416 &self,417 from: T::CrossAccountId,418 token: TokenId,419 amount: u128,420 ) -> sp_runtime::DispatchResult {421 ensure!(422 token == TokenId::default(),423 <CommonError<T>>::FungibleItemsHaveNoId424 );425426 T::Mutate::burn_from(427 from.as_sub(),428 amount429 .try_into()430 .map_err(|_| sp_runtime::ArithmeticError::Overflow)?,431 Precision::Exact,432 Fortitude::Polite,433 )?;434435 Ok(())436 }437}363438pallets/common/src/benchmarking.rsdiffbeforeafterboth33 MAX_COLLECTION_NAME_LENGTH, MAX_PROPERTIES_PER_ITEM, MAX_TOKEN_PREFIX_LENGTH,33 MAX_COLLECTION_NAME_LENGTH, MAX_PROPERTIES_PER_ITEM, MAX_TOKEN_PREFIX_LENGTH,34};34};353536use crate::{BenchmarkPropertyWriter, CollectionHandle, Config, Pallet};36use crate::{BenchmarkPropertyWriter, CollectionHandle, CollectionIssuer, Config, Pallet};373738const SEED: u32 = 1;38const SEED: u32 = 1;3939120 create_collection_raw(120 create_collection_raw(121 owner,121 owner,122 CollectionMode::NFT,122 CollectionMode::NFT,123 |owner: T::CrossAccountId, data| <Pallet<T>>::init_collection(owner.clone(), owner, data),123 |owner: T::CrossAccountId, data| {124 <Pallet<T>>::init_collection(owner.clone(), CollectionIssuer::User(owner), data)125 },124 |h| h,126 |h| h,125 )127 )126}128}pallets/common/src/dispatch.rsdiffbeforeafterboth11use sp_weights::Weight;11use sp_weights::Weight;12use up_data_structs::{CollectionId, CreateCollectionData};12use up_data_structs::{CollectionId, CreateCollectionData};131314use crate::{pallet::Config, CommonCollectionOperations};14use crate::{pallet::Config, CollectionIssuer, CommonCollectionOperations};151516// TODO: move to benchmarking16// TODO: move to benchmarking17/// Price of [`dispatch_tx`] call with noop `call` argument17/// Price of [`dispatch_tx`] call with noop `call` argument69 /// Check if the collection is internal.69 /// Check if the collection is internal.70 fn check_is_internal(&self) -> DispatchResult;70 fn check_is_internal(&self) -> DispatchResult;717172 /// Create a collection. The collection will be created according to the value of [`data.mode`](CreateCollectionData::mode).72 /// Create a regular collection. The collection will be created according to the value of [`data.mode`](CreateCollectionData::mode).73 ///73 ///74 /// * `sender` - The user who will become the owner of the collection.74 /// * `sender` - The user who will become the owner of the collection.75 /// * `issuer` - An entity that creates the collection.75 /// * `data` - Description of the created collection.76 /// * `data` - Description of the created collection.76 fn create(77 fn create(77 sender: T::CrossAccountId,78 sender: T::CrossAccountId,78 payer: T::CrossAccountId,79 issuer: CollectionIssuer<T::CrossAccountId>,79 data: CreateCollectionData<T::CrossAccountId>,80 data: CreateCollectionData<T::CrossAccountId>,80 ) -> Result<CollectionId, DispatchError>;81 ) -> Result<CollectionId, DispatchError>;8182pallets/common/src/lib.rsdiffbeforeafterboth80use sp_std::vec::Vec;80use sp_std::vec::Vec;81use sp_weights::Weight;81use sp_weights::Weight;82use up_data_structs::{82use up_data_structs::{83 budget::Budget, AccessMode, Collection, CollectionId, CollectionLimits, CollectionMode,83 budget::Budget, mapping::TokenAddressMapping, AccessMode, Collection, CollectionId,84 CollectionPermissions, CollectionProperties as CollectionPropertiesT, CollectionStats,84 CollectionLimits, CollectionMode, CollectionPermissions,85 CreateCollectionData, CreateItemData, CreateItemExData, PhantomType, PropertiesError,85 CollectionProperties as CollectionPropertiesT, CollectionStats, CreateCollectionData,86 PropertiesPermissionMap, Property, PropertyKey, PropertyKeyPermission, PropertyPermission,86 CreateItemData, CreateItemExData, PhantomType, PropertiesError, PropertiesPermissionMap,787 /// Fungible tokens hold no ID, and the default value of TokenId for a fungible collection is 0.788 /// Fungible tokens hold no ID, and the default value of TokenId for a fungible collection is 0.788 FungibleItemsHaveNoId,789 FungibleItemsHaveNoId,790791 /// Not Fungible item data used to mint in Fungible collection.792 NotFungibleDataUsedToMintFungibleCollectionToken,789 }793 }790794791 /// Storage of the count of created collections. Essentially contains the last collection ID.795 /// Storage of the count of created collections. Essentially contains the last collection ID.940 }944 }941}945}946947/// An issuer of a collection.948pub enum CollectionIssuer<CrossAccountId> {949 /// A user who creates the collection.950 User(CrossAccountId),951952 /// The internal mechanisms are creating the collection.953 Internals,954}942955943fn check_token_permissions<T: Config>(956fn check_token_permissions<T: Config>(944 collection_admin_permitted: bool,957 collection_admin_permitted: bool,1121}1134}112211351123impl<T: Config> Pallet<T> {1136impl<T: Config> Pallet<T> {1124 /// Create new collection.1137 /// Create new collection.1125 ///1138 ///1126 /// * `owner` - The owner of the collection.1139 /// * `owner` - The owner of the collection.1127 /// * `data` - Description of the created collection.1140 /// * `issuer` - An entity that creates the collection.1128 /// * `flags` - Extra flags to store.1141 /// * `is_special_collection` -- Whether this collection is a special one, i.e. can have special flags set.1129 pub fn init_collection(1130 owner: T::CrossAccountId,1131 payer: T::CrossAccountId,1132 data: CreateCollectionData<T::CrossAccountId>,1133 ) -> Result<CollectionId, DispatchError> {1134 ensure!(data.flags.is_allowed_for_user(), <Error<T>>::NoPermission);1135 Self::init_collection_internal(owner, payer, data)1136 }11371138 /// Initializes the collection with ForeignCollection flag. Returns [CollectionId] on success, [DispatchError] otherwise.1139 pub fn init_foreign_collection(1142 pub fn init_collection(1140 owner: T::CrossAccountId,1143 owner: T::CrossAccountId,1141 payer: T::CrossAccountId,1144 issuer: CollectionIssuer<T::CrossAccountId>,1142 mut data: CreateCollectionData<T::CrossAccountId>,1145 data: CreateCollectionData<T::CrossAccountId>,1143 ) -> Result<CollectionId, DispatchError> {1146 ) -> Result<CollectionId, DispatchError> {1144 data.flags.foreign = true;1147 match issuer {1145 let id = Self::init_collection_internal(owner, payer, data)?;1148 CollectionIssuer::User(payer) => {1146 Ok(id)1149 ensure!(data.flags.is_allowed_for_user(), <Error<T>>::NoPermission);1147 }115011481151 // Take a (non-refundable) deposit of collection creation1149 fn init_collection_internal(1152 let mut imbalance = <Debt<T::AccountId, <T as Config>::Currency>>::zero();1153 imbalance.subsume(<T as Config>::Currency::deposit(1154 &T::TreasuryAccountId::get(),1155 T::CollectionCreationPrice::get(),1156 Precision::Exact,1157 )?);1158 let credit = <T as Config>::Currency::settle(1159 payer.as_sub(),1160 imbalance,1161 Preservation::Preserve,1162 )1163 .map_err(|_| Error::<T>::NotSufficientFounds)?;11641165 debug_assert!(credit.peek().is_zero());1166 }1150 owner: T::CrossAccountId,1167 CollectionIssuer::Internals => {}1151 payer: T::CrossAccountId,1168 }1152 data: CreateCollectionData<T::CrossAccountId>,11691153 ) -> Result<CollectionId, DispatchError> {1154 {1170 {1155 ensure!(1171 ensure!(1156 data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH as usize,1172 data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH as usize,1225 );1241 );1226 <AdminAmount<T>>::insert(id, admin_amount);1242 <AdminAmount<T>>::insert(id, admin_amount);12271228 // Take a (non-refundable) deposit of collection creation1229 {1230 let mut imbalance = <Debt<T::AccountId, <T as Config>::Currency>>::zero();1231 imbalance.subsume(<T as Config>::Currency::deposit(1232 &T::TreasuryAccountId::get(),1233 T::CollectionCreationPrice::get(),1234 Precision::Exact,1235 )?);1236 let credit =1237 <T as Config>::Currency::settle(payer.as_sub(), imbalance, Preservation::Preserve)1238 .map_err(|_| Error::<T>::NotSufficientFounds)?;12391240 debug_assert!(credit.peek().is_zero())1241 }124212431243 <CreatedCollectionCount<T>>::put(created_count);1244 <CreatedCollectionCount<T>>::put(created_count);1244 <Pallet<T>>::deposit_event(Event::CollectionCreated(1245 <Pallet<T>>::deposit_event(Event::CollectionCreated(1255 );1256 );1256 <CollectionById<T>>::insert(id, collection);1257 <CollectionById<T>>::insert(id, collection);1257 Ok(id)1258 Ok(id)1258 }1259 }125912601260 /// Destroy collection.1261 /// Destroy collection.1261 ///1262 ///2293 ) -> u128;2294 ) -> u128;229422952295 /// Get extension for RFT collection.2296 /// Get extension for RFT collection.2296 fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>>;2297 fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>> {2298 None2299 }23002301 /// Get XCM extensions.2302 fn xcm_extensions(&self) -> Option<&dyn XcmExtensions<T>> {2303 None2304 }229723052298 /// The `operator` is allowed to transfer all tokens of the `owner` on their behalf.2306 /// The `operator` is allowed to transfer all tokens of the `owner` on their behalf.2299 /// * `owner` - Token owner2307 /// * `owner` - Token owner2333 ) -> DispatchResultWithPostInfo;2341 ) -> DispatchResultWithPostInfo;2334}2342}23432344/// XCM extensions for fungible and NFT collections2345pub trait XcmExtensions<T>2346where2347 T: Config,2348{2349 /// Does the token have children?2350 fn token_has_children(&self, _token: TokenId) -> bool {2351 false2352 }23532354 /// Create a collection's item using a transaction.2355 ///2356 /// This function performs additional XCM-related checks before the actual creation.2357 ///2358 /// The `transactional` attribute is needed because the inbound XCM messages2359 /// are processed in a non-transactional context.2360 /// To perform the needed logic, we use the internal pallets' functions2361 /// that are not inherently safe to use outside a transaction.2362 ///2363 /// This requirement is temporary until XCM message processing becomes transactional:2364 /// https://github.com/paritytech/polkadot-sdk/issues/4902365 #[transactional]2366 fn create_item(2367 &self,2368 depositor: &T::CrossAccountId,2369 to: T::CrossAccountId,2370 data: CreateItemData,2371 nesting_budget: &dyn Budget,2372 ) -> Result<TokenId, DispatchError> {2373 if T::CrossTokenAddressMapping::is_token_address(&to) {2374 return unsupported!(T);2375 }23762377 self.create_item_internal(depositor, to, data, nesting_budget)2378 }23792380 /// Create a collection's item.2381 fn create_item_internal(2382 &self,2383 depositor: &T::CrossAccountId,2384 to: T::CrossAccountId,2385 data: CreateItemData,2386 nesting_budget: &dyn Budget,2387 ) -> Result<TokenId, DispatchError>;23882389 /// Transfer an item from the `from` account to the `to` account using a transaction.2390 ///2391 /// This function performs additional XCM-related checks before the actual transfer.2392 ///2393 /// The `transactional` attribute is needed because the inbound XCM messages2394 /// are processed in a non-transactional context.2395 /// To perform the needed logic, we use the internal pallets' functions2396 /// that are not inherently safe to use outside a transaction.2397 ///2398 /// This requirement is temporary until XCM message processing becomes transactional:2399 /// https://github.com/paritytech/polkadot-sdk/issues/4902400 #[transactional]2401 fn transfer_item(2402 &self,2403 depositor: &T::CrossAccountId,2404 from: &T::CrossAccountId,2405 to: &T::CrossAccountId,2406 token: TokenId,2407 amount: u128,2408 nesting_budget: &dyn Budget,2409 ) -> DispatchResult {2410 if T::CrossTokenAddressMapping::is_token_address(to) {2411 return unsupported!(T);2412 }24132414 if self.token_has_children(token) {2415 return unsupported!(T);2416 }24172418 self.transfer_item_internal(depositor, from, to, token, amount, nesting_budget)2419 }24202421 /// Transfer an item from the `from` account to the `to` account.2422 fn transfer_item_internal(2423 &self,2424 depositor: &T::CrossAccountId,2425 from: &T::CrossAccountId,2426 to: &T::CrossAccountId,2427 token: TokenId,2428 amount: u128,2429 nesting_budget: &dyn Budget,2430 ) -> DispatchResult;24312432 /// Burn a collection's item using a transaction.2433 ///2434 /// The `transactional` attribute is needed because the inbound XCM messages2435 /// are processed in a non-transactional context.2436 /// To perform the needed logic, we use the internal pallets' functions2437 /// that are not inherently safe to use outside a transaction.2438 ///2439 /// This requirement is temporary until XCM message processing becomes transactional:2440 /// https://github.com/paritytech/polkadot-sdk/issues/4902441 #[transactional]2442 fn burn_item(&self, from: T::CrossAccountId, token: TokenId, amount: u128) -> DispatchResult {2443 self.burn_item_internal(from, token, amount)2444 }24452446 /// Burn a collection's item.2447 fn burn_item_internal(2448 &self,2449 from: T::CrossAccountId,2450 token: TokenId,2451 amount: u128,2452 ) -> DispatchResult;2453}233524542336/// Merge [`DispatchResult`] with [`Weight`] into [`DispatchResultWithPostInfo`].2455/// Merge [`DispatchResult`] with [`Weight`] into [`DispatchResultWithPostInfo`].2337///2456///pallets/foreign-assets/Cargo.tomldiffbeforeafterboth12frame-support = { workspace = true }12frame-support = { workspace = true }13frame-system = { workspace = true }13frame-system = { workspace = true }14log = { workspace = true }14log = { workspace = true }15orml-tokens = { workspace = true }16pallet-balances = { features = ["insecure_zero_ed"], workspace = true }15pallet-balances = { features = ["insecure_zero_ed"], workspace = true }17pallet-common = { workspace = true }16pallet-common = { workspace = true }18pallet-fungible = { workspace = true }17pallet-fungible = { workspace = true }30 "frame-support/std",29 "frame-support/std",31 "frame-system/std",30 "frame-system/std",32 "log/std",31 "log/std",33 "orml-tokens/std",34 "pallet-balances/std",32 "pallet-balances/std",35 "pallet-common/std",33 "pallet-common/std",36 "pallet-fungible/std",34 "pallet-fungible/std",pallets/foreign-assets/src/benchmarking.rsdiffbeforeafterboth161617#![allow(missing_docs)]17#![allow(missing_docs)]181819use frame_benchmarking::{account, v2::*};19use frame_benchmarking::v2::*;20use frame_support::traits::Currency;20use frame_system::RawOrigin;21use frame_system::RawOrigin;21use pallet_common::benchmarking::{create_data, create_u16_data};22use sp_std::{boxed::Box, vec, vec::Vec};22use sp_std::{boxed::Box, vec};23use staging_xcm::{opaque::latest::Junction::Parachain, v3::Junctions::X1, VersionedMultiLocation};23use staging_xcm::prelude::*;24use up_data_structs::{MAX_COLLECTION_NAME_LENGTH, MAX_TOKEN_PREFIX_LENGTH};242525use super::{Call, Config, Pallet};26use super::{Call, Config, ForeignCollectionMode, Pallet};26use crate::AssetMetadata;2728fn bounded<T: TryFrom<Vec<u8>>>(slice: &[u8]) -> T {29 T::try_from(slice.to_vec())30 .map_err(|_| "slice doesn't fit")31 .unwrap()32}332734#[benchmarks]28#[benchmarks]35mod benchmarks {29mod benchmarks {3036 use super::*;31 use super::*;373238 #[benchmark]33 #[benchmark]39 fn register_foreign_asset() -> Result<(), BenchmarkError> {34 fn force_register_foreign_asset() -> Result<(), BenchmarkError> {40 let owner: T::AccountId = account("user", 0, 1);41 let location: VersionedMultiLocation = VersionedMultiLocation::from(X1(Parachain(1000)));35 let location =36 MultiLocation::from(X3(Parachain(1000), PalletInstance(42), GeneralIndex(1)));42 let metadata: AssetMetadata<37 let name = create_u16_data::<MAX_COLLECTION_NAME_LENGTH>();43 <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance,44 > = AssetMetadata {45 name: bounded(b"name"),46 symbol: bounded(b"symbol"),47 decimals: 18,48 minimal_balance: 1u32.into(),49 };50 let mut balance: <<T as Config>::Currency as Currency<38 let token_prefix = create_data::<MAX_TOKEN_PREFIX_LENGTH>();51 <T as frame_system::Config>::AccountId,52 >>::Balance = 4_000_000_000u32.into();53 balance = balance * balance;54 <T as Config>::Currency::make_free_balance_be(&owner, balance);39 let mode = ForeignCollectionMode::NFT;554056 #[extrinsic_call]41 #[extrinsic_call]57 _(42 _(58 RawOrigin::Root,43 RawOrigin::Root,59 owner,60 Box::new(location),44 Box::new(location.into()),61 Box::new(metadata),45 name,46 token_prefix,47 mode,62 );48 );634964 Ok(())50 Ok(())65 }51 }6667 #[benchmark]68 fn update_foreign_asset() -> Result<(), BenchmarkError> {69 let owner: T::AccountId = account("user", 0, 1);70 let location: VersionedMultiLocation = VersionedMultiLocation::from(X1(Parachain(2000)));71 let metadata: AssetMetadata<72 <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance,73 > = AssetMetadata {74 name: bounded(b"name"),75 symbol: bounded(b"symbol"),76 decimals: 18,77 minimal_balance: 1u32.into(),78 };79 let metadata2: AssetMetadata<80 <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance,81 > = AssetMetadata {82 name: bounded(b"name2"),83 symbol: bounded(b"symbol2"),84 decimals: 18,85 minimal_balance: 1u32.into(),86 };87 let mut balance: <<T as Config>::Currency as Currency<88 <T as frame_system::Config>::AccountId,89 >>::Balance = 4_000_000_000u32.into();90 balance = balance * balance;91 <T as Config>::Currency::make_free_balance_be(&owner, balance);92 Pallet::<T>::register_foreign_asset(93 RawOrigin::Root.into(),94 owner,95 Box::new(location.clone()),96 Box::new(metadata),97 )?;9899 #[extrinsic_call]100 _(RawOrigin::Root, 0, Box::new(location), Box::new(metadata2));101102 Ok(())103 }104}52}10553pallets/foreign-assets/src/impl_fungibles.rsdiffbeforeafterbothno changes
pallets/foreign-assets/src/lib.rsdiffbeforeafterboth14// 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/>.161617//! # Foreign assets17//! # Foreign Assets18//!18//!19//! - [`Config`]20//! - [`Call`]21//! - [`Pallet`]22//!23//! ## Overview19//! ## Overview24//!20//!25//! The foreign assests pallet provides functions for:21//! The Foreign Assets is a proxy that maps XCM operations to the Unique Network's pallets logic.26//!27//! - Local and foreign assets management. The foreign assets can be updated without runtime upgrade.28//! - Bounds between asset and target collection for cross chain transfer and inner transfers.29//!30//! ## Overview31//!32//! Under construction332234#![cfg_attr(not(feature = "std"), no_std)]23#![cfg_attr(not(feature = "std"), no_std)]35#![allow(clippy::unused_unit)]24#![allow(clippy::unused_unit)]362537use frame_support::{26use core::ops::Deref;2738 dispatch::DispatchResult,28use frame_support::{dispatch::DispatchResult, pallet_prelude::*, traits::EnsureOrigin, PalletId};39 ensure,40 pallet_prelude::*,41 traits::{fungible, fungibles, Currency, EnsureOrigin},42};43use frame_system::pallet_prelude::*;29use frame_system::pallet_prelude::*;44use pallet_common::erc::CrossAccountId;30use pallet_common::{45use pallet_fungible::Pallet as PalletFungible;31 dispatch::CollectionDispatch, erc::CrossAccountId, XcmExtensions, NATIVE_FUNGIBLE_COLLECTION_ID,46use scale_info::TypeInfo;47use serde::{Deserialize, Serialize};48use sp_runtime::{49 traits::{One, Zero},50 ArithmeticError,51};32};52use sp_std::{boxed::Box, vec::Vec};33use sp_runtime::traits::AccountIdConversion;53use staging_xcm::{latest::MultiLocation, VersionedMultiLocation};34use sp_std::{boxed::Box, vec, vec::Vec};54// NOTE: MultiLocation is used in storages, we will need to do migration if upgrade the55// MultiLocation to the XCM v3.56use staging_xcm::{35use staging_xcm::{57 opaque::latest::{prelude::XcmError, Weight},36 opaque::latest::{prelude::XcmError, Weight},58 v3::XcmContext,37 v3::{prelude::*, MultiAsset, XcmContext},38 VersionedAssetId,59};39};60use staging_xcm_executor::{traits::WeightTrader, Assets};40use staging_xcm_executor::{41 traits::{ConvertLocation, Error as XcmExecutorError, TransactAsset, WeightTrader},42 Assets,43};61use up_data_structs::{CollectionId, CollectionMode, CreateCollectionData, TokenId};44use up_data_structs::{45 budget::ZeroBudget, CollectionId, CollectionMode, CollectionName, CollectionTokenPrefix,46 CreateCollectionData, CreateFungibleData, CreateItemData, TokenId,47};624863// TODO: Move to primitives64// Id of native currency.65// 0 - QTZ\UNQ66// 1 - KSM\DOT67#[derive(68 Clone,69 Copy,70 Eq,71 PartialEq,72 PartialOrd,73 Ord,74 MaxEncodedLen,75 RuntimeDebug,76 Encode,77 Decode,78 TypeInfo,79 Serialize,80 Deserialize,81)]82pub enum NativeCurrency {83 Here = 0,84 Parent = 1,85}8687#[derive(88 Clone,89 Copy,90 Eq,91 PartialEq,92 PartialOrd,93 Ord,94 MaxEncodedLen,95 RuntimeDebug,96 Encode,97 Decode,98 TypeInfo,99 Serialize,100 Deserialize,101)]102pub enum AssetId {103 ForeignAssetId(ForeignAssetId),104 NativeAssetId(NativeCurrency),105}106107pub trait TryAsForeign<T, F> {108 fn try_as_foreign(asset: T) -> Option<F>;109}110111impl TryAsForeign<AssetId, ForeignAssetId> for AssetId {112 fn try_as_foreign(asset: AssetId) -> Option<ForeignAssetId> {113 match asset {114 Self::ForeignAssetId(id) => Some(id),115 _ => None,116 }117 }118}119120pub type ForeignAssetId = u32;121pub type CurrencyId = AssetId;122123mod impl_fungibles;124pub mod weights;49pub mod weights;12550126#[cfg(feature = "runtime-benchmarks")]51#[cfg(feature = "runtime-benchmarks")]129pub use module::*;54pub use module::*;130pub use weights::WeightInfo;55pub use weights::WeightInfo;13156132/// Type alias for currency balance.133pub type BalanceOf<T> =134 <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;135136/// A mapping between ForeignAssetId and AssetMetadata.137pub trait AssetIdMapping<ForeignAssetId, MultiLocation, AssetMetadata> {138 /// Returns the AssetMetadata associated with a given ForeignAssetId.139 fn get_asset_metadata(foreign_asset_id: ForeignAssetId) -> Option<AssetMetadata>;140 /// Returns the MultiLocation associated with a given ForeignAssetId.141 fn get_multi_location(foreign_asset_id: ForeignAssetId) -> Option<MultiLocation>;142 /// Returns the CurrencyId associated with a given MultiLocation.143 fn get_currency_id(multi_location: MultiLocation) -> Option<CurrencyId>;144}145146pub struct XcmForeignAssetIdMapping<T>(sp_std::marker::PhantomData<T>);147148impl<T: Config> AssetIdMapping<ForeignAssetId, MultiLocation, AssetMetadata<BalanceOf<T>>>149 for XcmForeignAssetIdMapping<T>150{151 fn get_asset_metadata(foreign_asset_id: ForeignAssetId) -> Option<AssetMetadata<BalanceOf<T>>> {152 log::trace!(target: "fassets::asset_metadatas", "call");153 Pallet::<T>::asset_metadatas(AssetId::ForeignAssetId(foreign_asset_id))154 }155156 fn get_multi_location(foreign_asset_id: ForeignAssetId) -> Option<MultiLocation> {157 log::trace!(target: "fassets::get_multi_location", "call");158 Pallet::<T>::foreign_asset_locations(foreign_asset_id)159 }160161 fn get_currency_id(multi_location: MultiLocation) -> Option<CurrencyId> {162 log::trace!(target: "fassets::get_currency_id", "call");163 Pallet::<T>::location_to_currency_ids(multi_location).map(AssetId::ForeignAssetId)164 }165}166167#[frame_support::pallet]57#[frame_support::pallet]168pub mod module {58pub mod module {59 use pallet_common::CollectionIssuer;60 use up_data_structs::CollectionDescription;61169 use super::*;62 use super::*;17063171 #[pallet::config]64 #[pallet::config]172 pub trait Config:65 pub trait Config:173 frame_system::Config66 frame_system::Config174 + pallet_common::Config67 + pallet_common::Config175 + pallet_fungible::Config68 + pallet_fungible::Config176 + orml_tokens::Config177 + pallet_balances::Config69 + pallet_balances::Config178 {70 {179 /// The overarching event type.71 /// The overarching event type.180 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;72 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;18173182 /// Currency type for withdraw and balance storage.74 /// Origin for force registering of a foreign asset.183 type Currency: Currency<Self::AccountId>;75 type ForceRegisterOrigin: EnsureOrigin<Self::RuntimeOrigin>;18476185 /// Required origin for registering asset.77 /// The ID of the foreign assets pallet.186 type RegisterOrigin: EnsureOrigin<Self::RuntimeOrigin>;78 type PalletId: Get<PalletId>;1877980 /// Self-location of this parachain.81 type SelfLocation: Get<MultiLocation>;8283 /// The converter from a MultiLocation to a CrossAccountId.84 type LocationToAccountId: ConvertLocation<Self::CrossAccountId>;85188 /// Weight information for the extrinsics in this module.86 /// Weight information for the extrinsics in this module.189 type WeightInfo: WeightInfo;87 type WeightInfo: WeightInfo;190 }88 }19189192 pub type AssetName = BoundedVec<u8, ConstU32<32>>;193 pub type AssetSymbol = BoundedVec<u8, ConstU32<7>>;194195 #[derive(Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen)]196 pub struct AssetMetadata<Balance> {197 pub name: AssetName,198 pub symbol: AssetSymbol,199 pub decimals: u8,200 pub minimal_balance: Balance,201 }202203 #[pallet::error]90 #[pallet::error]204 pub enum Error<T> {91 pub enum Error<T> {205 /// The given location could not be used (e.g. because it cannot be expressed in the92 /// The foreign asset is already registered.206 /// desired version of XCM).93 ForeignAssetAlreadyRegistered,207 BadLocation,94208 /// MultiLocation existed95 /// The given asset ID could not be converted into the current XCM version.209 MultiLocationExisted,210 /// AssetId not exists211 AssetIdNotExists,212 /// AssetId exists213 AssetIdExisted,96 BadForeignAssetId,214 }97 }21598216 #[pallet::event]99 #[pallet::event]217 #[pallet::generate_deposit(fn deposit_event)]100 #[pallet::generate_deposit(fn deposit_event)]218 pub enum Event<T: Config> {101 pub enum Event<T: Config> {219 /// The foreign asset registered.102 /// The foreign asset registered.220 ForeignAssetRegistered {103 ForeignAssetRegistered {221 asset_id: ForeignAssetId,104 collection_id: CollectionId,222 asset_address: MultiLocation,105 asset_id: Box<VersionedAssetId>,223 metadata: AssetMetadata<BalanceOf<T>>,224 },106 },225 /// The foreign asset updated.226 ForeignAssetUpdated {227 asset_id: ForeignAssetId,228 asset_address: MultiLocation,229 metadata: AssetMetadata<BalanceOf<T>>,230 },231 /// The asset registered.232 AssetRegistered {233 asset_id: AssetId,234 metadata: AssetMetadata<BalanceOf<T>>,235 },236 /// The asset updated.237 AssetUpdated {238 asset_id: AssetId,239 metadata: AssetMetadata<BalanceOf<T>>,240 },241 }107 }242108243 /// Next available Foreign AssetId ID.109 /// The corresponding collections of foreign assets.244 ///245 /// NextForeignAssetId: ForeignAssetId246 #[pallet::storage]110 #[pallet::storage]247 #[pallet::getter(fn next_foreign_asset_id)]111 #[pallet::getter(fn foreign_asset_to_collection)]248 pub type NextForeignAssetId<T: Config> = StorageValue<_, ForeignAssetId, ValueQuery>;112 pub type ForeignAssetToCollection<T: Config> =249 /// The storages for MultiLocations.113 StorageMap<_, Twox64Concat, staging_xcm::v3::AssetId, CollectionId, OptionQuery>;250 ///251 /// ForeignAssetLocations: map ForeignAssetId => Option<MultiLocation>252 #[pallet::storage]253 #[pallet::getter(fn foreign_asset_locations)]254 pub type ForeignAssetLocations<T: Config> =255 StorageMap<_, Twox64Concat, ForeignAssetId, staging_xcm::v3::MultiLocation, OptionQuery>;256114257 /// The storages for CurrencyIds.115 /// The corresponding foreign assets of collections.258 ///259 /// LocationToCurrencyIds: map MultiLocation => Option<ForeignAssetId>260 #[pallet::storage]116 #[pallet::storage]261 #[pallet::getter(fn location_to_currency_ids)]117 #[pallet::getter(fn collection_to_foreign_asset)]262 pub type LocationToCurrencyIds<T: Config> =118 pub type CollectionToForeignAsset<T: Config> =263 StorageMap<_, Twox64Concat, staging_xcm::v3::MultiLocation, ForeignAssetId, OptionQuery>;119 StorageMap<_, Twox64Concat, CollectionId, staging_xcm::v3::AssetId, OptionQuery>;264120265 /// The storages for AssetMetadatas.121 /// The correponding NFT token id of reserve NFTs266 ///267 /// AssetMetadatas: map AssetIds => Option<AssetMetadata>268 #[pallet::storage]122 #[pallet::storage]269 #[pallet::getter(fn asset_metadatas)]123 #[pallet::getter(fn foreign_reserve_asset_instance_to_token_id)]270 pub type AssetMetadatas<T: Config> =124 pub type ForeignReserveAssetInstanceToTokenId<T: Config> = StorageDoubleMap<271 StorageMap<_, Twox64Concat, AssetId, AssetMetadata<BalanceOf<T>>, OptionQuery>;125 Hasher1 = Twox64Concat,126 Key1 = CollectionId,127 Hasher2 = Blake2_128Concat,128 Key2 = staging_xcm::v3::AssetInstance,129 Value = TokenId,130 QueryKind = OptionQuery,131 >;272132273 /// The storages for assets to fungible collection binding133 /// The correponding reserve NFT of a token ID274 ///275 #[pallet::storage]134 #[pallet::storage]276 #[pallet::getter(fn asset_binding)]135 #[pallet::getter(fn token_id_to_foreign_reserve_asset_instance)]277 pub type AssetBinding<T: Config> =136 pub type TokenIdToForeignReserveAssetInstance<T: Config> = StorageDoubleMap<278 StorageMap<_, Twox64Concat, ForeignAssetId, CollectionId, OptionQuery>;137 Hasher1 = Twox64Concat,138 Key1 = CollectionId,139 Hasher2 = Blake2_128Concat,140 Key2 = TokenId,141 Value = staging_xcm::v3::AssetInstance,142 QueryKind = OptionQuery,143 >;279144280 #[pallet::pallet]145 #[pallet::pallet]281 pub struct Pallet<T>(_);146 pub struct Pallet<T>(_);284 impl<T: Config> Pallet<T> {149 impl<T: Config> Pallet<T> {285 #[pallet::call_index(0)]150 #[pallet::call_index(0)]286 #[pallet::weight(<T as Config>::WeightInfo::register_foreign_asset())]151 #[pallet::weight(<T as Config>::WeightInfo::register_foreign_asset())]287 pub fn register_foreign_asset(152 pub fn force_register_foreign_asset(288 origin: OriginFor<T>,153 origin: OriginFor<T>,289 owner: T::AccountId,154 versioned_asset_id: Box<VersionedAssetId>,155 name: CollectionName,290 location: Box<VersionedMultiLocation>,156 token_prefix: CollectionTokenPrefix,291 metadata: Box<AssetMetadata<BalanceOf<T>>>,157 mode: ForeignCollectionMode,292 ) -> DispatchResult {158 ) -> DispatchResult {293 T::RegisterOrigin::ensure_origin(origin.clone())?;159 T::ForceRegisterOrigin::ensure_origin(origin.clone())?;294160295 let location: MultiLocation = (*location)161 let asset_id: AssetId = versioned_asset_id162 .as_ref()163 .clone()296 .try_into()164 .try_into()297 .map_err(|()| Error::<T>::BadLocation)?;165 .map_err(|()| Error::<T>::BadForeignAssetId)?;298166299 let md = metadata.clone();167 ensure!(168 !<ForeignAssetToCollection<T>>::contains_key(asset_id),169 <Error<T>>::ForeignAssetAlreadyRegistered,170 );171300 let name: Vec<u16> = md.name.into_iter().map(|x| x as u16).collect::<Vec<u16>>();172 let foreign_collection_owner = Self::pallet_account();173301 let mut description: Vec<u16> = "Foreign assets collection for "174 let description: CollectionDescription = "Foreign Assets Collection"302 .encode_utf16()175 .encode_utf16()303 .collect::<Vec<u16>>();176 .collect::<Vec<_>>()304 description.append(&mut name.clone());177 .try_into()178 .expect("description length < max description length; qed");305179306 let data: CreateCollectionData<T::CrossAccountId> = CreateCollectionData {180 let collection_id = T::CollectionDispatch::create(307 name: name.try_into().unwrap(),181 foreign_collection_owner,308 description: description.try_into().unwrap(),182 CollectionIssuer::Internals,309 mode: CollectionMode::Fungible(md.decimals),183 CreateCollectionData {310 ..Default::default()184 name,311 };185 token_prefix,312 let owner = T::CrossAccountId::from_sub(owner);313 let bounded_collection_id =186 description,314 <PalletFungible<T>>::init_foreign_collection(owner.clone(), owner, data)?;187 mode: mode.into(),315 let foreign_asset_id =188 ..Default::default()316 Self::do_register_foreign_asset(&location, &metadata, bounded_collection_id)?;189 },190 )?;317191192 <ForeignAssetToCollection<T>>::insert(asset_id, collection_id);193 <CollectionToForeignAsset<T>>::insert(collection_id, asset_id);194318 Self::deposit_event(Event::<T>::ForeignAssetRegistered {195 Self::deposit_event(Event::<T>::ForeignAssetRegistered {319 asset_id: foreign_asset_id,196 collection_id,320 asset_address: location,197 asset_id: versioned_asset_id,321 metadata: *metadata,322 });198 });323 Ok(())324 }325199326 #[pallet::call_index(1)]327 #[pallet::weight(<T as Config>::WeightInfo::update_foreign_asset())]328 pub fn update_foreign_asset(329 origin: OriginFor<T>,330 foreign_asset_id: ForeignAssetId,331 location: Box<VersionedMultiLocation>,332 metadata: Box<AssetMetadata<BalanceOf<T>>>,333 ) -> DispatchResult {334 T::RegisterOrigin::ensure_origin(origin)?;335336 let location: MultiLocation = (*location)337 .try_into()338 .map_err(|()| Error::<T>::BadLocation)?;339 Self::do_update_foreign_asset(foreign_asset_id, &location, &metadata)?;340341 Self::deposit_event(Event::<T>::ForeignAssetUpdated {342 asset_id: foreign_asset_id,343 asset_address: location,344 metadata: *metadata,345 });346 Ok(())200 Ok(())347 }201 }348 }202 }349}203}350204351impl<T: Config> Pallet<T> {205impl<T: Config> Pallet<T> {352 fn get_next_foreign_asset_id() -> Result<ForeignAssetId, DispatchError> {206 fn pallet_account() -> T::CrossAccountId {353 NextForeignAssetId::<T>::try_mutate(|current| -> Result<ForeignAssetId, DispatchError> {354 let id = *current;207 let owner: T::AccountId = T::PalletId::get().into_account_truncating();355 *current = current356 .checked_add(One::one())357 .ok_or(ArithmeticError::Overflow)?;358 Ok(id)208 T::CrossAccountId::from_sub(owner)359 })360 }209 }361210362 fn do_register_foreign_asset(211 /// Converts a concrete asset ID (the asset multilocation) to a local collection on Unique Network.363 location: &MultiLocation,212 ///213 /// The multilocation corresponds to a local collection if:214 /// * It is `Here` location that corresponds to the native token of this parachain.364 metadata: &AssetMetadata<BalanceOf<T>>,215 /// * It is `../Parachain(<Unique Network Para ID>)` that also corresponds to the native token of this parachain.365 bounded_collection_id: CollectionId,216 /// * It is `../Parachain(<Unique Network Para ID>)/GeneralIndex(<Collection ID>)` that corresponds366 ) -> Result<ForeignAssetId, DispatchError> {217 /// to the collection with the ID equal to `<Collection ID>`. The `<Collection ID>` must be in the valid range,367 let foreign_asset_id = Self::get_next_foreign_asset_id()?;218 /// otherwise `None` is returned.219 /// * It is `GeneralIndex(<Collection ID>)`. Same as the last one above.368 LocationToCurrencyIds::<T>::try_mutate(location, |maybe_currency_ids| -> DispatchResult {220 ///221 /// If the multilocation doesn't match the patterns listed above,222 /// or the `<Collection ID>` points to a foreign collection,369 ensure!(223 /// `None` is returned, identifying that the given multilocation doesn't correspond to a local collection.370 maybe_currency_ids.is_none(),224 fn local_asset_id_to_collection(asset_id: &AssetId) -> Option<CollectionLocality> {371 Error::<T>::MultiLocationExisted225 let AssetId::Concrete(asset_location) = asset_id else {372 );373 *maybe_currency_ids = Some(foreign_asset_id);226 return None;374 // *maybe_currency_ids = Some(CurrencyId::ForeignAsset(foreign_asset_id));227 };375228376 ForeignAssetLocations::<T>::try_mutate(229 let self_location = T::SelfLocation::get();377 foreign_asset_id,378 |maybe_location| -> DispatchResult {379 ensure!(maybe_location.is_none(), Error::<T>::MultiLocationExisted);380 *maybe_location = Some(*location);381230382 AssetMetadatas::<T>::try_mutate(231 if *asset_location == Here.into() || *asset_location == self_location {383 AssetId::ForeignAssetId(foreign_asset_id),384 |maybe_asset_metadatas| -> DispatchResult {385 ensure!(maybe_asset_metadatas.is_none(), Error::<T>::AssetIdExisted);232 return Some(CollectionLocality::Local(NATIVE_FUNGIBLE_COLLECTION_ID));386 *maybe_asset_metadatas = Some(metadata.clone());387 Ok(())233 }388 },389 )390 },391 )?;392234393 AssetBinding::<T>::try_mutate(foreign_asset_id, |collection_id| -> DispatchResult {235 let prefix = if asset_location.parents == 0 {394 *collection_id = Some(bounded_collection_id);236 &Here237 } else if asset_location.parents == self_location.parents {395 Ok(())238 &self_location.interior239 } else {396 })240 return None;397 })?;241 };398242399 Ok(foreign_asset_id)243 let GeneralIndex(collection_id) = asset_location.interior.match_and_split(prefix)? else {244 return None;245 };246247 let collection_id = CollectionId((*collection_id).try_into().ok()?);248249 Self::collection_to_foreign_asset(collection_id)250 .is_none()251 .then_some(CollectionLocality::Local(collection_id))400 }252 }401253402 fn do_update_foreign_asset(254 /// Converts an asset ID to a Unique Network's collection locality (either foreign or a local one).255 ///256 /// The function will check if the asset's reserve location has the corresponding257 /// foreign collection on Unique Network,258 /// and will return the "foreign" locality containing the collection ID if found.259 ///260 /// If no corresponding foreign collection is found, the function will check261 /// if the asset's reserve location corresponds to a local collection.262 /// If the local collection is found, the "local" locality with the collection ID is returned.263 ///264 /// If all of the above have failed, the `AssetIdConversionFailed` error will be returned.265 fn asset_to_collection(asset_id: &AssetId) -> Result<CollectionLocality, XcmError> {403 foreign_asset_id: ForeignAssetId,266 Self::foreign_asset_to_collection(asset_id)267 .map(CollectionLocality::Foreign)268 .or_else(|| Self::local_asset_id_to_collection(asset_id))269 .ok_or_else(|| XcmExecutorError::AssetIdConversionFailed.into())270 }271272 /// Converts an XCM asset instance of local collection to the Unique Network's token ID.273 ///274 /// The asset instance corresponds to the Unique Network's token ID if it is in the following format:275 /// `AssetInstance::Index(<token ID>)`.276 ///277 /// If the asset instance is not in the valid format or the `<token ID>` can't fit into the valid token ID,404 location: &MultiLocation,278 /// `None` will be returned.279 ///280 /// Note: this function can return `Some` containing the token ID of a non-existing NFT.281 /// It returns `None` when it failed to convert the `asset_instance` to a local ID.282 fn local_asset_instance_to_token_id(asset_instance: &AssetInstance) -> Option<TokenId> {283 match asset_instance {284 AssetInstance::Index(token_id) => Some(TokenId((*token_id).try_into().ok()?)),405 metadata: &AssetMetadata<BalanceOf<T>>,285 _ => None,286 }287 }288289 /// Obtains the token ID of the `asset_instance` in the collection.290 ///291 /// Note: this function can return `Some` containing the token ID of a non-existing NFT.292 /// It returns `None` when it failed to convert the `asset_instance` to a local ID.293 fn asset_instance_to_token_id(294 collection_locality: CollectionLocality,295 asset_instance: &AssetInstance,296 ) -> Option<TokenId> {297 match collection_locality {298 CollectionLocality::Local(_) => Self::local_asset_instance_to_token_id(asset_instance),299 CollectionLocality::Foreign(collection_id) => {300 Self::foreign_reserve_asset_instance_to_token_id(collection_id, asset_instance)301 }302 }303 }304305 /// Creates a foreign item in the the collection.306 fn create_foreign_asset_instance(307 xcm_ext: &dyn XcmExtensions<T>,308 collection_id: CollectionId,309 asset_instance: &AssetInstance,310 to: T::CrossAccountId,406 ) -> DispatchResult {311 ) -> DispatchResult {407 ForeignAssetLocations::<T>::try_mutate(312 let derivative_token_id = xcm_ext.create_item(408 foreign_asset_id,313 &Self::pallet_account(),409 |maybe_multi_locations| -> DispatchResult {314 to,410 let old_multi_locations = maybe_multi_locations315 CreateItemData::NFT(Default::default()),411 .as_mut()412 .ok_or(Error::<T>::AssetIdNotExists)?;316 &ZeroBudget,317 )?;413318414 AssetMetadatas::<T>::try_mutate(319 <ForeignReserveAssetInstanceToTokenId<T>>::insert(415 AssetId::ForeignAssetId(foreign_asset_id),320 collection_id,416 |maybe_asset_metadatas| -> DispatchResult {321 asset_instance,417 ensure!(322 derivative_token_id,418 maybe_asset_metadatas.is_some(),419 Error::<T>::AssetIdNotExists420 );323 );421324422 // modify location325 <TokenIdToForeignReserveAssetInstance<T>>::insert(423 if location != old_multi_locations {326 collection_id,424 LocationToCurrencyIds::<T>::remove(*old_multi_locations);327 derivative_token_id,425 LocationToCurrencyIds::<T>::try_mutate(328 asset_instance,426 location,329 );427 |maybe_currency_ids| -> DispatchResult {330428 ensure!(331 Ok(())429 maybe_currency_ids.is_none(),332 }430 Error::<T>::MultiLocationExisted333431 );334 /// Deposits an asset instance to the `to` account.432 // *maybe_currency_ids = Some(CurrencyId::ForeignAsset(foreign_asset_id));335 ///433 *maybe_currency_ids = Some(foreign_asset_id);336 /// Either transfers an existing item from the pallet's account434 Ok(())337 /// or creates a foreign item.435 },338 fn deposit_asset_instance(436 )?;339 xcm_ext: &dyn XcmExtensions<T>,437 }340 collection_locality: CollectionLocality,438 *maybe_asset_metadatas = Some(metadata.clone());341 asset_instance: &AssetInstance,439 *old_multi_locations = *location;342 to: T::CrossAccountId,440 Ok(())343 ) -> XcmResult {441 },344 let token_id = Self::asset_instance_to_token_id(collection_locality, asset_instance);345346 let deposit_result = match (collection_locality, token_id) {347 (_, Some(token_id)) => {348 let depositor = &Self::pallet_account();349 let from = depositor;350 let amount = 1;351352 xcm_ext.transfer_item(depositor, from, &to, token_id, amount, &ZeroBudget)353 }354 (CollectionLocality::Foreign(collection_id), None) => {355 Self::create_foreign_asset_instance(xcm_ext, collection_id, asset_instance, to)356 }357 (CollectionLocality::Local(_), None) => {358 return Err(XcmExecutorError::InstanceConversionFailed.into());359 }360 };361362 deposit_result363 .map_err(|_| XcmError::FailedToTransactAsset("non-fungible item deposit failed"))364 }365366 /// Withdraws an asset instance from the `from` account.367 ///368 /// Transfers the asset instance to the pallet's account.369 fn withdraw_asset_instance(370 xcm_ext: &dyn XcmExtensions<T>,371 collection_locality: CollectionLocality,372 asset_instance: &AssetInstance,373 from: T::CrossAccountId,374 ) -> XcmResult {375 let token_id = Self::asset_instance_to_token_id(collection_locality, asset_instance)376 .ok_or(XcmExecutorError::InstanceConversionFailed)?;377378 let depositor = &from;379 let to = Self::pallet_account();380 let amount = 1;381 xcm_ext382 .transfer_item(depositor, &from, &to, token_id, amount, &ZeroBudget)383 .map_err(|_| XcmError::FailedToTransactAsset("non-fungible item withdraw failed"))?;384385 Ok(())386 }387}388389impl<T: Config> TransactAsset for Pallet<T> {390 fn can_check_in(391 _origin: &MultiLocation,392 _what: &MultiAsset,393 _context: &XcmContext,394 ) -> XcmResult {395 Err(XcmError::Unimplemented)396 }397398 fn check_in(_origin: &MultiLocation, _what: &MultiAsset, _context: &XcmContext) {}399400 fn can_check_out(401 _dest: &MultiLocation,402 _what: &MultiAsset,403 _context: &XcmContext,404 ) -> XcmResult {405 Err(XcmError::Unimplemented)406 }407408 fn check_out(_dest: &MultiLocation, _what: &MultiAsset, _context: &XcmContext) {}409410 fn deposit_asset(411 what: &MultiAsset,412 to: &MultiLocation,413 _context: Option<&XcmContext>,414 ) -> XcmResult {415 let to = T::LocationToAccountId::convert_location(to)416 .ok_or(XcmExecutorError::AccountIdConversionFailed)?;417418 let collection_locality = Self::asset_to_collection(&what.id)?;419 let dispatch = T::CollectionDispatch::dispatch(*collection_locality)420 .map_err(|_| XcmExecutorError::AssetIdConversionFailed)?;421422 let collection = dispatch.as_dyn();423 let xcm_ext = collection.xcm_extensions().ok_or(XcmError::Unimplemented)?;424425 match what.fun {426 Fungibility::Fungible(amount) => xcm_ext427 .create_item(428 &Self::pallet_account(),429 to,430 CreateItemData::Fungible(CreateFungibleData { value: amount }),431 &ZeroBudget,442 )432 )443 },433 .map(|_| ())434 .map_err(|_| XcmError::FailedToTransactAsset("fungible item deposit failed")),435444 )436 Fungibility::NonFungible(asset_instance) => {437 Self::deposit_asset_instance(xcm_ext, collection_locality, &asset_instance, to)438 }439 }445 }440 }441442 fn withdraw_asset(443 what: &MultiAsset,444 from: &MultiLocation,445 _maybe_context: Option<&XcmContext>,446 ) -> Result<staging_xcm_executor::Assets, XcmError> {447 let from = T::LocationToAccountId::convert_location(from)448 .ok_or(XcmExecutorError::AccountIdConversionFailed)?;449450 let collection_locality = Self::asset_to_collection(&what.id)?;451 let dispatch = T::CollectionDispatch::dispatch(*collection_locality)452 .map_err(|_| XcmExecutorError::AssetIdConversionFailed)?;453454 let collection = dispatch.as_dyn();455 let xcm_ext = collection.xcm_extensions().ok_or(XcmError::NoPermission)?;456457 match what.fun {458 Fungibility::Fungible(amount) => xcm_ext459 .burn_item(from, TokenId::default(), amount)460 .map_err(|_| XcmError::FailedToTransactAsset("fungible item withdraw failed"))?,461462 Fungibility::NonFungible(asset_instance) => {463 Self::withdraw_asset_instance(xcm_ext, collection_locality, &asset_instance, from)?;464 }465 }466467 Ok(what.clone().into())468 }469470 fn internal_transfer_asset(471 what: &MultiAsset,472 from: &MultiLocation,473 to: &MultiLocation,474 _context: &XcmContext,475 ) -> Result<staging_xcm_executor::Assets, XcmError> {476 let from = T::LocationToAccountId::convert_location(from)477 .ok_or(XcmExecutorError::AccountIdConversionFailed)?;478479 let to = T::LocationToAccountId::convert_location(to)480 .ok_or(XcmExecutorError::AccountIdConversionFailed)?;481482 let collection_locality = Self::asset_to_collection(&what.id)?;483484 let dispatch = T::CollectionDispatch::dispatch(*collection_locality)485 .map_err(|_| XcmExecutorError::AssetIdConversionFailed)?;486 let collection = dispatch.as_dyn();487 let xcm_ext = collection.xcm_extensions().ok_or(XcmError::NoPermission)?;488489 let depositor = &from;490491 let token_id;492 let amount;493 let map_error: fn(DispatchError) -> XcmError;494495 match what.fun {496 Fungibility::Fungible(fungible_amount) => {497 token_id = TokenId::default();498 amount = fungible_amount;499 map_error = |_| XcmError::FailedToTransactAsset("fungible item transfer failed");500 }501502 Fungibility::NonFungible(asset_instance) => {503 token_id = Self::asset_instance_to_token_id(collection_locality, &asset_instance)504 .ok_or(XcmExecutorError::InstanceConversionFailed)?;505506 amount = 1;507 map_error = |_| XcmError::FailedToTransactAsset("non-fungible item transfer failed")508 }509 }510511 xcm_ext512 .transfer_item(depositor, &from, &to, token_id, amount, &ZeroBudget)513 .map_err(map_error)?;514515 Ok(what.clone().into())516 }446}517}447518448pub use frame_support::{519#[derive(Clone, Copy)]449 traits::{450 fungibles::Balanced, tokens::currency::Currency as CurrencyT, OnUnbalanced as OnUnbalancedT,520pub enum CollectionLocality {451 },521 Local(CollectionId),452 weights::{WeightToFee, WeightToFeePolynomial},522 Foreign(CollectionId),453};523}454524455pub struct FreeForAll<525impl Deref for CollectionLocality {456 WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,457 AssetId: Get<MultiLocation>,526 type Target = CollectionId;458 AccountId,459 Currency: CurrencyT<AccountId>,460 OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,461>(462 Weight,463 Currency::Balance,464 PhantomData<(WeightToFee, AssetId, AccountId, Currency, OnUnbalanced)>,465);466527467impl<528 fn deref(&self) -> &Self::Target {468 WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,529 match self {530 Self::Local(id) => id,469 AssetId: Get<MultiLocation>,531 Self::Foreign(id) => id,470 AccountId,532 }471 Currency: CurrencyT<AccountId>,533 }534}535536pub struct CurrencyIdConvert<T: Config>(PhantomData<T>);472 OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,537impl<T: Config> sp_runtime::traits::Convert<CollectionId, Option<MultiLocation>>473 > WeightTrader for FreeForAll<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>538 for CurrencyIdConvert<T>474{539{540 fn convert(collection_id: CollectionId) -> Option<MultiLocation> {541 if collection_id == NATIVE_FUNGIBLE_COLLECTION_ID {542 Some(T::SelfLocation::get())543 } else {544 <Pallet<T>>::collection_to_foreign_asset(collection_id)545 .and_then(|asset_id| match asset_id {546 AssetId::Concrete(location) => Some(location),547 _ => None,548 })549 .or_else(|| {550 T::SelfLocation::get()551 .pushed_with_interior(GeneralIndex(collection_id.0.into()))552 .ok()553 })554 }555 }556}557558#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]559pub enum ForeignCollectionMode {560 NFT,561 Fungible(u8),562}563564impl From<ForeignCollectionMode> for CollectionMode {565 fn from(value: ForeignCollectionMode) -> Self {566 match value {567 ForeignCollectionMode::NFT => Self::NFT,568 ForeignCollectionMode::Fungible(decimals) => Self::Fungible(decimals),569 }570 }571}572573pub struct FreeForAll;574575impl WeightTrader for FreeForAll {475 fn new() -> Self {576 fn new() -> Self {476 Self(Weight::default(), Zero::zero(), PhantomData)577 Self477 }578 }478579479 fn buy_weight(580 fn buy_weight(484 ) -> Result<Assets, XcmError> {585 ) -> Result<Assets, XcmError> {485 log::trace!(target: "fassets::weight", "buy_weight weight: {:?}, payment: {:?}", weight, payment);586 log::trace!(target: "fassets::weight", "buy_weight weight: {:?}, payment: {:?}", weight, payment);486 Ok(payment)587 Ok(payment)487 }488}489impl<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced> Drop490 for FreeForAll<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>491where492 WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,493 AssetId: Get<MultiLocation>,494 Currency: CurrencyT<AccountId>,495 OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,496{497 fn drop(&mut self) {498 OnUnbalanced::on_unbalanced(Currency::issue(self.1));499 }588 }500}589}501590pallets/fungible/src/benchmarking.rsdiffbeforeafterboth161617use frame_benchmarking::{account, v2::*};17use frame_benchmarking::{account, v2::*};18use pallet_common::{bench_init, benchmarking::create_collection_raw};18use pallet_common::{19 bench_init, benchmarking::create_collection_raw, CollectionIssuer, Pallet as PalletCommon,20};19use sp_std::prelude::*;21use sp_std::prelude::*;20use up_data_structs::{budget::Unlimited, CollectionMode, MAX_ITEMS_PER_BATCH};22use up_data_structs::{budget::Unlimited, CollectionMode, MAX_ITEMS_PER_BATCH};30 create_collection_raw(32 create_collection_raw(31 owner,33 owner,32 CollectionMode::Fungible(0),34 CollectionMode::Fungible(0),33 |owner: T::CrossAccountId, data| <Pallet<T>>::init_collection(owner.clone(), owner, data),35 |owner: T::CrossAccountId, data| {36 <PalletCommon<T>>::init_collection(owner.clone(), CollectionIssuer::User(owner), data)37 },34 FungibleHandle::cast,38 FungibleHandle::cast,35 )39 )36}40}pallets/fungible/src/common.rsdiffbeforeafterboth19use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, fail, weights::Weight};19use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, fail, weights::Weight};20use pallet_common::{20use pallet_common::{21 weights::WeightInfo as _, with_weight, CommonCollectionOperations, CommonWeightInfo,21 weights::WeightInfo as _, with_weight, CommonCollectionOperations, CommonWeightInfo,22 Error as CommonError, RefungibleExtensions, SelfWeightOf as PalletCommonWeightOf,22 Error as CommonError, SelfWeightOf as PalletCommonWeightOf, XcmExtensions,23};23};24use sp_runtime::{ArithmeticError, DispatchError};24use sp_runtime::{ArithmeticError, DispatchError};25use sp_std::{vec, vec::Vec};25use sp_std::{vec, vec::Vec};114 <Pallet<T>>::create_item(self, &sender, (to, fungible_data.value), nesting_budget),114 <Pallet<T>>::create_item(self, &sender, (to, fungible_data.value), nesting_budget),115 <CommonWeights<T>>::create_item(&data),115 <CommonWeights<T>>::create_item(&data),116 ),116 ),117 _ => fail!(<Error<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),117 _ => fail!(<CommonError<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),118 }118 }119 }119 }120120133 .checked_add(data.value)133 .checked_add(data.value)134 .ok_or(ArithmeticError::Overflow)?;134 .ok_or(ArithmeticError::Overflow)?;135 }135 }136 _ => fail!(<Error<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),136 _ => fail!(<CommonError<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),137 }137 }138 }138 }139139152 let weight = <CommonWeights<T>>::create_multiple_items_ex(&data);152 let weight = <CommonWeights<T>>::create_multiple_items_ex(&data);153 let data = match data {153 let data = match data {154 up_data_structs::CreateItemExData::Fungible(f) => f,154 up_data_structs::CreateItemExData::Fungible(f) => f,155 _ => fail!(<Error<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),155 _ => fail!(<CommonError<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),156 };156 };157157158 with_weight(158 with_weight(428 <Allowance<T>>::get((self.id, sender, spender))428 <Allowance<T>>::get((self.id, sender, spender))429 }429 }430431 fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>> {432 None433 }434430435 fn total_pieces(&self, token: TokenId) -> Option<u128> {431 fn total_pieces(&self, token: TokenId) -> Option<u128> {436 if token != TokenId::default() {432 if token != TokenId::default() {439 <TotalSupply<T>>::try_get(self.id).ok()435 <TotalSupply<T>>::try_get(self.id).ok()440 }436 }437438 fn xcm_extensions(&self) -> Option<&dyn XcmExtensions<T>> {439 Some(self)440 }441441442 fn set_allowance_for_all(442 fn set_allowance_for_all(443 &self,443 &self,458 }458 }459}459}460461impl<T: Config> XcmExtensions<T> for FungibleHandle<T> {462 fn create_item_internal(463 &self,464 depositor: &<T>::CrossAccountId,465 to: <T>::CrossAccountId,466 data: CreateItemData,467 nesting_budget: &dyn Budget,468 ) -> Result<TokenId, sp_runtime::DispatchError> {469 match &data {470 up_data_structs::CreateItemData::Fungible(fungible_data) => {471 <Pallet<T>>::create_multiple_items(472 self,473 depositor,474 [(to, fungible_data.value)].into_iter().collect(),475 nesting_budget,476 )?477 }478 _ => fail!(<CommonError<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),479 }480481 Ok(TokenId::default())482 }483484 fn transfer_item_internal(485 &self,486 depositor: &<T>::CrossAccountId,487 from: &<T>::CrossAccountId,488 to: &<T>::CrossAccountId,489 token: TokenId,490 amount: u128,491 nesting_budget: &dyn Budget,492 ) -> sp_runtime::DispatchResult {493 ensure!(494 token == TokenId::default(),495 <CommonError<T>>::FungibleItemsHaveNoId496 );497498 <Pallet<T>>::transfer_internal(self, depositor, from, to, amount, nesting_budget)499 .map(|_| ())500 .map_err(|post_info| post_info.error)501 }502503 fn burn_item_internal(504 &self,505 from: <T>::CrossAccountId,506 token: TokenId,507 amount: u128,508 ) -> sp_runtime::DispatchResult {509 <Self as CommonCollectionOperations<T>>::burn_item(self, from, token, amount)510 .map(|_| ())511 .map_err(|post_info| post_info.error)512 }513}460514pallets/fungible/src/lib.rsdiffbeforeafterboth95use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};95use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};96use sp_std::{collections::btree_map::BTreeMap, vec::Vec};96use sp_std::{collections::btree_map::BTreeMap, vec::Vec};97use up_data_structs::{97use up_data_structs::{98 budget::Budget, mapping::TokenAddressMapping, AccessMode, CollectionId, CreateCollectionData,98 budget::Budget, mapping::TokenAddressMapping, AccessMode, CollectionId, Property, PropertyKey,99 Property, PropertyKey, TokenId,99 TokenId,100};100};101use weights::WeightInfo;101use weights::WeightInfo;121121122 #[pallet::error]122 #[pallet::error]123 pub enum Error<T> {123 pub enum Error<T> {124 /// Not Fungible item data used to mint in Fungible collection.125 NotFungibleDataUsedToMintFungibleCollectionToken,126 /// Tried to set data for fungible item.124 /// Tried to set data for fungible item.127 FungibleItemsDontHaveData,125 FungibleItemsDontHaveData,128 /// Fungible token does not support nesting.126 /// Fungible token does not support nesting.212210213/// Pallet implementation for fungible assets211/// Pallet implementation for fungible assets214impl<T: Config> Pallet<T> {212impl<T: Config> Pallet<T> {215 /// Initializes the collection. Returns [CollectionId] on success, [DispatchError] otherwise.216 pub fn init_collection(217 owner: T::CrossAccountId,218 payer: T::CrossAccountId,219 data: CreateCollectionData<T::CrossAccountId>,220 ) -> Result<CollectionId, DispatchError> {221 <PalletCommon<T>>::init_collection(owner, payer, data)222 }223224 /// Initializes the collection with ForeignCollection flag. Returns [CollectionId] on success, [DispatchError] otherwise.225 pub fn init_foreign_collection(226 owner: T::CrossAccountId,227 payer: T::CrossAccountId,228 data: CreateCollectionData<T::CrossAccountId>,229 ) -> Result<CollectionId, DispatchError> {230 <PalletCommon<T>>::init_foreign_collection(owner, payer, data)231 }232233 /// Destroys a collection.213 /// Destroys a collection.234 pub fn destroy_collection(214 pub fn destroy_collection(294 .checked_sub(amount)274 .checked_sub(amount)295 .ok_or(<CommonError<T>>::TokenValueTooLow)?;275 .ok_or(<CommonError<T>>::TokenValueTooLow)?;296297 // Foreign collection check298 ensure!(!collection.flags.foreign, <CommonError<T>>::NoPermission);299276300 if collection.permissions.access() == AccessMode::AllowList {277 if collection.permissions.access() == AccessMode::AllowList {301 collection.check_allowlist(owner)?;278 collection.check_allowlist(owner)?;328 Ok(())305 Ok(())329 }306 }330331 /// Burns the specified amount of the token.332 pub fn burn_foreign(333 collection: &FungibleHandle<T>,334 owner: &T::CrossAccountId,335 amount: u128,336 ) -> DispatchResult {337 let total_supply = <TotalSupply<T>>::get(collection.id)338 .checked_sub(amount)339 .ok_or(<CommonError<T>>::TokenValueTooLow)?;340341 let balance = <Balance<T>>::get((collection.id, owner))342 .checked_sub(amount)343 .ok_or(<CommonError<T>>::TokenValueTooLow)?;344 // =========345346 if balance == 0 {347 <Balance<T>>::remove((collection.id, owner));348 <PalletStructure<T>>::unnest_if_nested(owner, collection.id, TokenId::default());349 } else {350 <Balance<T>>::insert((collection.id, owner), balance);351 }352 <TotalSupply<T>>::insert(collection.id, total_supply);353354 <PalletEvm<T>>::deposit_log(355 ERC20Events::Transfer {356 from: *owner.as_eth(),357 to: H160::default(),358 value: amount.into(),359 }360 .to_log(collection_id_to_address(collection.id)),361 );362 <PalletCommon<T>>::deposit_event(CommonEvent::ItemDestroyed(363 collection.id,364 TokenId::default(),365 owner.clone(),366 amount,367 ));368 Ok(())369 }370307371 /// Transfers the specified amount of tokens. Will check that308 /// Transfers the specified amount of tokens. Will check that372 /// the transfer is allowed for the token.309 /// the transfer is allowed for the token.468 }405 }469406470 /// Minting tokens for multiple IDs.407 /// Minting tokens for multiple IDs.471 /// It is a utility function used in [`create_multiple_items`][`Pallet::create_multiple_items`]472 /// and [`create_multiple_items_foreign`][`Pallet::create_multiple_items_foreign`]408 /// See [`create_item`][`Pallet::create_item`] for more details.473 pub fn create_multiple_items_common(409 pub fn create_multiple_items(474 collection: &FungibleHandle<T>,410 collection: &FungibleHandle<T>,475 sender: &T::CrossAccountId,411 depositor: &T::CrossAccountId,476 data: BTreeMap<T::CrossAccountId, u128>,412 data: BTreeMap<T::CrossAccountId, u128>,477 nesting_budget: &dyn Budget,413 nesting_budget: &dyn Budget,478 ) -> DispatchResult {414 ) -> DispatchResult {415 if !collection.is_owner_or_admin(depositor) {416 ensure!(417 collection.permissions.mint_mode(),418 <CommonError<T>>::PublicMintingNotAllowed419 );420 collection.check_allowlist(depositor)?;421422 for (owner, _) in data.iter() {423 collection.check_allowlist(owner)?;424 }425 }426479 let total_supply = data427 let total_supply = data480 .values()428 .values()486434487 for (to, _) in data.iter() {435 for (to, _) in data.iter() {488 <PalletStructure<T>>::check_nesting(436 <PalletStructure<T>>::check_nesting(489 sender,437 depositor,490 to,438 to,491 collection.id,439 collection.id,492 TokenId::default(),440 TokenId::default(),533 Ok(())481 Ok(())534 }482 }535536 /// Minting tokens for multiple IDs.537 /// See [`create_item`][`Pallet::create_item`] for more details.538 pub fn create_multiple_items(539 collection: &FungibleHandle<T>,540 sender: &T::CrossAccountId,541 data: BTreeMap<T::CrossAccountId, u128>,542 nesting_budget: &dyn Budget,543 ) -> DispatchResult {544 // Foreign collection check545 ensure!(!collection.flags.foreign, <CommonError<T>>::NoPermission);546547 if !collection.is_owner_or_admin(sender) {548 ensure!(549 collection.permissions.mint_mode(),550 <CommonError<T>>::PublicMintingNotAllowed551 );552 collection.check_allowlist(sender)?;553554 for (owner, _) in data.iter() {555 collection.check_allowlist(owner)?;556 }557 }558559 Self::create_multiple_items_common(collection, sender, data, nesting_budget)560 }561562 /// Minting tokens for multiple IDs.563 /// See [`create_item_foreign`][`Pallet::create_item_foreign`] for more details.564 pub fn create_multiple_items_foreign(565 collection: &FungibleHandle<T>,566 sender: &T::CrossAccountId,567 data: BTreeMap<T::CrossAccountId, u128>,568 nesting_budget: &dyn Budget,569 ) -> DispatchResult {570 Self::create_multiple_items_common(collection, sender, data, nesting_budget)571 }572483573 fn set_allowance_unchecked(484 fn set_allowance_unchecked(574 collection: &FungibleHandle<T>,485 collection: &FungibleHandle<T>,802 )713 )803 }714 }804805 /// Creates fungible token.806 ///807 /// - `data`: Contains user who will become the owners of the tokens and amount808 /// of tokens he will receive.809 pub fn create_item_foreign(810 collection: &FungibleHandle<T>,811 sender: &T::CrossAccountId,812 data: CreateItemData<T>,813 nesting_budget: &dyn Budget,814 ) -> DispatchResult {815 Self::create_multiple_items_foreign(816 collection,817 sender,818 [(data.0, data.1)].into_iter().collect(),819 nesting_budget,820 )821 }822715823 /// Returns 10 tokens owners in no particular order716 /// Returns 10 tokens owners in no particular order824 ///717 ///pallets/nonfungible/src/benchmarking.rsdiffbeforeafterboth18use pallet_common::{18use pallet_common::{19 bench_init,19 bench_init,20 benchmarking::{create_collection_raw, property_key, property_value},20 benchmarking::{create_collection_raw, property_key, property_value},21 CollectionIssuer, Pallet as PalletCommon,21};22};22use sp_std::prelude::*;23use sp_std::prelude::*;23use up_data_structs::{24use up_data_structs::{56 create_collection_raw(57 create_collection_raw(57 owner,58 owner,58 CollectionMode::NFT,59 CollectionMode::NFT,59 |owner: T::CrossAccountId, data| <Pallet<T>>::init_collection(owner.clone(), owner, data),60 |owner: T::CrossAccountId, data| {61 <PalletCommon<T>>::init_collection(owner.clone(), CollectionIssuer::User(owner), data)62 },60 NonfungibleHandle::cast,63 NonfungibleHandle::cast,61 )64 )62}65}pallets/nonfungible/src/common.rsdiffbeforeafterboth19use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, fail, weights::Weight};19use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, fail, weights::Weight};20use pallet_common::{20use pallet_common::{21 weights::WeightInfo as _, with_weight, write_token_properties_total_weight,21 weights::WeightInfo as _, with_weight, write_token_properties_total_weight,22 CommonCollectionOperations, CommonWeightInfo, RefungibleExtensions,22 CommonCollectionOperations, CommonWeightInfo, SelfWeightOf as PalletCommonWeightOf,23 SelfWeightOf as PalletCommonWeightOf,23 XcmExtensions,24};24};25use pallet_structure::Pallet as PalletStructure;25use pallet_structure::Pallet as PalletStructure;26use sp_runtime::DispatchError;26use sp_runtime::DispatchError;535 }535 }536 }536 }537537538 fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>> {539 None540 }541542 fn total_pieces(&self, token: TokenId) -> Option<u128> {538 fn total_pieces(&self, token: TokenId) -> Option<u128> {543 if <TokenData<T>>::contains_key((self.id, token)) {539 if <TokenData<T>>::contains_key((self.id, token)) {544 Some(1)540 Some(1)547 }543 }548 }544 }549545546 fn xcm_extensions(&self) -> Option<&dyn XcmExtensions<T>> {547 Some(self)548 }549550 fn set_allowance_for_all(550 fn set_allowance_for_all(551 &self,551 &self,552 owner: T::CrossAccountId,552 owner: T::CrossAccountId,568 <Pallet<T>>::repair_item(self, token),568 <Pallet<T>>::repair_item(self, token),569 <CommonWeights<T>>::force_repair_item(),569 <CommonWeights<T>>::force_repair_item(),570 )570 )571 }572}573574impl<T: Config> XcmExtensions<T> for NonfungibleHandle<T> {575 fn token_has_children(&self, token: TokenId) -> bool {576 <Pallet<T>>::token_has_children(self.id, token)577 }578579 fn create_item_internal(580 &self,581 depositor: &<T>::CrossAccountId,582 to: <T>::CrossAccountId,583 data: up_data_structs::CreateItemData,584 nesting_budget: &dyn Budget,585 ) -> Result<TokenId, sp_runtime::DispatchError> {586 <Pallet<T>>::create_multiple_items(587 self,588 depositor,589 vec![map_create_data::<T>(data, &to)?],590 nesting_budget,591 )?;592593 Ok(self.last_token_id())594 }595596 fn transfer_item_internal(597 &self,598 depositor: &<T>::CrossAccountId,599 from: &<T>::CrossAccountId,600 to: &<T>::CrossAccountId,601 token: TokenId,602 amount: u128,603 nesting_budget: &dyn Budget,604 ) -> sp_runtime::DispatchResult {605 ensure!(amount == 1, <Error<T>>::NonfungibleItemsHaveNoAmount);606607 <Pallet<T>>::transfer_internal(self, depositor, from, to, token, nesting_budget)608 .map(|_| ())609 .map_err(|post_info| post_info.error)610 }611612 fn burn_item_internal(613 &self,614 from: T::CrossAccountId,615 token: TokenId,616 amount: u128,617 ) -> sp_runtime::DispatchResult {618 ensure!(amount == 1, <Error<T>>::NonfungibleItemsHaveNoAmount);619620 <Pallet<T>>::burn(self, &from, token)571 }621 }572}622}573623pallets/nonfungible/src/lib.rsdiffbeforeafterboth117use sp_std::{collections::btree_map::BTreeMap, vec, vec::Vec};117use sp_std::{collections::btree_map::BTreeMap, vec, vec::Vec};118use up_data_structs::{118use up_data_structs::{119 budget::Budget, mapping::TokenAddressMapping, AccessMode, AuxPropertyValue, CollectionId,119 budget::Budget, mapping::TokenAddressMapping, AccessMode, AuxPropertyValue, CollectionId,120 CreateCollectionData, CreateNftExData, CustomDataLimit, PropertiesPermissionMap, Property,120 CreateNftExData, CustomDataLimit, PropertiesPermissionMap, Property, PropertyKey,121 PropertyKey, PropertyKeyPermission, PropertyScope, PropertyValue, TokenChild, TokenId,121 PropertyKeyPermission, PropertyScope, PropertyValue, TokenChild, TokenId,122 TokenProperties as TokenPropertiesT,122 TokenProperties as TokenPropertiesT,123};123};383383384// unchecked calls skips any permission checks384// unchecked calls skips any permission checks385impl<T: Config> Pallet<T> {385impl<T: Config> Pallet<T> {386 /// Create NFT collection387 ///388 /// `init_collection` will take non-refundable deposit for collection creation.389 ///390 /// - `data`: Contains settings for collection limits and permissions.391 pub fn init_collection(392 owner: T::CrossAccountId,393 payer: T::CrossAccountId,394 data: CreateCollectionData<T::CrossAccountId>,395 ) -> Result<CollectionId, DispatchError> {396 <PalletCommon<T>>::init_collection(owner, payer, data)397 }398399 /// Destroy NFT collection386 /// Destroy NFT collection400 ///387 ///pallets/refungible/src/benchmarking.rsdiffbeforeafterboth20use pallet_common::{20use pallet_common::{21 bench_init,21 bench_init,22 benchmarking::{create_collection_raw, property_key, property_value},22 benchmarking::{create_collection_raw, property_key, property_value},23 CollectionIssuer, Pallet as PalletCommon,23};24};24use sp_std::prelude::*;25use sp_std::prelude::*;25use up_data_structs::{26use up_data_structs::{61 create_collection_raw(62 create_collection_raw(62 owner,63 owner,63 CollectionMode::ReFungible,64 CollectionMode::ReFungible,64 |owner: T::CrossAccountId, data| <Pallet<T>>::init_collection(owner.clone(), owner, data),65 |owner: T::CrossAccountId, data| {66 <PalletCommon<T>>::init_collection(owner.clone(), CollectionIssuer::User(owner), data)67 },65 RefungibleHandle::cast,68 RefungibleHandle::cast,66 )69 )67}70}pallets/refungible/src/lib.rsdiffbeforeafterboth103use sp_runtime::{ArithmeticError, DispatchError, DispatchResult, TransactionOutcome};103use sp_runtime::{ArithmeticError, DispatchError, DispatchResult, TransactionOutcome};104use sp_std::{collections::btree_map::BTreeMap, vec, vec::Vec};104use sp_std::{collections::btree_map::BTreeMap, vec, vec::Vec};105use up_data_structs::{105use up_data_structs::{106 budget::Budget, mapping::TokenAddressMapping, AccessMode, CollectionId, CreateCollectionData,106 budget::Budget, mapping::TokenAddressMapping, AccessMode, CollectionId,107 CreateRefungibleExMultipleOwners, PropertiesPermissionMap, Property, PropertyKey,107 CreateRefungibleExMultipleOwners, PropertiesPermissionMap, Property, PropertyKey,108 PropertyKeyPermission, PropertyScope, PropertyValue, TokenId, TokenOwnerError,108 PropertyKeyPermission, PropertyScope, PropertyValue, TokenId, TokenOwnerError,109 TokenProperties as TokenPropertiesT, MAX_REFUNGIBLE_PIECES,109 TokenProperties as TokenPropertiesT, MAX_REFUNGIBLE_PIECES,296296297// unchecked calls skips any permission checks297// unchecked calls skips any permission checks298impl<T: Config> Pallet<T> {298impl<T: Config> Pallet<T> {299 /// Create RFT collection300 ///301 /// `init_collection` will take non-refundable deposit for collection creation.302 ///303 /// - `data`: Contains settings for collection limits and permissions.304 pub fn init_collection(305 owner: T::CrossAccountId,306 payer: T::CrossAccountId,307 data: CreateCollectionData<T::CrossAccountId>,308 ) -> Result<CollectionId, DispatchError> {309 <PalletCommon<T>>::init_collection(owner, payer, data)310 }311312 /// Destroy RFT collection299 /// Destroy RFT collection313 ///300 ///pallets/structure/src/benchmarking.rsdiffbeforeafterboth161617use frame_benchmarking::v2::{account, benchmarks, BenchmarkError};17use frame_benchmarking::v2::{account, benchmarks, BenchmarkError};18use frame_support::traits::{fungible::Balanced, tokens::Precision, Get};18use frame_support::traits::{fungible::Balanced, tokens::Precision, Get};19use pallet_common::Config as CommonConfig;19use pallet_common::{CollectionIssuer, Config as CommonConfig};20use pallet_evm::account::CrossAccountId;20use pallet_evm::account::CrossAccountId;21use sp_std::vec;21use sp_std::vec;22use up_data_structs::{22use up_data_structs::{44 .unwrap();44 .unwrap();45 T::CollectionDispatch::create(45 T::CollectionDispatch::create(46 caller_cross.clone(),46 caller_cross.clone(),47 caller_cross.clone(),47 CollectionIssuer::User(caller_cross.clone()),48 CreateCollectionData {48 CreateCollectionData {49 mode: CollectionMode::NFT,49 mode: CollectionMode::NFT,50 ..Default::default()50 ..Default::default()pallets/unique/src/eth/mod.rsdiffbeforeafterboth26 dispatch::CollectionDispatch,26 dispatch::CollectionDispatch,27 erc::{static_property::key, CollectionHelpersEvents},27 erc::{static_property::key, CollectionHelpersEvents},28 eth::{self, collection_id_to_address, map_eth_to_id},28 eth::{self, collection_id_to_address, map_eth_to_id},29 CollectionById, CollectionHandle, Pallet as PalletCommon,29 CollectionById, CollectionHandle, CollectionIssuer, Pallet as PalletCommon,30};30};31use pallet_evm::{account::CrossAccountId, OnMethodCall, PrecompileHandle, PrecompileResult};31use pallet_evm::{account::CrossAccountId, OnMethodCall, PrecompileHandle, PrecompileResult};32use pallet_evm_coder_substrate::{32use pallet_evm_coder_substrate::{112112113 let collection_id = T::CollectionDispatch::create(caller, collection_helpers_address, data)113 let collection_id = T::CollectionDispatch::create(114 caller,115 CollectionIssuer::User(collection_helpers_address),116 data,117 )114 .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;118 .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;115 let address = pallet_common::eth::collection_id_to_address(collection_id);119 let address = pallet_common::eth::collection_id_to_address(collection_id);242246243 let collection_id = T::CollectionDispatch::create(caller, collection_helpers_address, data)247 let collection_id = T::CollectionDispatch::create(248 caller,249 CollectionIssuer::User(collection_helpers_address),250 data,251 )244 .map_err(dispatch_to_evm::<T>)?;252 .map_err(dispatch_to_evm::<T>)?;245253276 T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());284 T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());277 let collection_id = T::CollectionDispatch::create(caller, collection_helpers_address, data)285 let collection_id = T::CollectionDispatch::create(286 caller,287 CollectionIssuer::User(collection_helpers_address),288 data,289 )278 .map_err(dispatch_to_evm::<T>)?;290 .map_err(dispatch_to_evm::<T>)?;279291pallets/unique/src/eth/stubs/CollectionHelpers.soldiffbeforeafterboth199type CollectionFlags is uint8;199type CollectionFlags is uint8;200200201library CollectionFlagsLib {201library CollectionFlagsLib {202 /// Tokens in foreign collections can be transferred, but not burnt202 /// A collection of foreign assets203 CollectionFlags constant foreignField = CollectionFlags.wrap(128);203 CollectionFlags constant foreignField = CollectionFlags.wrap(128);204 /// Supports ERC721Metadata204 /// Supports ERC721Metadata205 CollectionFlags constant erc721metadataField = CollectionFlags.wrap(64);205 CollectionFlags constant erc721metadataField = CollectionFlags.wrap(64);pallets/unique/src/lib.rsdiffbeforeafterboth93 use frame_system::{ensure_root, ensure_signed};93 use frame_system::{ensure_root, ensure_signed};94 use pallet_common::{94 use pallet_common::{95 dispatch::{dispatch_tx, CollectionDispatch},95 dispatch::{dispatch_tx, CollectionDispatch},96 CollectionHandle, CommonWeightInfo, Pallet as PalletCommon, RefungibleExtensionsWeightInfo,96 CollectionHandle, CollectionIssuer, CommonWeightInfo, Pallet as PalletCommon,97 RefungibleExtensionsWeightInfo,97 };98 };98 use pallet_evm::account::CrossAccountId;99 use pallet_evm::account::CrossAccountId;403 let sender = T::CrossAccountId::from_sub(sender);404 let sender = T::CrossAccountId::from_sub(sender);404 let _id = T::CollectionDispatch::create(sender.clone(), sender, data)?;405 let _id = T::CollectionDispatch::create(406 sender.clone(),407 CollectionIssuer::User(sender),408 data,409 )?;405410406 Ok(())411 Ok(())primitives/data-structs/src/budget.rsdiffbeforeafterboth37 }37 }38}38}3940pub struct ZeroBudget;41impl Budget for ZeroBudget {42 fn consume_custom(&self, _calls: u32) -> bool {43 false44 }45}3946primitives/data-structs/src/lib.rsdiffbeforeafterboth378#[derive(AbiCoderFlags, Bitfields, Clone, Copy, PartialEq, Eq, Debug, Default)]378#[derive(AbiCoderFlags, Bitfields, Clone, Copy, PartialEq, Eq, Debug, Default)]379#[bondrewd(enforce_bytes = 1)]379#[bondrewd(enforce_bytes = 1)]380pub struct CollectionFlags {380pub struct CollectionFlags {381 /// Tokens in foreign collections can be transferred, but not burnt381 /// A collection of foreign assets382 #[bondrewd(bits = "0..1")]382 #[bondrewd(bits = "0..1")]383 pub foreign: bool,383 pub foreign: bool,384 /// Supports ERC721Metadata384 /// Supports ERC721Metadataruntime/common/config/orml.rsdiffbeforeafterboth161617use frame_support::{17use frame_support::{parameter_types, traits::Everything};18 parameter_types,19 traits::{Contains, Everything},20};21use frame_system::EnsureSigned;18use frame_system::EnsureSigned;22use orml_traits::{location::AbsoluteReserveProvider, parameter_type_with_key};19use orml_traits::{location::AbsoluteReserveProvider, parameter_type_with_key};23use pallet_foreign_assets::{CurrencyId, NativeCurrency};20use pallet_foreign_assets::CurrencyIdConvert;24use sp_runtime::traits::Convert;21use sp_runtime::traits::Convert;25use sp_std::{vec, vec::Vec};26use staging_xcm::latest::{Junction::*, Junctions::*, MultiLocation, Weight};22use staging_xcm::latest::{Junction::*, Junctions::*, MultiLocation, Weight};27use staging_xcm_executor::XcmExecutor;23use staging_xcm_executor::XcmExecutor;28use up_common::{24use up_common::{29 constants::*,25 constants::*,30 types::{AccountId, Balance},26 types::{AccountId, Balance},31};27};28use up_data_structs::CollectionId;322933use crate::{30use crate::{34 runtime_common::config::{31 runtime_common::config::xcm::{SelfLocation, UniversalLocation, Weigher, XcmExecutorConfig},35 pallets::TreasuryAccountId,36 substrate::{MaxLocks, MaxReserves},37 xcm::{38 xcm_assets::CurrencyIdConvert, SelfLocation, UniversalLocation, Weigher,39 XcmExecutorConfig,40 },41 },42 RelayChainBlockNumberProvider, Runtime, RuntimeEvent,32 RelayChainBlockNumberProvider, Runtime, RuntimeEvent,43};33};443459 };49 };60}50}6162parameter_type_with_key! {63 pub ExistentialDeposits: |currency_id: CurrencyId| -> Balance {64 match currency_id {65 CurrencyId::NativeAssetId(symbol) => match symbol {66 NativeCurrency::Here => 0,67 NativeCurrency::Parent=> 0,68 },69 _ => 100_00070 }71 };72}7374pub fn get_all_module_accounts() -> Vec<AccountId> {75 vec![TreasuryAccountId::get()]76}7778pub struct DustRemovalWhitelist;79impl Contains<AccountId> for DustRemovalWhitelist {80 fn contains(a: &AccountId) -> bool {81 get_all_module_accounts().contains(a)82 }83}845185pub struct AccountIdToMultiLocation;52pub struct AccountIdToMultiLocation;86impl Convert<AccountId, MultiLocation> for AccountIdToMultiLocation {53impl Convert<AccountId, MultiLocation> for AccountIdToMultiLocation {93 }60 }94}61}9596pub struct CurrencyHooks;97impl orml_traits::currency::MutationHooks<AccountId, CurrencyId, Balance> for CurrencyHooks {98 type OnDust = orml_tokens::TransferDust<Runtime, TreasuryAccountId>;99 type OnSlash = ();100 type PreTransfer = ();101 type PostTransfer = ();102 type PreDeposit = ();103 type PostDeposit = ();104 type OnNewTokenAccount = ();105 type OnKilledTokenAccount = ();106}10762108impl orml_vesting::Config for Runtime {63impl orml_vesting::Config for Runtime {109 type RuntimeEvent = RuntimeEvent;64 type RuntimeEvent = RuntimeEvent;115 type BlockNumberProvider = RelayChainBlockNumberProvider<Runtime>;70 type BlockNumberProvider = RelayChainBlockNumberProvider<Runtime>;116}71}117118impl orml_tokens::Config for Runtime {119 type RuntimeEvent = RuntimeEvent;120 type Balance = Balance;121 type Amount = Amount;122 type CurrencyId = CurrencyId;123 type WeightInfo = ();124 type ExistentialDeposits = ExistentialDeposits;125 type CurrencyHooks = CurrencyHooks;126 type MaxLocks = MaxLocks;127 type MaxReserves = MaxReserves;128 // TODO: Add all module accounts129 type DustRemovalWhitelist = DustRemovalWhitelist;130 /// The id type for named reserves.131 type ReserveIdentifier = ();132}13372134impl orml_xtokens::Config for Runtime {73impl orml_xtokens::Config for Runtime {135 type RuntimeEvent = RuntimeEvent;74 type RuntimeEvent = RuntimeEvent;136 type Balance = Balance;75 type Balance = Balance;137 type CurrencyId = CurrencyId;76 type CurrencyId = CollectionId;138 type CurrencyIdConvert = CurrencyIdConvert;77 type CurrencyIdConvert = CurrencyIdConvert<Self>;139 type AccountIdToMultiLocation = AccountIdToMultiLocation;78 type AccountIdToMultiLocation = AccountIdToMultiLocation;140 type SelfLocation = SelfLocation;79 type SelfLocation = SelfLocation;141 type XcmExecutor = XcmExecutor<XcmExecutorConfig<Self>>;80 type XcmExecutor = XcmExecutor<XcmExecutorConfig<Self>>;runtime/common/config/pallets/foreign_asset.rsdiffbeforeafterboth1use frame_support::{parameter_types, PalletId};2#[cfg(not(feature = "governance"))]3use frame_system::EnsureRoot;1use up_common::types::AccountId;4use pallet_evm::account::CrossAccountId;25use sp_core::H160;6use staging_xcm::prelude::*;7use staging_xcm_builder::AccountKey20Aliases;89#[cfg(feature = "governance")]10use crate::runtime_common::config::governance;3use crate::{Balances, Runtime, RuntimeEvent};11use crate::{12 runtime_common::config::{13 ethereum::CrossAccountId as ConfigCrossAccountId,14 xcm::{LocationToAccountId, SelfLocation},15 },16 RelayNetwork, Runtime, RuntimeEvent,17};1819parameter_types! {20 pub ForeignAssetPalletId: PalletId = PalletId(*b"frgnasts");21}2223pub struct LocationToCrossAccountId;24impl staging_xcm_executor::traits::ConvertLocation<ConfigCrossAccountId>25 for LocationToCrossAccountId26{27 fn convert_location(location: &MultiLocation) -> Option<ConfigCrossAccountId> {28 LocationToAccountId::convert_location(location)29 .map(ConfigCrossAccountId::from_sub)30 .or_else(|| {31 let eth_address =32 AccountKey20Aliases::<RelayNetwork, H160>::convert_location(location)?;3334 Some(ConfigCrossAccountId::from_eth(eth_address))35 })36 }37}4385impl pallet_foreign_assets::Config for Runtime {39impl pallet_foreign_assets::Config for Runtime {6 type RuntimeEvent = RuntimeEvent;40 type RuntimeEvent = RuntimeEvent;4142 #[cfg(feature = "governance")]7 type Currency = Balances;43 type ForceRegisterOrigin = governance::RootOrTechnicalCommitteeMember;4445 #[cfg(not(feature = "governance"))]8 type RegisterOrigin = frame_system::EnsureRoot<AccountId>;46 type ForceRegisterOrigin = EnsureRoot<Self::AccountId>;4748 type PalletId = ForeignAssetPalletId;49 type SelfLocation = SelfLocation;50 type LocationToAccountId = LocationToCrossAccountId;9 type WeightInfo = pallet_foreign_assets::weights::SubstrateWeight<Self>;51 type WeightInfo = pallet_foreign_assets::weights::SubstrateWeight<Self>;10}52}1153runtime/common/config/xcm.rsdiffbeforeafterbothno changes
runtime/common/config/xcm/foreignassets.rsdiffbeforeafterbothno changes
runtime/common/config/xcm/mod.rsdiffbeforeafterbothno changes
runtime/common/config/xcm/nativeassets.rsdiffbeforeafterbothno changes
runtime/common/construct_runtime.rsdiffbeforeafterboth47 Vesting: orml_vesting = 37,47 Vesting: orml_vesting = 37,484849 XTokens: orml_xtokens = 38,49 XTokens: orml_xtokens = 38,50 Tokens: orml_tokens = 39,50 // [REMOVED] Tokens: orml_tokens = 39,51 // Contracts: pallet_contracts::{Pallet, Call, Storage, Event<T>} = 38,51 // Contracts: pallet_contracts::{Pallet, Call, Storage, Event<T>} = 38,525253 #[cfg(feature = "governance")]53 #[cfg(feature = "governance")]runtime/common/dispatch.rsdiffbeforeafterboth17use frame_support::{dispatch::DispatchResult, ensure, fail};17use frame_support::{dispatch::DispatchResult, ensure, fail};18use pallet_balances_adapter::NativeFungibleHandle;18use pallet_balances_adapter::NativeFungibleHandle;19pub use pallet_common::dispatch::CollectionDispatch;19pub use pallet_common::dispatch::CollectionDispatch;20#[cfg(not(feature = "refungible"))]21use pallet_common::unsupported;22use pallet_common::{20use pallet_common::{23 erc::CommonEvmHandler, eth::map_eth_to_id, CollectionById, CollectionHandle,21 erc::CommonEvmHandler, eth::map_eth_to_id, CollectionById, CollectionHandle, CollectionIssuer,24 CommonCollectionOperations,22 CommonCollectionOperations, Pallet as PalletCommon,25};23};26use pallet_evm::{PrecompileHandle, PrecompileResult};24use pallet_evm::{PrecompileHandle, PrecompileResult};27use pallet_fungible::{FungibleHandle, Pallet as PalletFungible};25use pallet_fungible::{FungibleHandle, Pallet as PalletFungible};706871 fn create(69 fn create(72 sender: T::CrossAccountId,70 sender: T::CrossAccountId,73 payer: T::CrossAccountId,71 issuer: CollectionIssuer<T::CrossAccountId>,74 data: CreateCollectionData<T::CrossAccountId>,72 data: CreateCollectionData<T::CrossAccountId>,75 ) -> Result<CollectionId, DispatchError> {73 ) -> Result<CollectionId, DispatchError> {76 let id = match data.mode {74 match data.mode {77 CollectionMode::NFT => <PalletNonfungible<T>>::init_collection(sender, payer, data)?,78 CollectionMode::Fungible(decimal_points) => {75 CollectionMode::Fungible(decimal_points) => {79 // check params76 // check params80 ensure!(77 ensure!(81 decimal_points <= MAX_DECIMAL_POINTS,78 decimal_points <= MAX_DECIMAL_POINTS,82 pallet_unique::Error::<T>::CollectionDecimalPointLimitExceeded79 pallet_unique::Error::<T>::CollectionDecimalPointLimitExceeded83 );80 );84 <PalletFungible<T>>::init_collection(sender, payer, data)?85 }81 }8687 #[cfg(feature = "refungible")]88 CollectionMode::ReFungible => <PalletRefungible<T>>::init_collection(sender, payer, data)?,898290 #[cfg(not(feature = "refungible"))]83 #[cfg(not(feature = "refungible"))]91 CollectionMode::ReFungible => return unsupported!(T),84 CollectionMode::ReFungible => return unsupported!(T),8586 _ => {}92 };87 };8893 Ok(id)89 <PalletCommon<T>>::init_collection(sender, issuer, data)94 }90 }959196 fn destroy(sender: T::CrossAccountId, collection_id: CollectionId) -> DispatchResult {92 fn destroy(sender: T::CrossAccountId, collection_id: CollectionId) -> DispatchResult {runtime/opal/Cargo.tomldiffbeforeafterboth151 'up-rpc/std',151 'up-rpc/std',152 'up-sponsorship/std',152 'up-sponsorship/std',153153154 "orml-tokens/std",155 "orml-traits/std",154 "orml-traits/std",156 "orml-vesting/std",155 "orml-vesting/std",157 "orml-xcm-support/std",156 "orml-xcm-support/std",179 'frame-system/try-runtime',178 'frame-system/try-runtime',180 'frame-try-runtime',179 'frame-try-runtime',181 'frame-try-runtime?/try-runtime',180 'frame-try-runtime?/try-runtime',182 'orml-tokens/try-runtime',183 'orml-vesting/try-runtime',181 'orml-vesting/try-runtime',184 'orml-xtokens/try-runtime',182 'orml-xtokens/try-runtime',185 'pallet-app-promotion/try-runtime',183 'pallet-app-promotion/try-runtime',254frame-support = { workspace = true }252frame-support = { workspace = true }255frame-system = { workspace = true }253frame-system = { workspace = true }256frame-system-rpc-runtime-api = { workspace = true }254frame-system-rpc-runtime-api = { workspace = true }257orml-tokens = { workspace = true }258orml-traits = { workspace = true }255orml-traits = { workspace = true }259orml-vesting = { workspace = true }256orml-vesting = { workspace = true }260orml-xcm-support = { workspace = true }257orml-xcm-support = { workspace = true }runtime/quartz/Cargo.tomldiffbeforeafterboth150 'up-rpc/std',150 'up-rpc/std',151 'up-sponsorship/std',151 'up-sponsorship/std',152152153 "orml-tokens/std",154 "orml-traits/std",153 "orml-traits/std",155 "orml-vesting/std",154 "orml-vesting/std",156 "orml-xcm-support/std",155 "orml-xcm-support/std",175 'frame-support/try-runtime',174 'frame-support/try-runtime',176 'frame-system/try-runtime',175 'frame-system/try-runtime',177 'frame-try-runtime',176 'frame-try-runtime',178 'orml-tokens/try-runtime',179 'orml-vesting/try-runtime',177 'orml-vesting/try-runtime',180 'orml-xtokens/try-runtime',178 'orml-xtokens/try-runtime',181 'pallet-app-promotion/try-runtime',179 'pallet-app-promotion/try-runtime',242frame-support = { workspace = true }240frame-support = { workspace = true }243frame-system = { workspace = true }241frame-system = { workspace = true }244frame-system-rpc-runtime-api = { workspace = true }242frame-system-rpc-runtime-api = { workspace = true }245orml-tokens = { workspace = true }246orml-traits = { workspace = true }243orml-traits = { workspace = true }247orml-vesting = { workspace = true }244orml-vesting = { workspace = true }248orml-xcm-support = { workspace = true }245orml-xcm-support = { workspace = true }runtime/tests/src/tests.rsdiffbeforeafterboth2357#[test]2357#[test]2358fn total_number_collections_bound_neg() {2358fn total_number_collections_bound_neg() {2359 new_test_ext().execute_with(|| {2359 new_test_ext().execute_with(|| {2360 let user = 1;2360 let origin1 = RuntimeOrigin::signed(1);2361 let origin1 = RuntimeOrigin::signed(user);236123622362 for i in 1..=COLLECTION_NUMBER_LIMIT {2363 for i in 1..=COLLECTION_NUMBER_LIMIT {2363 create_test_collection(&CollectionMode::NFT, CollectionId(i));2364 create_test_collection(&CollectionMode::NFT, CollectionId(i));2376 };2377 };237723782378 // 11-th collection in chain. Expects error2379 // 11-th collection in chain. Expects error2380 add_balance(user, CollectionCreationPrice::get() as u64 + 1);2379 assert_noop!(2381 assert_noop!(2380 Unique::create_collection_ex(origin1, data),2382 Unique::create_collection_ex(origin1, data),2381 CommonError::<Test>::TotalCollectionsLimitExceeded2383 CommonError::<Test>::TotalCollectionsLimitExceededruntime/unique/Cargo.tomldiffbeforeafterboth147 'up-rpc/std',147 'up-rpc/std',148 'up-sponsorship/std',148 'up-sponsorship/std',149149150 "orml-tokens/std",151 "orml-traits/std",150 "orml-traits/std",152 "orml-vesting/std",151 "orml-vesting/std",153 "orml-xcm-support/std",152 "orml-xcm-support/std",173 'frame-support/try-runtime',172 'frame-support/try-runtime',174 'frame-system/try-runtime',173 'frame-system/try-runtime',175 'frame-try-runtime',174 'frame-try-runtime',176 'orml-tokens/try-runtime',177 'orml-vesting/try-runtime',175 'orml-vesting/try-runtime',178 'orml-xtokens/try-runtime',176 'orml-xtokens/try-runtime',179 'pallet-app-promotion/try-runtime',177 'pallet-app-promotion/try-runtime',245frame-support = { workspace = true }243frame-support = { workspace = true }246frame-system = { workspace = true }244frame-system = { workspace = true }247frame-system-rpc-runtime-api = { workspace = true }245frame-system-rpc-runtime-api = { workspace = true }248orml-tokens = { workspace = true }249orml-traits = { workspace = true }246orml-traits = { workspace = true }250orml-vesting = { workspace = true }247orml-vesting = { workspace = true }251orml-xcm-support = { workspace = true }248orml-xcm-support = { workspace = true }