git.delta.rocks / unique-network / refs/commits / 6a580826c34a

difftreelog

Merge pull request #1024 from UniqueNetwork/refactor/foreign-assets-as-thin-proxy

Yaroslav Bolyukin2023-11-29parents: #bfab603 #d561386.patch.diff
in: master
Refactor/foreign assets as thin proxy

50 files changed

modified.envdiffbeforeafterboth
1616
17KUSAMA_MAINNET_BRANCH=release-v1.0.017KUSAMA_MAINNET_BRANCH=release-v1.0.0
18STATEMINE_BUILD_BRANCH=release-parachains-v943018STATEMINE_BUILD_BRANCH=release-parachains-v9430
19KARURA_BUILD_BRANCH=release-karura-2.21.019KARURA_BUILD_BRANCH=xnft-poc
20MOONRIVER_BUILD_BRANCH=runtime-250020MOONRIVER_BUILD_BRANCH=runtime-2500
21SHIDEN_BUILD_BRANCH=v5.18.021SHIDEN_BUILD_BRANCH=v5.18.0
22QUARTZ_MAINNET_BRANCH=release-v1001006322QUARTZ_MAINNET_BRANCH=release-v10010063
modified.github/workflows/ci-develop.ymldiffbeforeafterboth
36 uses: ./.github/workflows/xcm.yml36 uses: ./.github/workflows/xcm.yml
37 secrets: inherit37 secrets: inherit
38 38
39 xnft:
40 if: github.event.pull_request.draft == false && contains(github.event.pull_request.labels.*.name, 'CI-xnft')
41 uses: ./.github/workflows/xnft.yml
42 secrets: inherit
43
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')
modified.github/workflows/ci-master.ymldiffbeforeafterboth
32 uses: ./.github/workflows/xcm.yml32 uses: ./.github/workflows/xcm.yml
33 secrets: inherit # pass all secrets from initial workflow to nested33 secrets: inherit # pass all secrets from initial workflow to nested
34 34
35 xnft:
36 uses: ./.github/workflows/xnft.yml
37 secrets: inherit # pass all secrets from initial workflow to nested
38
35 collator-selection:39 collator-selection:
36 uses: ./.github/workflows/collator-selection.yml40 uses: ./.github/workflows/collator-selection.yml
added.github/workflows/xnft.ymldiffbeforeafterboth

no changes

modifiedCargo.lockdiffbeforeafterboth
25672567
2568[[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"
25732573
2574[[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]
25832583
2584[[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",
46784678
4679[[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]
46884688
4689[[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]
6707
6708[[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]
67246706
6725[[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",
67446726
6745[[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",
67596741
6760[[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",
67746756
6775[[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",
67886770
6789[[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",
1369513675
13696[[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",
modifiedCargo.tomldiffbeforeafterboth
199try-runtime-cli = "0.35.0"199try-runtime-cli = "0.35.0"
200200
201# ORML201# ORML
202orml-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" }
modifiedjs-packages/playgrounds/types.tsdiffbeforeafterboth
160 External = 1,160 External = 1,
161 /// Supports ERC721Metadata161 /// Supports ERC721Metadata
162 Erc721metadata = 64,162 Erc721metadata = 64,
163 /// Tokens in foreign collections can be transferred, but not burnt163 /// A collection of foreign assets
164 Foreign = 128,164 Foreign = 128,
165}165}
166166
modifiedjs-packages/playgrounds/types.xcm.tsdiffbeforeafterboth
28 },28 },
29}29}
3030
31export interface IForeignAssetMetadata {
32 name?: number | Uint8Array,
33 symbol?: string,
34 decimals?: number,
35 minimalBalance?: bigint,
36}
modifiedjs-packages/playgrounds/unique.xcm.tsdiffbeforeafterboth
2import 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';
66
77
8export class XcmChainHelper extends ChainHelperBase {8export class XcmChainHelper extends ChainHelperBase {
104}104}
105105
106export 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 }
115115
116 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 }
253
254 async assetInfo(assetId: number | bigint) {
255 return (await this.helper.callRpc('api.query.assets.asset', [assetId])).toJSON();
256 }
258257
259 async account(assetId: string | number | bigint, address: string) {258 async account(assetId: string | number | bigint, address: string) {
260 const accountAsset = (259 const accountAsset = (
modifiedjs-packages/tests/eth/api/CollectionHelpers.soldiffbeforeafterboth
132type CollectionFlags is uint8;132type CollectionFlags is uint8;
133133
134library CollectionFlagsLib {134library CollectionFlagsLib {
135 /// Tokens in foreign collections can be transferred, but not burnt135 /// A collection of foreign assets
136 CollectionFlags constant foreignField = CollectionFlags.wrap(128);136 CollectionFlags constant foreignField = CollectionFlags.wrap(128);
137 /// Supports ERC721Metadata137 /// Supports ERC721Metadata
138 CollectionFlags constant erc721metadataField = CollectionFlags.wrap(64);138 CollectionFlags constant erc721metadataField = CollectionFlags.wrap(64);
modifiedjs-packages/tests/pallet-presence.test.tsdiffbeforeafterboth
47 'nonfungible',47 'nonfungible',
48 'charging',48 'charging',
49 'configuration',49 'configuration',
50 'tokens',
51 'xtokens',50 'xtokens',
52 'maintenance',51 'maintenance',
53];52];
modifiedjs-packages/tests/xcm/xcm.types.tsdiffbeforeafterboth
506 });506 });
507 }507 }
508
509 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};
516
517 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});
523
524 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}
509532
modifiedjs-packages/tests/xcm/xcmOpal.test.tsdiffbeforeafterboth
17import 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';
2021
21const 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;
2627
27const 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;
3334
34const RELAY_DECIMALS = 12;35const RELAY_DECIMALS = 12;
35const WESTMINT_DECIMALS = 12;36const WESTMINT_DECIMALS = 12;
3637
37const TRANSFER_AMOUNT = 1_000_000_000_000_000_000n;38const TRANSFER_AMOUNT = 1_000_000_000_000_000_000n;
3839
39// 10,000.00 (ten thousands) USDT40// 10,000.00 (ten thousands) USDT
40const ASSET_AMOUNT = 1_000_000_000_000_000_000_000n;41const USDT_ASSET_AMOUNT = 1_000_000_000_000_000_000_000n;
42
43const testHelper = new XcmTestHelper('opal');
4144
42describeXCM('[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;
5962
63 let usdtCollectionId: number;
64 let relayCollectionId: number;
6065
61 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 donor
70
71 relayCollectionId = await testHelper.registerRelayNativeTokenOnUnique(alice);
65 });72 });
6673
67 await usingWestmintPlaygrounds(westmintUrl, async (helper) => {74 await usingWestmintPlaygrounds(westmintUrl, async (helper) => {
68 // 350.00 (three hundred fifty) DOT
69 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 }
93
73 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 );
100
101 const sovereignFundingAmount = 3_500_000_000n;
74102
75 // 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 });
79
80107
81 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 };
98
99 const metadata =123 const assetId = {Concrete: location};
100 {124
101 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 }
137
107 balanceOpalBefore = await helper.balance.getSubstrate(alice.address);138 balanceOpalBefore = await helper.balance.getSubstrate(alice.address);
139 usdtCollectionId = await helper.foreignAssets.foreignCollectionId(assetId);
108 });140 });
109
110141
189 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 delivered
222 await helper.wait.newBlocks(3);252 await helper.wait.newBlocks(3);
223253
224 // expext collection id will be with id 1
225 const free = await helper.ft.getBalance(1, {Substrate: alice.address});254 const free = await helper.ft.getBalance(usdtCollectionId, {Substrate: alice.address});
226255
227 balanceOpalAfter = await helper.balance.getSubstrate(alice.address);256 balanceOpalAfter = await helper.balance.getSubstrate(alice.address);
228257
229 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',
261290
262 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 ];
288312
289 // 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 succeeded
291 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 });
294318
295 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;
297321
298 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});
300324
301 // Providing the relay currency to the unique sender account325 // Providing the relay currency to the unique sender account
302 await usingRelayPlaygrounds(relayUrl, async (helper) => {326 await usingRelayPlaygrounds(relayUrl, async (helper) => {
345 await helper.wait.newBlocks(3);369 await helper.wait.newBlocks(3);
346370
347 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});
349373
350 const wndFee = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;374 const wndFee = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;
351 console.log(375 console.log(
383407
384 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 ];
modifiedjs-packages/tests/xcm/xcmQuartz.test.tsdiffbeforeafterboth
19import {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';
23
2423
25const STATEMINE_PALLET_INSTANCE = 50;24const STATEMINE_PALLET_INSTANCE = 50;
3938
40const SAFE_XCM_VERSION = 2;39const SAFE_XCM_VERSION = 2;
40
41const testHelper = new XcmTestHelper('quartz');
4142
42describeXCM('[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;
5960
61 let usdtCollectionId: number;
62 let relayCollectionId: number;
6063
61 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);
71
72 relayCollectionId = await testHelper.registerRelayNativeTokenOnUnique(alice);
68 });73 });
6974
70 await usingRelayPlaygrounds(relayUrl, async (helper) => {75 await usingRelayPlaygrounds(relayUrl, async (helper) => {
74 });79 });
7580
76 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 }
100
92 await helper.assets.mint(101 await helper.assets.mint(
93 alice,102 alice,
96 USDT_ASSET_AMOUNT,105 USDT_ASSET_AMOUNT,
97 );106 );
107
108 const sovereignFundingAmount = 3_500_000_000n;
98109
99 // 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 action
106117
107 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 };
124
125 const metadata =133 const assetId = {Concrete: location};
126 {134
127 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 }
141
133 balanceQuartzBefore = await helper.balance.getSubstrate(alice.address);142 balanceQuartzBefore = await helper.balance.getSubstrate(alice.address);
143 usdtCollectionId = await helper.foreignAssets.foreignCollectionId(assetId);
134 });144 });
135145
136146
248 // ensure that asset has been delivered258 // ensure that asset has been delivered
249 await helper.wait.newBlocks(3);259 await helper.wait.newBlocks(3);
250260
251 // expext collection id will be with id 1
252 const free = await helper.ft.getBalance(1, {Substrate: alice.address});261 const free = await helper.ft.getBalance(usdtCollectionId, {Substrate: alice.address});
253262
254 balanceQuartzAfter = await helper.balance.getSubstrate(alice.address);263 balanceQuartzAfter = await helper.balance.getSubstrate(alice.address);
255264
288 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 ];
321326
322 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});
325330
326 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);
370375
371 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});
373378
374 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;
409414
410 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 });
10191022
1020 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,
modifiedjs-packages/tests/xcm/xcmUnique.test.tsdiffbeforeafterboth
20import {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';
2424
25const STATEMINT_PALLET_INSTANCE = 50;25const STATEMINT_PALLET_INSTANCE = 50;
2626
50const 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;
52
53const testHelper = new XcmTestHelper('unique');
5254
53describeXCM('[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;
7072
73 let usdtCollectionId: number;
74 let relayCollectionId: number;
7175
72 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);
83
84 relayCollectionId = await testHelper.registerRelayNativeTokenOnUnique(alice);
79 });85 });
8086
81 await usingRelayPlaygrounds(relayUrl, async (helper) => {87 await usingRelayPlaygrounds(relayUrl, async (helper) => {
85 });91 });
8692
87 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 }
112
103 await helper.assets.mint(113 await helper.assets.mint(
104 alice,114 alice,
107 USDT_ASSET_AMOUNT,117 USDT_ASSET_AMOUNT,
108 );118 );
119
120 const sovereignFundingAmount = 3_500_000_000n;
109121
110 // 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 action
117129
118 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 };
135
136 const metadata =145 const assetId = {Concrete: location};
137 {146
138 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 }
153
144 balanceUniqueBefore = await helper.balance.getSubstrate(alice.address);154 balanceUniqueBefore = await helper.balance.getSubstrate(alice.address);
155 usdtCollectionId = await helper.foreignAssets.foreignCollectionId(assetId);
145 });156 });
146157
147158
259 // ensure that asset has been delivered270 // ensure that asset has been delivered
260 await helper.wait.newBlocks(3);271 await helper.wait.newBlocks(3);
261272
262 // expext collection id will be with id 1
263 const free = await helper.ft.getBalance(1, {Substrate: alice.address});273 const free = await helper.ft.getBalance(usdtCollectionId, {Substrate: alice.address});
264274
265 balanceUniqueAfter = await helper.balance.getSubstrate(alice.address);275 balanceUniqueAfter = await helper.balance.getSubstrate(alice.address);
266276
299 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 ];
332338
333 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});
336342
337 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);
381387
382 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});
384390
385 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;
420426
421 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 });
12761280
1277 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,
modifiednode/cli/src/chain_spec.rsdiffbeforeafterboth
167 .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 },
modifiedpallets/balances-adapter/src/common.rsdiffbeforeafterboth
33
4use 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};
814
9use crate::{Config, NativeFungibleHandle, Pallet};15use crate::{Config, NativeFungibleHandle, Pallet};
1016
332 0338 0
333 }339 }
334340
335 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 }
338344
339 fn set_allowance_for_all(345 fn set_allowance_for_all(
361 }367 }
362}368}
369
370impl<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_data
383 .value
384 .try_into()
385 .map_err(|_| sp_runtime::ArithmeticError::Overflow)?,
386 )?;
387
388 Ok(TokenId::default())
389 }
390 _ => {
391 fail!(<CommonError<T>>::NotFungibleDataUsedToMintFungibleCollectionToken)
392 }
393 }
394 }
395
396 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>>::FungibleItemsHaveNoId
408 );
409
410 <Pallet<T>>::transfer(from, to, amount)
411 .map(|_| ())
412 .map_err(|post_info| post_info.error)
413 }
414
415 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>>::FungibleItemsHaveNoId
424 );
425
426 T::Mutate::burn_from(
427 from.as_sub(),
428 amount
429 .try_into()
430 .map_err(|_| sp_runtime::ArithmeticError::Overflow)?,
431 Precision::Exact,
432 Fortitude::Polite,
433 )?;
434
435 Ok(())
436 }
437}
363438
modifiedpallets/common/src/benchmarking.rsdiffbeforeafterboth
33 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};
3535
36use crate::{BenchmarkPropertyWriter, CollectionHandle, Config, Pallet};36use crate::{BenchmarkPropertyWriter, CollectionHandle, CollectionIssuer, Config, Pallet};
3737
38const SEED: u32 = 1;38const SEED: u32 = 1;
3939
120 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}
modifiedpallets/common/src/dispatch.rsdiffbeforeafterboth
11use sp_weights::Weight;11use sp_weights::Weight;
12use up_data_structs::{CollectionId, CreateCollectionData};12use up_data_structs::{CollectionId, CreateCollectionData};
1313
14use crate::{pallet::Config, CommonCollectionOperations};14use crate::{pallet::Config, CollectionIssuer, CommonCollectionOperations};
1515
16// TODO: move to benchmarking16// TODO: move to benchmarking
17/// Price of [`dispatch_tx`] call with noop `call` argument17/// Price of [`dispatch_tx`] call with noop `call` argument
69 /// 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;
7171
72 /// 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>;
8182
modifiedpallets/common/src/lib.rsdiffbeforeafterboth
80use 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,
790
791 /// Not Fungible item data used to mint in Fungible collection.
792 NotFungibleDataUsedToMintFungibleCollectionToken,
789 }793 }
790794
791 /// 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}
946
947/// An issuer of a collection.
948pub enum CollectionIssuer<CrossAccountId> {
949 /// A user who creates the collection.
950 User(CrossAccountId),
951
952 /// The internal mechanisms are creating the collection.
953 Internals,
954}
942955
943fn check_token_permissions<T: Config>(956fn check_token_permissions<T: Config>(
944 collection_admin_permitted: bool,957 collection_admin_permitted: bool,
1121}1134}
11221135
1123impl<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 }
1137
1138 /// 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 }1150
11481151 // Take a (non-refundable) deposit of collection creation
1149 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)?;
1164
1165 debug_assert!(credit.peek().is_zero());
1166 }
1150 owner: T::CrossAccountId,1167 CollectionIssuer::Internals => {}
1151 payer: T::CrossAccountId,1168 }
1152 data: CreateCollectionData<T::CrossAccountId>,1169
1153 ) -> 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);
1227
1228 // Take a (non-refundable) deposit of collection creation
1229 {
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)?;
1239
1240 debug_assert!(credit.peek().is_zero())
1241 }
12421243
1243 <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 }
12591260
1260 /// Destroy collection.1261 /// Destroy collection.
1261 ///1262 ///
2293 ) -> u128;2294 ) -> u128;
22942295
2295 /// 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 None
2299 }
2300
2301 /// Get XCM extensions.
2302 fn xcm_extensions(&self) -> Option<&dyn XcmExtensions<T>> {
2303 None
2304 }
22972305
2298 /// 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 owner
2333 ) -> DispatchResultWithPostInfo;2341 ) -> DispatchResultWithPostInfo;
2334}2342}
2343
2344/// XCM extensions for fungible and NFT collections
2345pub trait XcmExtensions<T>
2346where
2347 T: Config,
2348{
2349 /// Does the token have children?
2350 fn token_has_children(&self, _token: TokenId) -> bool {
2351 false
2352 }
2353
2354 /// 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 messages
2359 /// are processed in a non-transactional context.
2360 /// To perform the needed logic, we use the internal pallets' functions
2361 /// 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/490
2365 #[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 }
2376
2377 self.create_item_internal(depositor, to, data, nesting_budget)
2378 }
2379
2380 /// 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>;
2388
2389 /// 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 messages
2394 /// are processed in a non-transactional context.
2395 /// To perform the needed logic, we use the internal pallets' functions
2396 /// 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/490
2400 #[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 }
2413
2414 if self.token_has_children(token) {
2415 return unsupported!(T);
2416 }
2417
2418 self.transfer_item_internal(depositor, from, to, token, amount, nesting_budget)
2419 }
2420
2421 /// 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;
2431
2432 /// Burn a collection's item using a transaction.
2433 ///
2434 /// The `transactional` attribute is needed because the inbound XCM messages
2435 /// are processed in a non-transactional context.
2436 /// To perform the needed logic, we use the internal pallets' functions
2437 /// 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/490
2441 #[transactional]
2442 fn burn_item(&self, from: T::CrossAccountId, token: TokenId, amount: u128) -> DispatchResult {
2443 self.burn_item_internal(from, token, amount)
2444 }
2445
2446 /// 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}
23352454
2336/// Merge [`DispatchResult`] with [`Weight`] into [`DispatchResultWithPostInfo`].2455/// Merge [`DispatchResult`] with [`Weight`] into [`DispatchResultWithPostInfo`].
2337///2456///
modifiedpallets/foreign-assets/Cargo.tomldiffbeforeafterboth
12frame-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",
modifiedpallets/foreign-assets/src/benchmarking.rsdiffbeforeafterboth
1616
17#![allow(missing_docs)]17#![allow(missing_docs)]
1818
19use 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};
2425
25use super::{Call, Config, Pallet};26use super::{Call, Config, ForeignCollectionMode, Pallet};
26use crate::AssetMetadata;
27
28fn 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}
3327
34#[benchmarks]28#[benchmarks]
35mod benchmarks {29mod benchmarks {
30
36 use super::*;31 use super::*;
3732
38 #[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;
5540
56 #[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 );
6349
64 Ok(())50 Ok(())
65 }51 }
66
67 #[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 )?;
98
99 #[extrinsic_call]
100 _(RawOrigin::Root, 0, Box::new(location), Box::new(metadata2));
101
102 Ok(())
103 }
104}52}
10553
deletedpallets/foreign-assets/src/impl_fungibles.rsdiffbeforeafterboth

no changes

modifiedpallets/foreign-assets/src/lib.rsdiffbeforeafterboth
14// You should have received a copy of the GNU General Public License14// You should have received a copy of the GNU General Public License
15// 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/>.
1616
17//! # Foreign assets17//! # Foreign Assets
18//!18//!
19//! - [`Config`]
20//! - [`Call`]
21//! - [`Pallet`]
22//!
23//! ## Overview19//! ## Overview
24//!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//! ## Overview
31//!
32//! Under construction
3322
34#![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)]
3625
37use frame_support::{26use core::ops::Deref;
27
38 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 the
55// 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};
6248
63// TODO: Move to primitives
64// Id of native currency.
65// 0 - QTZ\UNQ
66// 1 - KSM\DOT
67#[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}
86
87#[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}
106
107pub trait TryAsForeign<T, F> {
108 fn try_as_foreign(asset: T) -> Option<F>;
109}
110
111impl 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}
119
120pub type ForeignAssetId = u32;
121pub type CurrencyId = AssetId;
122
123mod impl_fungibles;
124pub mod weights;49pub mod weights;
12550
126#[cfg(feature = "runtime-benchmarks")]51#[cfg(feature = "runtime-benchmarks")]
129pub use module::*;54pub use module::*;
130pub use weights::WeightInfo;55pub use weights::WeightInfo;
13156
132/// Type alias for currency balance.
133pub type BalanceOf<T> =
134 <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
135
136/// 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}
145
146pub struct XcmForeignAssetIdMapping<T>(sp_std::marker::PhantomData<T>);
147
148impl<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 }
155
156 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 }
160
161 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}
166
167#[frame_support::pallet]57#[frame_support::pallet]
168pub mod module {58pub mod module {
59 use pallet_common::CollectionIssuer;
60 use up_data_structs::CollectionDescription;
61
169 use super::*;62 use super::*;
17063
171 #[pallet::config]64 #[pallet::config]
172 pub trait Config:65 pub trait Config:
173 frame_system::Config66 frame_system::Config
174 + pallet_common::Config67 + pallet_common::Config
175 + pallet_fungible::Config68 + pallet_fungible::Config
176 + orml_tokens::Config
177 + pallet_balances::Config69 + pallet_balances::Config
178 {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>;
18173
182 /// 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>;
18476
185 /// Required origin for registering asset.77 /// The ID of the foreign assets pallet.
186 type RegisterOrigin: EnsureOrigin<Self::RuntimeOrigin>;78 type PalletId: Get<PalletId>;
18779
80 /// Self-location of this parachain.
81 type SelfLocation: Get<MultiLocation>;
82
83 /// The converter from a MultiLocation to a CrossAccountId.
84 type LocationToAccountId: ConvertLocation<Self::CrossAccountId>;
85
188 /// 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 }
19189
192 pub type AssetName = BoundedVec<u8, ConstU32<32>>;
193 pub type AssetSymbol = BoundedVec<u8, ConstU32<7>>;
194
195 #[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 }
202
203 #[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,94
208 /// MultiLocation existed95 /// The given asset ID could not be converted into the current XCM version.
209 MultiLocationExisted,
210 /// AssetId not exists
211 AssetIdNotExists,
212 /// AssetId exists
213 AssetIdExisted,96 BadForeignAssetId,
214 }97 }
21598
216 #[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 }
242108
243 /// Next available Foreign AssetId ID.109 /// The corresponding collections of foreign assets.
244 ///
245 /// NextForeignAssetId: ForeignAssetId
246 #[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>;
256114
257 /// 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>;
264120
265 /// The storages for AssetMetadatas.121 /// The correponding NFT token id of reserve NFTs
266 ///
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 >;
272132
273 /// The storages for assets to fungible collection binding133 /// The correponding reserve NFT of a token ID
274 ///
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 >;
279144
280 #[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())?;
294160
295 let location: MultiLocation = (*location)161 let asset_id: AssetId = versioned_asset_id
162 .as_ref()
163 .clone()
296 .try_into()164 .try_into()
297 .map_err(|()| Error::<T>::BadLocation)?;165 .map_err(|()| Error::<T>::BadForeignAssetId)?;
298166
299 let md = metadata.clone();167 ensure!(
168 !<ForeignAssetToCollection<T>>::contains_key(asset_id),
169 <Error<T>>::ForeignAssetAlreadyRegistered,
170 );
171
300 let name: Vec<u16> = md.name.into_iter().map(|x| x as u16).collect::<Vec<u16>>();172 let foreign_collection_owner = Self::pallet_account();
173
301 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");
305179
306 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 )?;
317191
192 <ForeignAssetToCollection<T>>::insert(asset_id, collection_id);
193 <CollectionToForeignAsset<T>>::insert(collection_id, asset_id);
194
318 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 }
325199
326 #[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)?;
335
336 let location: MultiLocation = (*location)
337 .try_into()
338 .map_err(|()| Error::<T>::BadLocation)?;
339 Self::do_update_foreign_asset(foreign_asset_id, &location, &metadata)?;
340
341 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}
350204
351impl<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 = current
356 .checked_add(One::one())
357 .ok_or(ArithmeticError::Overflow)?;
358 Ok(id)208 T::CrossAccountId::from_sub(owner)
359 })
360 }209 }
361210
362 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 corresponds
366 ) -> 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 };
375228
376 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);
381230
382 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 )?;
392234
393 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 &Here
237 } else if asset_location.parents == self_location.parents {
395 Ok(())238 &self_location.interior
239 } else {
396 })240 return None;
397 })?;241 };
398242
399 Ok(foreign_asset_id)243 let GeneralIndex(collection_id) = asset_location.interior.match_and_split(prefix)? else {
244 return None;
245 };
246
247 let collection_id = CollectionId((*collection_id).try_into().ok()?);
248
249 Self::collection_to_foreign_asset(collection_id)
250 .is_none()
251 .then_some(CollectionLocality::Local(collection_id))
400 }252 }
401253
402 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 corresponding
257 /// 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 check
261 /// 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 }
271
272 /// 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 }
288
289 /// 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 }
304
305 /// 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 )?;
413318
414 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>::AssetIdNotExists
420 );323 );
421324
422 // 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 {330
428 ensure!(331 Ok(())
429 maybe_currency_ids.is_none(),332 }
430 Error::<T>::MultiLocationExisted333
431 );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 account
434 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);
345
346 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;
351
352 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 };
361
362 deposit_result
363 .map_err(|_| XcmError::FailedToTransactAsset("non-fungible item deposit failed"))
364 }
365
366 /// 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)?;
377
378 let depositor = &from;
379 let to = Self::pallet_account();
380 let amount = 1;
381 xcm_ext
382 .transfer_item(depositor, &from, &to, token_id, amount, &ZeroBudget)
383 .map_err(|_| XcmError::FailedToTransactAsset("non-fungible item withdraw failed"))?;
384
385 Ok(())
386 }
387}
388
389impl<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 }
397
398 fn check_in(_origin: &MultiLocation, _what: &MultiAsset, _context: &XcmContext) {}
399
400 fn can_check_out(
401 _dest: &MultiLocation,
402 _what: &MultiAsset,
403 _context: &XcmContext,
404 ) -> XcmResult {
405 Err(XcmError::Unimplemented)
406 }
407
408 fn check_out(_dest: &MultiLocation, _what: &MultiAsset, _context: &XcmContext) {}
409
410 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)?;
417
418 let collection_locality = Self::asset_to_collection(&what.id)?;
419 let dispatch = T::CollectionDispatch::dispatch(*collection_locality)
420 .map_err(|_| XcmExecutorError::AssetIdConversionFailed)?;
421
422 let collection = dispatch.as_dyn();
423 let xcm_ext = collection.xcm_extensions().ok_or(XcmError::Unimplemented)?;
424
425 match what.fun {
426 Fungibility::Fungible(amount) => xcm_ext
427 .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")),
435
444 )436 Fungibility::NonFungible(asset_instance) => {
437 Self::deposit_asset_instance(xcm_ext, collection_locality, &asset_instance, to)
438 }
439 }
445 }440 }
441
442 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)?;
449
450 let collection_locality = Self::asset_to_collection(&what.id)?;
451 let dispatch = T::CollectionDispatch::dispatch(*collection_locality)
452 .map_err(|_| XcmExecutorError::AssetIdConversionFailed)?;
453
454 let collection = dispatch.as_dyn();
455 let xcm_ext = collection.xcm_extensions().ok_or(XcmError::NoPermission)?;
456
457 match what.fun {
458 Fungibility::Fungible(amount) => xcm_ext
459 .burn_item(from, TokenId::default(), amount)
460 .map_err(|_| XcmError::FailedToTransactAsset("fungible item withdraw failed"))?,
461
462 Fungibility::NonFungible(asset_instance) => {
463 Self::withdraw_asset_instance(xcm_ext, collection_locality, &asset_instance, from)?;
464 }
465 }
466
467 Ok(what.clone().into())
468 }
469
470 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)?;
478
479 let to = T::LocationToAccountId::convert_location(to)
480 .ok_or(XcmExecutorError::AccountIdConversionFailed)?;
481
482 let collection_locality = Self::asset_to_collection(&what.id)?;
483
484 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)?;
488
489 let depositor = &from;
490
491 let token_id;
492 let amount;
493 let map_error: fn(DispatchError) -> XcmError;
494
495 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 }
501
502 Fungibility::NonFungible(asset_instance) => {
503 token_id = Self::asset_instance_to_token_id(collection_locality, &asset_instance)
504 .ok_or(XcmExecutorError::InstanceConversionFailed)?;
505
506 amount = 1;
507 map_error = |_| XcmError::FailedToTransactAsset("non-fungible item transfer failed")
508 }
509 }
510
511 xcm_ext
512 .transfer_item(depositor, &from, &to, token_id, amount, &ZeroBudget)
513 .map_err(map_error)?;
514
515 Ok(what.clone().into())
516 }
446}517}
447518
448pub 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}
454524
455pub 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);
466527
467impl<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}
535
536pub 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}
557
558#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]
559pub enum ForeignCollectionMode {
560 NFT,
561 Fungible(u8),
562}
563
564impl 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}
572
573pub struct FreeForAll;
574
575impl WeightTrader for FreeForAll {
475 fn new() -> Self {576 fn new() -> Self {
476 Self(Weight::default(), Zero::zero(), PhantomData)577 Self
477 }578 }
478579
479 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> Drop
490 for FreeForAll<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>
491where
492 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}
501590
modifiedpallets/fungible/src/benchmarking.rsdiffbeforeafterboth
1616
17use 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}
modifiedpallets/fungible/src/common.rsdiffbeforeafterboth
19use 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 }
120120
133 .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 }
139139
152 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 };
157157
158 with_weight(158 with_weight(
428 <Allowance<T>>::get((self.id, sender, spender))428 <Allowance<T>>::get((self.id, sender, spender))
429 }429 }
430
431 fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>> {
432 None
433 }
434430
435 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 }
437
438 fn xcm_extensions(&self) -> Option<&dyn XcmExtensions<T>> {
439 Some(self)
440 }
441441
442 fn set_allowance_for_all(442 fn set_allowance_for_all(
443 &self,443 &self,
458 }458 }
459}459}
460
461impl<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 }
480
481 Ok(TokenId::default())
482 }
483
484 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>>::FungibleItemsHaveNoId
496 );
497
498 <Pallet<T>>::transfer_internal(self, depositor, from, to, amount, nesting_budget)
499 .map(|_| ())
500 .map_err(|post_info| post_info.error)
501 }
502
503 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}
460514
modifiedpallets/fungible/src/lib.rsdiffbeforeafterboth
95use 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;
121121
122 #[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.
212210
213/// Pallet implementation for fungible assets211/// Pallet implementation for fungible assets
214impl<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 }
223
224 /// 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 }
232
233 /// 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)?;
296
297 // Foreign collection check
298 ensure!(!collection.flags.foreign, <CommonError<T>>::NoPermission);
299276
300 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 }
330
331 /// 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)?;
340
341 let balance = <Balance<T>>::get((collection.id, owner))
342 .checked_sub(amount)
343 .ok_or(<CommonError<T>>::TokenValueTooLow)?;
344 // =========
345
346 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);
353
354 <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 }
370307
371 /// Transfers the specified amount of tokens. Will check that308 /// Transfers the specified amount of tokens. Will check that
372 /// the transfer is allowed for the token.309 /// the transfer is allowed for the token.
468 }405 }
469406
470 /// 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>>::PublicMintingNotAllowed
419 );
420 collection.check_allowlist(depositor)?;
421
422 for (owner, _) in data.iter() {
423 collection.check_allowlist(owner)?;
424 }
425 }
426
479 let total_supply = data427 let total_supply = data
480 .values()428 .values()
486434
487 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 }
535
536 /// 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 check
545 ensure!(!collection.flags.foreign, <CommonError<T>>::NoPermission);
546
547 if !collection.is_owner_or_admin(sender) {
548 ensure!(
549 collection.permissions.mint_mode(),
550 <CommonError<T>>::PublicMintingNotAllowed
551 );
552 collection.check_allowlist(sender)?;
553
554 for (owner, _) in data.iter() {
555 collection.check_allowlist(owner)?;
556 }
557 }
558
559 Self::create_multiple_items_common(collection, sender, data, nesting_budget)
560 }
561
562 /// 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 }
572483
573 fn set_allowance_unchecked(484 fn set_allowance_unchecked(
574 collection: &FungibleHandle<T>,485 collection: &FungibleHandle<T>,
802 )713 )
803 }714 }
804
805 /// Creates fungible token.
806 ///
807 /// - `data`: Contains user who will become the owners of the tokens and amount
808 /// 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 }
822715
823 /// Returns 10 tokens owners in no particular order716 /// Returns 10 tokens owners in no particular order
824 ///717 ///
modifiedpallets/nonfungible/src/benchmarking.rsdiffbeforeafterboth
18use 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}
modifiedpallets/nonfungible/src/common.rsdiffbeforeafterboth
19use 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 }
537537
538 fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>> {
539 None
540 }
541
542 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 }
549545
546 fn xcm_extensions(&self) -> Option<&dyn XcmExtensions<T>> {
547 Some(self)
548 }
549
550 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}
573
574impl<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 }
578
579 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 )?;
592
593 Ok(self.last_token_id())
594 }
595
596 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);
606
607 <Pallet<T>>::transfer_internal(self, depositor, from, to, token, nesting_budget)
608 .map(|_| ())
609 .map_err(|post_info| post_info.error)
610 }
611
612 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);
619
620 <Pallet<T>>::burn(self, &from, token)
571 }621 }
572}622}
573623
modifiedpallets/nonfungible/src/lib.rsdiffbeforeafterboth
117use 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};
383383
384// unchecked calls skips any permission checks384// unchecked calls skips any permission checks
385impl<T: Config> Pallet<T> {385impl<T: Config> Pallet<T> {
386 /// Create NFT collection
387 ///
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 }
398
399 /// Destroy NFT collection386 /// Destroy NFT collection
400 ///387 ///
modifiedpallets/refungible/src/benchmarking.rsdiffbeforeafterboth
20use 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}
modifiedpallets/refungible/src/lib.rsdiffbeforeafterboth
103use 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,
296296
297// unchecked calls skips any permission checks297// unchecked calls skips any permission checks
298impl<T: Config> Pallet<T> {298impl<T: Config> Pallet<T> {
299 /// Create RFT collection
300 ///
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 }
311
312 /// Destroy RFT collection299 /// Destroy RFT collection
313 ///300 ///
modifiedpallets/structure/src/benchmarking.rsdiffbeforeafterboth
1616
17use 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()
modifiedpallets/unique/src/eth/mod.rsdiffbeforeafterboth
26 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::{
112112
113 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);
242246
243 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>)?;
245253
276 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>)?;
279291
modifiedpallets/unique/src/eth/stubs/CollectionHelpers.soldiffbeforeafterboth
199type CollectionFlags is uint8;199type CollectionFlags is uint8;
200200
201library CollectionFlagsLib {201library CollectionFlagsLib {
202 /// Tokens in foreign collections can be transferred, but not burnt202 /// A collection of foreign assets
203 CollectionFlags constant foreignField = CollectionFlags.wrap(128);203 CollectionFlags constant foreignField = CollectionFlags.wrap(128);
204 /// Supports ERC721Metadata204 /// Supports ERC721Metadata
205 CollectionFlags constant erc721metadataField = CollectionFlags.wrap(64);205 CollectionFlags constant erc721metadataField = CollectionFlags.wrap(64);
modifiedpallets/unique/src/lib.rsdiffbeforeafterboth
93 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 )?;
405410
406 Ok(())411 Ok(())
modifiedprimitives/data-structs/src/budget.rsdiffbeforeafterboth
37 }37 }
38}38}
39
40pub struct ZeroBudget;
41impl Budget for ZeroBudget {
42 fn consume_custom(&self, _calls: u32) -> bool {
43 false
44 }
45}
3946
modifiedprimitives/data-structs/src/lib.rsdiffbeforeafterboth
378#[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 assets
382 #[bondrewd(bits = "0..1")]382 #[bondrewd(bits = "0..1")]
383 pub foreign: bool,383 pub foreign: bool,
384 /// Supports ERC721Metadata384 /// Supports ERC721Metadata
modifiedruntime/common/config/orml.rsdiffbeforeafterboth
1616
17use 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;
3229
33use 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};
4434
59 };49 };
60}50}
61
62parameter_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_000
70 }
71 };
72}
73
74pub fn get_all_module_accounts() -> Vec<AccountId> {
75 vec![TreasuryAccountId::get()]
76}
77
78pub struct DustRemovalWhitelist;
79impl Contains<AccountId> for DustRemovalWhitelist {
80 fn contains(a: &AccountId) -> bool {
81 get_all_module_accounts().contains(a)
82 }
83}
8451
85pub struct AccountIdToMultiLocation;52pub struct AccountIdToMultiLocation;
86impl Convert<AccountId, MultiLocation> for AccountIdToMultiLocation {53impl Convert<AccountId, MultiLocation> for AccountIdToMultiLocation {
93 }60 }
94}61}
95
96pub 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}
10762
108impl 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}
117
118impl 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 accounts
129 type DustRemovalWhitelist = DustRemovalWhitelist;
130 /// The id type for named reserves.
131 type ReserveIdentifier = ();
132}
13372
134impl 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>>;
modifiedruntime/common/config/pallets/foreign_asset.rsdiffbeforeafterboth
1use 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;
8
9#[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};
18
19parameter_types! {
20 pub ForeignAssetPalletId: PalletId = PalletId(*b"frgnasts");
21}
22
23pub struct LocationToCrossAccountId;
24impl staging_xcm_executor::traits::ConvertLocation<ConfigCrossAccountId>
25 for LocationToCrossAccountId
26{
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)?;
33
34 Some(ConfigCrossAccountId::from_eth(eth_address))
35 })
36 }
37}
438
5impl pallet_foreign_assets::Config for Runtime {39impl pallet_foreign_assets::Config for Runtime {
6 type RuntimeEvent = RuntimeEvent;40 type RuntimeEvent = RuntimeEvent;
41
42 #[cfg(feature = "governance")]
7 type Currency = Balances;43 type ForceRegisterOrigin = governance::RootOrTechnicalCommitteeMember;
44
45 #[cfg(not(feature = "governance"))]
8 type RegisterOrigin = frame_system::EnsureRoot<AccountId>;46 type ForceRegisterOrigin = EnsureRoot<Self::AccountId>;
47
48 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}
1153
addedruntime/common/config/xcm.rsdiffbeforeafterboth

no changes

deletedruntime/common/config/xcm/foreignassets.rsdiffbeforeafterboth

no changes

deletedruntime/common/config/xcm/mod.rsdiffbeforeafterboth

no changes

deletedruntime/common/config/xcm/nativeassets.rsdiffbeforeafterboth

no changes

modifiedruntime/common/construct_runtime.rsdiffbeforeafterboth
47 Vesting: orml_vesting = 37,47 Vesting: orml_vesting = 37,
4848
49 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,
5252
53 #[cfg(feature = "governance")]53 #[cfg(feature = "governance")]
modifiedruntime/common/dispatch.rsdiffbeforeafterboth
17use 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};
7068
71 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 params
80 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>::CollectionDecimalPointLimitExceeded
83 );80 );
84 <PalletFungible<T>>::init_collection(sender, payer, data)?
85 }81 }
86
87 #[cfg(feature = "refungible")]
88 CollectionMode::ReFungible => <PalletRefungible<T>>::init_collection(sender, payer, data)?,
8982
90 #[cfg(not(feature = "refungible"))]83 #[cfg(not(feature = "refungible"))]
91 CollectionMode::ReFungible => return unsupported!(T),84 CollectionMode::ReFungible => return unsupported!(T),
85
86 _ => {}
92 };87 };
88
93 Ok(id)89 <PalletCommon<T>>::init_collection(sender, issuer, data)
94 }90 }
9591
96 fn destroy(sender: T::CrossAccountId, collection_id: CollectionId) -> DispatchResult {92 fn destroy(sender: T::CrossAccountId, collection_id: CollectionId) -> DispatchResult {
modifiedruntime/opal/Cargo.tomldiffbeforeafterboth
151 'up-rpc/std',151 'up-rpc/std',
152 'up-sponsorship/std',152 'up-sponsorship/std',
153153
154 "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 }
modifiedruntime/quartz/Cargo.tomldiffbeforeafterboth
150 'up-rpc/std',150 'up-rpc/std',
151 'up-sponsorship/std',151 'up-sponsorship/std',
152152
153 "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 }
modifiedruntime/tests/src/tests.rsdiffbeforeafterboth
2357#[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);
23612362
2362 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 };
23772378
2378 // 11-th collection in chain. Expects error2379 // 11-th collection in chain. Expects error
2380 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>::TotalCollectionsLimitExceeded
modifiedruntime/unique/Cargo.tomldiffbeforeafterboth
147 'up-rpc/std',147 'up-rpc/std',
148 'up-sponsorship/std',148 'up-sponsorship/std',
149149
150 "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 }