difftreelog
Merge pull request #1024 from UniqueNetwork/refactor/foreign-assets-as-thin-proxy
in: master
Refactor/foreign assets as thin proxy
50 files changed
.envdiffbeforeafterboth--- a/.env
+++ b/.env
@@ -16,7 +16,7 @@
KUSAMA_MAINNET_BRANCH=release-v1.0.0
STATEMINE_BUILD_BRANCH=release-parachains-v9430
-KARURA_BUILD_BRANCH=release-karura-2.21.0
+KARURA_BUILD_BRANCH=xnft-poc
MOONRIVER_BUILD_BRANCH=runtime-2500
SHIDEN_BUILD_BRANCH=v5.18.0
QUARTZ_MAINNET_BRANCH=release-v10010063
.github/workflows/ci-develop.ymldiffbeforeafterboth--- a/.github/workflows/ci-develop.yml
+++ b/.github/workflows/ci-develop.yml
@@ -35,6 +35,11 @@
if: github.event.pull_request.draft == false && contains(github.event.pull_request.labels.*.name, 'CI-xcm')
uses: ./.github/workflows/xcm.yml
secrets: inherit
+
+ xnft:
+ if: github.event.pull_request.draft == false && contains(github.event.pull_request.labels.*.name, 'CI-xnft')
+ uses: ./.github/workflows/xnft.yml
+ secrets: inherit
collator-selection:
if: github.event.pull_request.draft == false && contains(github.event.pull_request.labels.*.name, 'CI-collator-selection')
.github/workflows/ci-master.ymldiffbeforeafterboth--- a/.github/workflows/ci-master.yml
+++ b/.github/workflows/ci-master.yml
@@ -31,6 +31,10 @@
xcm:
uses: ./.github/workflows/xcm.yml
secrets: inherit # pass all secrets from initial workflow to nested
+
+ xnft:
+ uses: ./.github/workflows/xnft.yml
+ secrets: inherit # pass all secrets from initial workflow to nested
collator-selection:
uses: ./.github/workflows/collator-selection.yml
.github/workflows/xnft.ymldiffbeforeafterboth--- /dev/null
+++ b/.github/workflows/xnft.yml
@@ -0,0 +1,215 @@
+name: xnft-testnet
+
+# Controls when the action will run.
+on:
+ workflow_call:
+
+ # Allows you to run this workflow manually from the Actions tab
+ workflow_dispatch:
+
+#Define Workflow variables
+env:
+ REPO_URL: ${{ github.server_url }}/${{ github.repository }}
+
+# A workflow run is made up of one or more jobs that can run sequentially or in parallel
+jobs:
+ prepare-execution-marix:
+ name: Prepare execution matrix
+
+ runs-on: [self-hosted-ci]
+ outputs:
+ matrix: ${{ steps.create_matrix.outputs.matrix }}
+
+ steps:
+ - name: Clean Workspace
+ uses: AutoModality/action-clean@v1.1.0
+
+ # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
+ - uses: actions/checkout@v3.1.0
+ with:
+ ref: ${{ github.head_ref }} #Checking out head commit
+
+ - name: Read .env file
+ uses: xom9ikk/dotenv@v2
+
+ - name: Create Execution matrix
+ uses: CertainLach/create-matrix-action@v4
+ id: create_matrix
+ with:
+ matrix: |
+ network {quartz}, relay_branch {${{ env.KUSAMA_MAINNET_BRANCH }}}, acala_version {${{ env.KARURA_BUILD_BRANCH }}}, runtest {all-quartz}, runtime_features {quartz-runtime}
+
+ xnft:
+ needs: prepare-execution-marix
+ # The type of runner that the job will run on
+ runs-on: [XL]
+
+ timeout-minutes: 600
+
+ name: ${{ matrix.network }}
+
+ continue-on-error: true #Do not stop testing of matrix runs failed. As it decided during PR review - it required 50/50& Let's check it with false.
+
+ strategy:
+ matrix:
+ include: ${{fromJson(needs.prepare-execution-marix.outputs.matrix)}}
+
+ steps:
+ - name: Skip if pull request is in Draft
+ if: github.event.pull_request.draft == true
+ run: exit 1
+
+ - name: Clean Workspace
+ uses: AutoModality/action-clean@v1.1.0
+
+ # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
+ - uses: actions/checkout@v3.1.0
+ with:
+ ref: ${{ github.head_ref }} #Checking out head commit
+
+ # Prepare SHA
+ - name: Prepare SHA
+ uses: ./.github/actions/prepare
+
+ - name: Read .env file
+ uses: xom9ikk/dotenv@v2
+
+ - name: Log in to Docker Hub
+ uses: docker/login-action@v2.1.0
+ with:
+ username: ${{ secrets.CORE_DOCKERHUB_USERNAME }}
+ password: ${{ secrets.CORE_DOCKERHUB_TOKEN }}
+
+ # Check POLKADOT version and build it if it doesn't exist in repository
+ - name: Generate ENV related extend Dockerfile file for POLKADOT
+ uses: cuchi/jinja2-action@v1.2.0
+ with:
+ template: .docker/Dockerfile-polkadot.j2
+ output_file: .docker/Dockerfile-polkadot.${{ matrix.relay_branch }}.yml
+ variables: |
+ RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }}
+ POLKADOT_BUILD_BRANCH=${{ matrix.relay_branch }}
+
+ - name: Check if the dockerhub repository contains the needed version POLKADOT
+ run: |
+ # aquire token
+ TOKEN=$(curl -s -H "Content-Type: application/json" -X POST -d '{"username": "'${{ secrets.CORE_DOCKERHUB_USERNAME }}'", "password": "'${{ secrets.CORE_DOCKERHUB_TOKEN }}'"}' https://hub.docker.com/v2/users/login/ | jq -r .token)
+ export TOKEN=$TOKEN
+
+ # Get TAGS from DOCKERHUB POLKADOT repository
+ POLKADOT_TAGS=$(curl -s -H "Authorization: JWT ${TOKEN}" https://hub.docker.com/v2/repositories/uniquenetwork/builder-polkadot/tags/?page_size=100 | jq -r '."results"[]["name"]')
+ # Show TAGS
+ echo "POLKADOT TAGS:"
+ echo $POLKADOT_TAGS
+ # Check correct version POLKADOT and build it if it doesn't exist in POLKADOT TAGS
+ if [[ ${POLKADOT_TAGS[*]} =~ (^|[[:space:]])"${{ matrix.relay_branch }}"($|[[:space:]]) ]]; then
+ echo "Repository has needed POLKADOT version";
+ docker pull uniquenetwork/builder-polkadot:${{ matrix.relay_branch }}
+ else
+ echo "Repository has not needed POLKADOT version, so build it";
+ cd .docker/ && docker build --file ./Dockerfile-polkadot.${{ matrix.relay_branch }}.yml --tag uniquenetwork/builder-polkadot:${{ matrix.relay_branch }} .
+ echo "Push needed POLKADOT version to the repository";
+ docker push uniquenetwork/builder-polkadot:${{ matrix.relay_branch }}
+ fi
+ shell: bash
+
+ # Check ACALA version and build it if it doesn't exist in repository
+ - name: Generate ENV related extend Dockerfile file for ACALA
+ uses: cuchi/jinja2-action@v1.2.0
+ with:
+ template: .docker/Dockerfile-acala.j2
+ output_file: .docker/Dockerfile-acala.${{ matrix.acala_version }}.yml
+ variables: |
+ RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }}
+ ACALA_BUILD_BRANCH=${{ matrix.acala_version }}
+
+ - name: Check if the dockerhub repository contains the needed ACALA version
+ run: |
+ # aquire token
+ TOKEN=$(curl -s -H "Content-Type: application/json" -X POST -d '{"username": "'${{ secrets.CORE_DOCKERHUB_USERNAME }}'", "password": "'${{ secrets.CORE_DOCKERHUB_TOKEN }}'"}' https://hub.docker.com/v2/users/login/ | jq -r .token)
+ export TOKEN=$TOKEN
+
+ # Get TAGS from DOCKERHUB repository
+ ACALA_TAGS=$(curl -s -H "Authorization: JWT ${TOKEN}" https://hub.docker.com/v2/repositories/uniquenetwork/builder-acala/tags/?page_size=100 | jq -r '."results"[]["name"]')
+ # Show TAGS
+ echo "ACALA TAGS:"
+ echo $ACALA_TAGS
+ # Check correct version ACALA and build it if it doesn't exist in ACALA TAGS
+ if [[ ${ACALA_TAGS[*]} =~ (^|[[:space:]])"${{ matrix.acala_version }}"($|[[:space:]]) ]]; then
+ echo "Repository has needed ACALA version";
+ docker pull uniquenetwork/builder-acala:${{ matrix.acala_version }}
+ else
+ echo "Repository has not needed ACALA version, so build it";
+ cd .docker/ && docker build --file ./Dockerfile-acala.${{ matrix.acala_version }}.yml --tag uniquenetwork/builder-acala:${{ matrix.acala_version }} .
+ echo "Push needed ACALA version to the repository";
+ docker push uniquenetwork/builder-acala:${{ matrix.acala_version }}
+ fi
+ shell: bash
+
+ - name: Build unique-chain
+ run: |
+ docker build --file .docker/Dockerfile-unique \
+ --build-arg RUNTIME_FEATURES=${{ matrix.runtime_features }} \
+ --build-arg RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }} \
+ --tag uniquenetwork/ci-xnft-local:${{ matrix.network }}-${{ env.REF_SLUG }}-${{ env.BUILD_SHA }} \
+ .
+
+ - name: Push docker image version
+ run: docker push uniquenetwork/ci-xnft-local:${{ matrix.network }}-${{ env.REF_SLUG }}-${{ env.BUILD_SHA }}
+
+ - uses: actions/setup-node@v3.5.1
+ with:
+ node-version: 18
+
+ - name: Clone xnft-tests
+ run: git clone https://github.com/UniqueNetwork/xnft-tests.git
+
+ - name: Install baedeker
+ uses: UniqueNetwork/baedeker-action/setup@built
+
+ - name: Setup library
+ run: mkdir -p .baedeker/vendor/ && git clone https://github.com/UniqueNetwork/baedeker-library .baedeker/vendor/baedeker-library
+
+ - name: Start network
+ uses: UniqueNetwork/baedeker-action@built
+ id: bdk
+ with:
+ jpath: |
+ .baedeker/vendor
+ tla-str: |
+ relay_spec=rococo-local
+ inputs: |
+ xnft-tests/.baedeker/testnets.jsonnet
+ snippet:(import 'baedeker-library/ops/rewrites.libsonnet').rewriteNodePaths({'bin/polkadot':{dockerImage:'uniquenetwork/builder-polkadot:${{ matrix.relay_branch }}'}})
+ snippet:(import 'baedeker-library/ops/rewrites.libsonnet').rewriteNodePaths({'bin/quartz':{dockerImage:'uniquenetwork/ci-xnft-local:${{ matrix.network }}-${{ env.REF_SLUG }}-${{ env.BUILD_SHA }}'}})
+ snippet:(import 'baedeker-library/ops/rewrites.libsonnet').rewriteNodePaths({'bin/karura':{dockerImage:'uniquenetwork/builder-acala:${{ matrix.acala_version }}'}})
+
+ - name: Yarn install
+ working-directory: xnft-tests
+ run: |
+ yarn install
+ yarn add mochawesome
+
+ - name: Run XNFT Tests
+ working-directory: xnft-tests
+ run: |
+ NOW=$(date +%s) && yarn ${{ matrix.runtest }} --reporter mochawesome --reporter-options reportFilename=test-${NOW}
+
+ - name: XNFT Tests Report
+ uses: phoenix-actions/test-reporting@v10
+ id: test-report
+ if: success() || failure()
+ with:
+ name: XNFT Tests ${{ matrix.network }}
+ path: xnft-tests/mochawesome-report/test-*.json
+ reporter: mochawesome-json
+ fail-on-error: 'false'
+
+ - name: Clean Workspace
+ if: always()
+ uses: AutoModality/action-clean@v1.1.0
+
+ - name: Remove builder cache
+ if: always()
+ run: |
+ docker system prune -a -f
Cargo.lockdiffbeforeafterboth--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2567,15 +2567,15 @@
[[package]]
name = "data-encoding"
-version = "2.4.0"
+version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
+checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"
[[package]]
name = "data-encoding-macro"
-version = "0.1.13"
+version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c904b33cc60130e1aeea4956ab803d08a3f4a0ca82d64ed757afac3891f2bb99"
+checksum = "20c01c06f5f429efdf2bae21eb67c28b3df3cf85b7dd2d8ef09c0838dac5d33e"
dependencies = [
"data-encoding",
"data-encoding-macro-internal",
@@ -2583,9 +2583,9 @@
[[package]]
name = "data-encoding-macro-internal"
-version = "0.1.11"
+version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8fdf3fce3ce863539ec1d7fd1b6dcc3c645663376b43ed376bbf887733e4f772"
+checksum = "0047d07f2c89b17dd631c80450d69841a6b5d7fb17278cbc43d7e4cfcf2576f3"
dependencies = [
"data-encoding",
"syn 1.0.109",
@@ -4678,21 +4678,21 @@
[[package]]
name = "if-addrs"
-version = "0.7.0"
+version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cbc0fa01ffc752e9dbc72818cdb072cd028b86be5e09dd04c5a643704fe101a9"
+checksum = "cabb0019d51a643781ff15c9c8a3e5dedc365c47211270f4e8f82812fedd8f0a"
dependencies = [
"libc",
- "winapi",
+ "windows-sys 0.48.0",
]
[[package]]
name = "if-watch"
-version = "3.1.0"
+version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbb892e5777fe09e16f3d44de7802f4daa7267ecbe8c466f19d94e25bb0c303e"
+checksum = "d6b0422c86d7ce0e97169cc42e04ae643caf278874a7a3c87b8150a220dc7e1e"
dependencies = [
- "async-io 1.13.0",
+ "async-io 2.2.0",
"core-foundation",
"fnv",
"futures",
@@ -6557,7 +6557,6 @@
"impl-trait-for-tuples",
"log",
"num_enum",
- "orml-tokens",
"orml-traits",
"orml-vesting",
"orml-xcm-support",
@@ -6706,26 +6705,9 @@
]
[[package]]
-name = "orml-tokens"
-version = "0.5.0-dev"
-source = "git+https://github.com/uniquenetwork/open-runtime-module-library?branch=unique-polkadot-v1.3.0#6f36a9e79b75421ac56b259fd47d03db6c3e1603"
-dependencies = [
- "frame-support",
- "frame-system",
- "log",
- "orml-traits",
- "parity-scale-codec",
- "scale-info",
- "serde",
- "sp-arithmetic",
- "sp-runtime",
- "sp-std",
-]
-
-[[package]]
name = "orml-traits"
-version = "0.5.0-dev"
-source = "git+https://github.com/uniquenetwork/open-runtime-module-library?branch=unique-polkadot-v1.3.0#6f36a9e79b75421ac56b259fd47d03db6c3e1603"
+version = "0.6.1"
+source = "git+https://github.com/uniquenetwork/open-runtime-module-library?branch=unique-polkadot-v1.3.0#99604b000be6252597cfcb82eee82554f6a07a0e"
dependencies = [
"frame-support",
"impl-trait-for-tuples",
@@ -6744,8 +6726,8 @@
[[package]]
name = "orml-utilities"
-version = "0.5.0-dev"
-source = "git+https://github.com/uniquenetwork/open-runtime-module-library?branch=unique-polkadot-v1.3.0#6f36a9e79b75421ac56b259fd47d03db6c3e1603"
+version = "0.6.1"
+source = "git+https://github.com/uniquenetwork/open-runtime-module-library?branch=unique-polkadot-v1.3.0#99604b000be6252597cfcb82eee82554f6a07a0e"
dependencies = [
"frame-support",
"parity-scale-codec",
@@ -6759,8 +6741,8 @@
[[package]]
name = "orml-vesting"
-version = "0.5.0-dev"
-source = "git+https://github.com/uniquenetwork/open-runtime-module-library?branch=unique-polkadot-v1.3.0#6f36a9e79b75421ac56b259fd47d03db6c3e1603"
+version = "0.6.1"
+source = "git+https://github.com/uniquenetwork/open-runtime-module-library?branch=unique-polkadot-v1.3.0#99604b000be6252597cfcb82eee82554f6a07a0e"
dependencies = [
"frame-support",
"frame-system",
@@ -6774,8 +6756,8 @@
[[package]]
name = "orml-xcm-support"
-version = "0.5.0-dev"
-source = "git+https://github.com/uniquenetwork/open-runtime-module-library?branch=unique-polkadot-v1.3.0#6f36a9e79b75421ac56b259fd47d03db6c3e1603"
+version = "0.6.1"
+source = "git+https://github.com/uniquenetwork/open-runtime-module-library?branch=unique-polkadot-v1.3.0#99604b000be6252597cfcb82eee82554f6a07a0e"
dependencies = [
"frame-support",
"orml-traits",
@@ -6788,8 +6770,8 @@
[[package]]
name = "orml-xtokens"
-version = "0.5.0-dev"
-source = "git+https://github.com/uniquenetwork/open-runtime-module-library?branch=unique-polkadot-v1.3.0#6f36a9e79b75421ac56b259fd47d03db6c3e1603"
+version = "0.6.1"
+source = "git+https://github.com/uniquenetwork/open-runtime-module-library?branch=unique-polkadot-v1.3.0#99604b000be6252597cfcb82eee82554f6a07a0e"
dependencies = [
"cumulus-primitives-core",
"frame-support",
@@ -7446,7 +7428,6 @@
"frame-support",
"frame-system",
"log",
- "orml-tokens",
"pallet-balances",
"pallet-common",
"pallet-fungible",
@@ -10150,7 +10131,6 @@
"impl-trait-for-tuples",
"log",
"num_enum",
- "orml-tokens",
"orml-traits",
"orml-vesting",
"orml-xcm-support",
@@ -13695,9 +13675,9 @@
[[package]]
name = "spinners"
-version = "4.1.0"
+version = "4.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08615eea740067d9899969bc2891c68a19c315cb1f66640af9a9ecb91b13bcab"
+checksum = "a0ef947f358b9c238923f764c72a4a9d42f2d637c46e059dbd319d6e7cfb4f82"
dependencies = [
"lazy_static",
"maplit",
@@ -15028,7 +15008,6 @@
"impl-trait-for-tuples",
"log",
"num_enum",
- "orml-tokens",
"orml-traits",
"orml-vesting",
"orml-xcm-support",
Cargo.tomldiffbeforeafterboth--- a/Cargo.toml
+++ b/Cargo.toml
@@ -199,7 +199,6 @@
try-runtime-cli = "0.35.0"
# ORML
-orml-tokens = { default-features = false, git = "https://github.com/uniquenetwork/open-runtime-module-library", branch = "unique-polkadot-v1.3.0" }
orml-traits = { default-features = false, git = "https://github.com/uniquenetwork/open-runtime-module-library", branch = "unique-polkadot-v1.3.0" }
orml-vesting = { default-features = false, git = "https://github.com/uniquenetwork/open-runtime-module-library", branch = "unique-polkadot-v1.3.0" }
orml-xcm-support = { default-features = false, git = "https://github.com/uniquenetwork/open-runtime-module-library", branch = "unique-polkadot-v1.3.0" }
js-packages/playgrounds/types.tsdiffbeforeafterboth--- a/js-packages/playgrounds/types.ts
+++ b/js-packages/playgrounds/types.ts
@@ -160,7 +160,7 @@
External = 1,
/// Supports ERC721Metadata
Erc721metadata = 64,
- /// Tokens in foreign collections can be transferred, but not burnt
+ /// A collection of foreign assets
Foreign = 128,
}
js-packages/playgrounds/types.xcm.tsdiffbeforeafterboth--- a/js-packages/playgrounds/types.xcm.ts
+++ b/js-packages/playgrounds/types.xcm.ts
@@ -27,10 +27,3 @@
conviction: number,
},
}
-
-export interface IForeignAssetMetadata {
- name?: number | Uint8Array,
- symbol?: string,
- decimals?: number,
- minimalBalance?: bigint,
-}
\ No newline at end of file
js-packages/playgrounds/unique.xcm.tsdiffbeforeafterboth--- a/js-packages/playgrounds/unique.xcm.ts
+++ b/js-packages/playgrounds/unique.xcm.ts
@@ -2,7 +2,7 @@
import type {IKeyringPair} from '@polkadot/types/types';
import {ChainHelperBase, EthereumBalanceGroup, HelperGroup, SubstrateBalanceGroup, UniqueHelper} from './unique.js';
import type {ILogger, TSigner, TSubstrateAccount} from './types.js';
-import type {AcalaAssetMetadata, DemocracyStandardAccountVote, IForeignAssetMetadata, MoonbeamAssetInfo} from './types.xcm.js';
+import type {AcalaAssetMetadata, DemocracyStandardAccountVote, MoonbeamAssetInfo} from './types.xcm.js';
export class XcmChainHelper extends ChainHelperBase {
@@ -104,22 +104,17 @@
}
export class ForeignAssetsGroup extends HelperGroup<UniqueHelper> {
- async register(signer: TSigner, ownerAddress: TSubstrateAccount, location: any, metadata: IForeignAssetMetadata) {
+ async register(signer: TSigner, assetId: any, name: string, tokenPrefix: string, mode: 'NFT' | { Fungible: number }) {
await this.helper.executeExtrinsic(
signer,
- 'api.tx.foreignAssets.registerForeignAsset',
- [ownerAddress, location, metadata],
+ 'api.tx.foreignAssets.forceRegisterForeignAsset',
+ [{V3: assetId}, this.helper.util.str2vec(name), tokenPrefix, mode],
true,
);
}
- async update(signer: TSigner, foreignAssetId: number, location: any, metadata: IForeignAssetMetadata) {
- await this.helper.executeExtrinsic(
- signer,
- 'api.tx.foreignAssets.updateForeignAsset',
- [foreignAssetId, location, metadata],
- true,
- );
+ async foreignCollectionId(assetId: any) {
+ return (await this.helper.callRpc('api.query.foreignAssets.foreignAssetToCollection', [assetId])).toJSON();
}
}
@@ -256,6 +251,10 @@
await this.helper.executeExtrinsic(signer, 'api.tx.assets.mint', [assetId, beneficiary, amount], true);
}
+ async assetInfo(assetId: number | bigint) {
+ return (await this.helper.callRpc('api.query.assets.asset', [assetId])).toJSON();
+ }
+
async account(assetId: string | number | bigint, address: string) {
const accountAsset = (
await this.helper.callRpc('api.query.assets.account', [assetId, address])
js-packages/tests/eth/api/CollectionHelpers.soldiffbeforeafterboth--- a/js-packages/tests/eth/api/CollectionHelpers.sol
+++ b/js-packages/tests/eth/api/CollectionHelpers.sol
@@ -132,7 +132,7 @@
type CollectionFlags is uint8;
library CollectionFlagsLib {
- /// Tokens in foreign collections can be transferred, but not burnt
+ /// A collection of foreign assets
CollectionFlags constant foreignField = CollectionFlags.wrap(128);
/// Supports ERC721Metadata
CollectionFlags constant erc721metadataField = CollectionFlags.wrap(64);
js-packages/tests/pallet-presence.test.tsdiffbeforeafterboth--- a/js-packages/tests/pallet-presence.test.ts
+++ b/js-packages/tests/pallet-presence.test.ts
@@ -47,7 +47,6 @@
'nonfungible',
'charging',
'configuration',
- 'tokens',
'xtokens',
'maintenance',
];
js-packages/tests/xcm/xcm.types.tsdiffbeforeafterboth--- a/js-packages/tests/xcm/xcm.types.ts
+++ b/js-packages/tests/xcm/xcm.types.ts
@@ -505,4 +505,27 @@
await expectFailedToTransact(helper, messageSent);
});
}
+
+ async registerRelayNativeTokenOnUnique(alice: IKeyringPair) {
+ return await usingPlaygrounds(async (helper) => {
+ const relayLocation = {
+ parents: 1,
+ interior: 'Here',
+ };
+ const relayAssetId = {Concrete: relayLocation};
+
+ const relayCollectionId = await helper.foreignAssets.foreignCollectionId(relayAssetId);
+ if(relayCollectionId == null) {
+ const name = 'Relay Tokens';
+ const tokenPrefix = 'xDOT';
+ const decimals = 10;
+ await helper.getSudo().foreignAssets.register(alice, relayAssetId, name, tokenPrefix, {Fungible: decimals});
+
+ return await helper.foreignAssets.foreignCollectionId(relayAssetId);
+ } else {
+ console.log('Relay foreign collection is already registered');
+ return relayCollectionId;
+ }
+ });
+ }
}
js-packages/tests/xcm/xcmOpal.test.tsdiffbeforeafterboth--- a/js-packages/tests/xcm/xcmOpal.test.ts
+++ b/js-packages/tests/xcm/xcmOpal.test.ts
@@ -17,6 +17,7 @@
import type {IKeyringPair} from '@polkadot/types/types';
import config from '../config.js';
import {itSub, expect, describeXCM, usingPlaygrounds, usingWestmintPlaygrounds, usingRelayPlaygrounds} from '../util/index.js';
+import {XcmTestHelper} from './xcm.types.js';
const STATEMINE_CHAIN = +(process.env.RELAY_WESTMINT_ID || 1000);
const UNIQUE_CHAIN = +(process.env.RELAY_OPAL_ID || 2095);
@@ -25,11 +26,11 @@
const westmintUrl = config.westmintUrl;
const STATEMINE_PALLET_INSTANCE = 50;
-const ASSET_ID = 100;
-const ASSET_METADATA_DECIMALS = 18;
-const ASSET_METADATA_NAME = 'USDT';
-const ASSET_METADATA_DESCRIPTION = 'USDT';
-const ASSET_METADATA_MINIMAL_BALANCE = 1n;
+const USDT_ASSET_ID = 100;
+const USDT_ASSET_METADATA_DECIMALS = 18;
+const USDT_ASSET_METADATA_NAME = 'USDT';
+const USDT_ASSET_METADATA_DESCRIPTION = 'USDT';
+const USDT_ASSET_METADATA_MINIMAL_BALANCE = 1n;
const RELAY_DECIMALS = 12;
const WESTMINT_DECIMALS = 12;
@@ -37,7 +38,9 @@
const TRANSFER_AMOUNT = 1_000_000_000_000_000_000n;
// 10,000.00 (ten thousands) USDT
-const ASSET_AMOUNT = 1_000_000_000_000_000_000_000n;
+const USDT_ASSET_AMOUNT = 1_000_000_000_000_000_000_000n;
+
+const testHelper = new XcmTestHelper('opal');
describeXCM('[XCM] Integration test: Exchanging USDT with Westmint', () => {
let alice: IKeyringPair;
@@ -57,57 +60,85 @@
let balanceBobRelayTokenBefore: bigint;
let balanceBobRelayTokenAfter: bigint;
+ let usdtCollectionId: number;
+ let relayCollectionId: number;
before(async () => {
await usingPlaygrounds(async (_helper, privateKey) => {
alice = await privateKey('//Alice');
bob = await privateKey('//Bob'); // funds donor
+
+ relayCollectionId = await testHelper.registerRelayNativeTokenOnUnique(alice);
});
await usingWestmintPlaygrounds(westmintUrl, async (helper) => {
- // 350.00 (three hundred fifty) DOT
- const fundingAmount = 3_500_000_000_000n;
+ const assetInfo = await helper.assets.assetInfo(USDT_ASSET_ID);
+ if(assetInfo == null) {
+ await helper.assets.create(
+ alice,
+ USDT_ASSET_ID,
+ alice.address,
+ USDT_ASSET_METADATA_MINIMAL_BALANCE,
+ );
+ await helper.assets.setMetadata(
+ alice,
+ USDT_ASSET_ID,
+ USDT_ASSET_METADATA_NAME,
+ USDT_ASSET_METADATA_DESCRIPTION,
+ USDT_ASSET_METADATA_DECIMALS,
+ );
+ } else {
+ console.log('The USDT asset is already registered on AssetHub');
+ }
+
+ await helper.assets.mint(
+ alice,
+ USDT_ASSET_ID,
+ alice.address,
+ USDT_ASSET_AMOUNT,
+ );
- await helper.assets.create(alice, ASSET_ID, alice.address, ASSET_METADATA_MINIMAL_BALANCE);
- await helper.assets.setMetadata(alice, ASSET_ID, ASSET_METADATA_NAME, ASSET_METADATA_DESCRIPTION, ASSET_METADATA_DECIMALS);
- await helper.assets.mint(alice, ASSET_ID, alice.address, ASSET_AMOUNT);
+ const sovereignFundingAmount = 3_500_000_000n;
// funding parachain sovereing account (Parachain: 2095)
const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(UNIQUE_CHAIN);
- await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, fundingAmount);
+ await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, sovereignFundingAmount);
});
-
await usingPlaygrounds(async (helper) => {
const location = {
- V2: {
- parents: 1,
- interior: {X3: [
- {
- Parachain: STATEMINE_CHAIN,
- },
- {
- PalletInstance: STATEMINE_PALLET_INSTANCE,
- },
- {
- GeneralIndex: ASSET_ID,
- },
- ]},
- },
+ parents: 1,
+ interior: {X3: [
+ {
+ Parachain: STATEMINE_CHAIN,
+ },
+ {
+ PalletInstance: STATEMINE_PALLET_INSTANCE,
+ },
+ {
+ GeneralIndex: USDT_ASSET_ID,
+ },
+ ]},
};
+ const assetId = {Concrete: location};
- const metadata =
- {
- name: ASSET_ID,
- symbol: ASSET_METADATA_NAME,
- decimals: ASSET_METADATA_DECIMALS,
- minimalBalance: ASSET_METADATA_MINIMAL_BALANCE,
- };
- await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata);
+ if(await helper.foreignAssets.foreignCollectionId(assetId) == null) {
+ const tokenPrefix = USDT_ASSET_METADATA_NAME;
+ await helper.getSudo().foreignAssets.register(
+ alice,
+ assetId,
+ USDT_ASSET_METADATA_NAME,
+ tokenPrefix,
+ {Fungible: USDT_ASSET_METADATA_DECIMALS},
+ );
+ } else {
+ console.log('Foreign collection is already registered on Opal');
+ }
+
balanceOpalBefore = await helper.balance.getSubstrate(alice.address);
+ usdtCollectionId = await helper.foreignAssets.foreignCollectionId(assetId);
});
-
// Providing the relay currency to the unique sender account
await usingRelayPlaygrounds(relayUrl, async (helper) => {
const destination = {
@@ -189,7 +220,7 @@
PalletInstance: STATEMINE_PALLET_INSTANCE,
},
{
- GeneralIndex: ASSET_ID,
+ GeneralIndex: USDT_ASSET_ID,
},
]},
},
@@ -216,19 +247,17 @@
expect(balanceStmnBefore > balanceStmnAfter).to.be.true;
});
-
// ensure that asset has been delivered
await helper.wait.newBlocks(3);
- // expext collection id will be with id 1
- const free = await helper.ft.getBalance(1, {Substrate: alice.address});
+ const free = await helper.ft.getBalance(usdtCollectionId, {Substrate: alice.address});
balanceOpalAfter = await helper.balance.getSubstrate(alice.address);
console.log(
'[Westmint -> Opal] transaction fees on Opal: %s USDT',
- helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, ASSET_METADATA_DECIMALS),
+ helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, USDT_ASSET_METADATA_DECIMALS),
);
console.log(
'[Westmint -> Opal] transaction fees on Opal: %s OPL',
@@ -261,16 +290,11 @@
const currencies: [any, bigint][] = [
[
- {
- ForeignAssetId: 0,
- },
- //10_000_000_000_000_000n,
+ usdtCollectionId,
TRANSFER_AMOUNT,
],
[
- {
- NativeAssetId: 'Parent',
- },
+ relayCollectionId,
400_000_000_000_000n,
],
];
@@ -288,7 +312,7 @@
// The USDT token never paid fees. Its amount not changed from begin value.
// Also check that xcm transfer has been succeeded
- expect((await helper.assets.account(ASSET_ID, alice.address))! == ASSET_AMOUNT).to.be.true;
+ expect((await helper.assets.account(USDT_ASSET_ID, alice.address))! == USDT_ASSET_AMOUNT).to.be.true;
});
});
@@ -296,7 +320,7 @@
const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n;
balanceBobBefore = await helper.balance.getSubstrate(bob.address);
- balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});
+ balanceBobRelayTokenBefore = await helper.ft.getBalance(relayCollectionId, {Substrate: bob.address});
// Providing the relay currency to the unique sender account
await usingRelayPlaygrounds(relayUrl, async (helper) => {
@@ -345,7 +369,7 @@
await helper.wait.newBlocks(3);
balanceBobAfter = await helper.balance.getSubstrate(bob.address);
- balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});
+ balanceBobRelayTokenAfter = await helper.ft.getBalance(relayCollectionId, {Substrate: bob.address});
const wndFee = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;
console.log(
@@ -383,9 +407,7 @@
const currencies: any = [
[
- {
- NativeAssetId: 'Parent',
- },
+ relayCollectionId,
50_000_000_000_000_000n,
],
];
js-packages/tests/xcm/xcmQuartz.test.tsdiffbeforeafterboth--- a/js-packages/tests/xcm/xcmQuartz.test.ts
+++ b/js-packages/tests/xcm/xcmQuartz.test.ts
@@ -19,9 +19,8 @@
import {DevUniqueHelper, Event} from '@unique/playgrounds/unique.dev.js';
import {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';
import {hexToString} from '@polkadot/util';
-
+import {XcmTestHelper} from './xcm.types.js';
-
const STATEMINE_PALLET_INSTANCE = 50;
const TRANSFER_AMOUNT = 2000000000000000000000000n;
@@ -39,6 +38,8 @@
const SAFE_XCM_VERSION = 2;
+const testHelper = new XcmTestHelper('quartz');
+
describeXCM('[XCM] Integration test: Exchanging USDT with Statemine', () => {
let alice: IKeyringPair;
let bob: IKeyringPair;
@@ -57,6 +58,8 @@
let balanceBobRelayTokenBefore: bigint;
let balanceBobRelayTokenAfter: bigint;
+ let usdtCollectionId: number;
+ let relayCollectionId: number;
before(async () => {
await usingPlaygrounds(async (helper, privateKey) => {
@@ -65,6 +68,8 @@
// Set the default version to wrap the first message to other chains.
await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
+
+ relayCollectionId = await testHelper.registerRelayNativeTokenOnUnique(alice);
});
await usingRelayPlaygrounds(relayUrl, async (helper) => {
@@ -74,21 +79,25 @@
});
await usingStateminePlaygrounds(statemineUrl, async (helper) => {
- const sovereignFundingAmount = 3_500_000_000n;
+ const assetInfo = await helper.assets.assetInfo(USDT_ASSET_ID);
+ if(assetInfo == null) {
+ await helper.assets.create(
+ alice,
+ USDT_ASSET_ID,
+ alice.address,
+ USDT_ASSET_METADATA_MINIMAL_BALANCE,
+ );
+ await helper.assets.setMetadata(
+ alice,
+ USDT_ASSET_ID,
+ USDT_ASSET_METADATA_NAME,
+ USDT_ASSET_METADATA_DESCRIPTION,
+ USDT_ASSET_METADATA_DECIMALS,
+ );
+ } else {
+ console.log('The USDT asset is already registered on AssetHub');
+ }
- await helper.assets.create(
- alice,
- USDT_ASSET_ID,
- alice.address,
- USDT_ASSET_METADATA_MINIMAL_BALANCE,
- );
- await helper.assets.setMetadata(
- alice,
- USDT_ASSET_ID,
- USDT_ASSET_METADATA_NAME,
- USDT_ASSET_METADATA_DESCRIPTION,
- USDT_ASSET_METADATA_DECIMALS,
- );
await helper.assets.mint(
alice,
USDT_ASSET_ID,
@@ -96,6 +105,8 @@
USDT_ASSET_AMOUNT,
);
+ const sovereignFundingAmount = 3_500_000_000n;
+
// funding parachain sovereing account on Statemine(t).
// The sovereign account should be created before any action
// (the assets pallet on Statemine(t) check if the sovereign account exists)
@@ -106,31 +117,30 @@
await usingPlaygrounds(async (helper) => {
const location = {
- V2: {
- parents: 1,
- interior: {X3: [
- {
- Parachain: STATEMINE_CHAIN,
- },
- {
- PalletInstance: STATEMINE_PALLET_INSTANCE,
- },
- {
- GeneralIndex: USDT_ASSET_ID,
- },
- ]},
- },
+ parents: 1,
+ interior: {X3: [
+ {
+ Parachain: STATEMINE_CHAIN,
+ },
+ {
+ PalletInstance: STATEMINE_PALLET_INSTANCE,
+ },
+ {
+ GeneralIndex: USDT_ASSET_ID,
+ },
+ ]},
};
+ const assetId = {Concrete: location};
- const metadata =
- {
- name: USDT_ASSET_ID,
- symbol: USDT_ASSET_METADATA_NAME,
- decimals: USDT_ASSET_METADATA_DECIMALS,
- minimalBalance: USDT_ASSET_METADATA_MINIMAL_BALANCE,
- };
- await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata);
+ if(await helper.foreignAssets.foreignCollectionId(assetId) == null) {
+ const tokenPrefix = USDT_ASSET_METADATA_NAME;
+ await helper.getSudo().foreignAssets.register(alice, assetId, USDT_ASSET_METADATA_NAME, tokenPrefix, {Fungible: USDT_ASSET_METADATA_DECIMALS});
+ } else {
+ console.log('Foreign collection is already registered on Quartz');
+ }
+
balanceQuartzBefore = await helper.balance.getSubstrate(alice.address);
+ usdtCollectionId = await helper.foreignAssets.foreignCollectionId(assetId);
});
@@ -248,8 +258,7 @@
// ensure that asset has been delivered
await helper.wait.newBlocks(3);
- // expext collection id will be with id 1
- const free = await helper.ft.getBalance(1, {Substrate: alice.address});
+ const free = await helper.ft.getBalance(usdtCollectionId, {Substrate: alice.address});
balanceQuartzAfter = await helper.balance.getSubstrate(alice.address);
@@ -288,15 +297,11 @@
const relayFee = 400_000_000_000_000n;
const currencies: [any, bigint][] = [
[
- {
- ForeignAssetId: 0,
- },
+ usdtCollectionId,
TRANSFER_AMOUNT,
],
[
- {
- NativeAssetId: 'Parent',
- },
+ relayCollectionId,
relayFee,
],
];
@@ -321,7 +326,7 @@
itSub('Should connect and send Relay token to Quartz', async ({helper}) => {
balanceBobBefore = await helper.balance.getSubstrate(bob.address);
- balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});
+ balanceBobRelayTokenBefore = await helper.ft.getBalance(relayCollectionId, {Substrate: bob.address});
await usingRelayPlaygrounds(relayUrl, async (helper) => {
const destination = {
@@ -369,7 +374,7 @@
await helper.wait.newBlocks(3);
balanceBobAfter = await helper.balance.getSubstrate(bob.address);
- balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});
+ balanceBobRelayTokenAfter = await helper.ft.getBalance(relayCollectionId, {Substrate: bob.address});
const wndFeeOnQuartz = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;
const wndDiffOnQuartz = balanceBobRelayTokenAfter - balanceBobRelayTokenBefore;
@@ -409,9 +414,7 @@
const currencies: any = [
[
- {
- NativeAssetId: 'Parent',
- },
+ relayCollectionId,
TRANSFER_AMOUNT_RELAY,
],
];
@@ -1018,9 +1021,7 @@
});
itSub('Should connect and send QTZ to Moonriver', async ({helper}) => {
- const currencyId = {
- NativeAssetId: 'Here',
- };
+ const currencyId = 0;
const dest = {
V2: {
parents: 1,
js-packages/tests/xcm/xcmUnique.test.tsdiffbeforeafterboth--- a/js-packages/tests/xcm/xcmUnique.test.ts
+++ b/js-packages/tests/xcm/xcmUnique.test.ts
@@ -20,7 +20,7 @@
import {Event} from '@unique/playgrounds/unique.dev.js';
import {hexToString, nToBigInt} from '@polkadot/util';
import {ACALA_CHAIN, ASTAR_CHAIN, MOONBEAM_CHAIN, POLKADEX_CHAIN, SAFE_XCM_VERSION, STATEMINT_CHAIN, UNIQUE_CHAIN, expectFailedToTransact, expectUntrustedReserveLocationFail, uniqueAssetId, uniqueVersionedMultilocation} from './xcm.types.js';
-
+import {XcmTestHelper} from './xcm.types.js';
const STATEMINT_PALLET_INSTANCE = 50;
@@ -50,6 +50,8 @@
const USDT_ASSET_METADATA_MINIMAL_BALANCE = 1n;
const USDT_ASSET_AMOUNT = 10_000_000_000_000_000_000_000_000n;
+const testHelper = new XcmTestHelper('unique');
+
describeXCM('[XCM] Integration test: Exchanging USDT with Statemint', () => {
let alice: IKeyringPair;
let bob: IKeyringPair;
@@ -68,6 +70,8 @@
let balanceBobRelayTokenBefore: bigint;
let balanceBobRelayTokenAfter: bigint;
+ let usdtCollectionId: number;
+ let relayCollectionId: number;
before(async () => {
await usingPlaygrounds(async (helper, privateKey) => {
@@ -76,6 +80,8 @@
// Set the default version to wrap the first message to other chains.
await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
+
+ relayCollectionId = await testHelper.registerRelayNativeTokenOnUnique(alice);
});
await usingRelayPlaygrounds(relayUrl, async (helper) => {
@@ -85,21 +91,25 @@
});
await usingStatemintPlaygrounds(statemintUrl, async (helper) => {
- const sovereignFundingAmount = 3_500_000_000n;
+ const assetInfo = await helper.assets.assetInfo(USDT_ASSET_ID);
+ if(assetInfo == null) {
+ await helper.assets.create(
+ alice,
+ USDT_ASSET_ID,
+ alice.address,
+ USDT_ASSET_METADATA_MINIMAL_BALANCE,
+ );
+ await helper.assets.setMetadata(
+ alice,
+ USDT_ASSET_ID,
+ USDT_ASSET_METADATA_NAME,
+ USDT_ASSET_METADATA_DESCRIPTION,
+ USDT_ASSET_METADATA_DECIMALS,
+ );
+ } else {
+ console.log('The USDT asset is already registered on AssetHub');
+ }
- await helper.assets.create(
- alice,
- USDT_ASSET_ID,
- alice.address,
- USDT_ASSET_METADATA_MINIMAL_BALANCE,
- );
- await helper.assets.setMetadata(
- alice,
- USDT_ASSET_ID,
- USDT_ASSET_METADATA_NAME,
- USDT_ASSET_METADATA_DESCRIPTION,
- USDT_ASSET_METADATA_DECIMALS,
- );
await helper.assets.mint(
alice,
USDT_ASSET_ID,
@@ -107,6 +117,8 @@
USDT_ASSET_AMOUNT,
);
+ const sovereignFundingAmount = 3_500_000_000n;
+
// funding parachain sovereing account on Statemint.
// The sovereign account should be created before any action
// (the assets pallet on Statemint check if the sovereign account exists)
@@ -117,31 +129,30 @@
await usingPlaygrounds(async (helper) => {
const location = {
- V2: {
- parents: 1,
- interior: {X3: [
- {
- Parachain: STATEMINT_CHAIN,
- },
- {
- PalletInstance: STATEMINT_PALLET_INSTANCE,
- },
- {
- GeneralIndex: USDT_ASSET_ID,
- },
- ]},
- },
+ parents: 1,
+ interior: {X3: [
+ {
+ Parachain: STATEMINT_CHAIN,
+ },
+ {
+ PalletInstance: STATEMINT_PALLET_INSTANCE,
+ },
+ {
+ GeneralIndex: USDT_ASSET_ID,
+ },
+ ]},
};
+ const assetId = {Concrete: location};
- const metadata =
- {
- name: USDT_ASSET_ID,
- symbol: USDT_ASSET_METADATA_NAME,
- decimals: USDT_ASSET_METADATA_DECIMALS,
- minimalBalance: USDT_ASSET_METADATA_MINIMAL_BALANCE,
- };
- await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata);
+ if(await helper.foreignAssets.foreignCollectionId(assetId) == null) {
+ const tokenPrefix = USDT_ASSET_METADATA_NAME;
+ await helper.getSudo().foreignAssets.register(alice, assetId, USDT_ASSET_METADATA_NAME, tokenPrefix, {Fungible: USDT_ASSET_METADATA_DECIMALS});
+ } else {
+ console.log('Foreign collection is already registered on Unique');
+ }
+
balanceUniqueBefore = await helper.balance.getSubstrate(alice.address);
+ usdtCollectionId = await helper.foreignAssets.foreignCollectionId(assetId);
});
@@ -259,8 +270,7 @@
// ensure that asset has been delivered
await helper.wait.newBlocks(3);
- // expext collection id will be with id 1
- const free = await helper.ft.getBalance(1, {Substrate: alice.address});
+ const free = await helper.ft.getBalance(usdtCollectionId, {Substrate: alice.address});
balanceUniqueAfter = await helper.balance.getSubstrate(alice.address);
@@ -299,15 +309,11 @@
const relayFee = 400_000_000_000_000n;
const currencies: [any, bigint][] = [
[
- {
- ForeignAssetId: 0,
- },
+ usdtCollectionId,
TRANSFER_AMOUNT,
],
[
- {
- NativeAssetId: 'Parent',
- },
+ relayCollectionId,
relayFee,
],
];
@@ -332,7 +338,7 @@
itSub('Should connect and send Relay token to Unique', async ({helper}) => {
balanceBobBefore = await helper.balance.getSubstrate(bob.address);
- balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});
+ balanceBobRelayTokenBefore = await helper.ft.getBalance(relayCollectionId, {Substrate: bob.address});
await usingRelayPlaygrounds(relayUrl, async (helper) => {
const destination = {
@@ -380,7 +386,7 @@
await helper.wait.newBlocks(3);
balanceBobAfter = await helper.balance.getSubstrate(bob.address);
- balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});
+ balanceBobRelayTokenAfter = await helper.ft.getBalance(relayCollectionId, {Substrate: bob.address});
const wndFeeOnUnique = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;
const wndDiffOnUnique = balanceBobRelayTokenAfter - balanceBobRelayTokenBefore;
@@ -420,9 +426,7 @@
const currencies: any = [
[
- {
- NativeAssetId: 'Parent',
- },
+ relayCollectionId,
TRANSFER_AMOUNT_RELAY,
],
];
@@ -1275,9 +1279,7 @@
});
itSub('Should connect and send UNQ to Moonbeam', async ({helper}) => {
- const currencyId = {
- NativeAssetId: 'Here',
- };
+ const currencyId = 0;
const dest = {
V2: {
parents: 1,
node/cli/src/chain_spec.rsdiffbeforeafterboth--- a/node/cli/src/chain_spec.rs
+++ b/node/cli/src/chain_spec.rs
@@ -167,7 +167,6 @@
.map(|k| (k, 1 << 100))
.collect(),
},
- tokens: TokensConfig { balances: vec![] },
sudo: SudoConfig {
key: Some($root_key),
},
@@ -231,7 +230,6 @@
.map(|k| (k, 1 << 100))
.collect(),
},
- tokens: TokensConfig { balances: vec![] },
sudo: SudoConfig {
key: Some($root_key),
},
pallets/balances-adapter/src/common.rsdiffbeforeafterboth--- a/pallets/balances-adapter/src/common.rs
+++ b/pallets/balances-adapter/src/common.rs
@@ -1,10 +1,16 @@
use alloc::{vec, vec::Vec};
use core::marker::PhantomData;
-use frame_support::{ensure, fail, weights::Weight};
+use frame_support::{
+ ensure, fail,
+ traits::tokens::{fungible::Mutate, Fortitude, Precision},
+ weights::Weight,
+};
use pallet_balances::{weights::SubstrateWeight as BalancesWeight, WeightInfo};
-use pallet_common::{CommonCollectionOperations, CommonWeightInfo, Error as CommonError};
-use up_data_structs::TokenId;
+use pallet_common::{
+ erc::CrossAccountId, CommonCollectionOperations, CommonWeightInfo, Error as CommonError,
+};
+use up_data_structs::{budget::Budget, TokenId};
use crate::{Config, NativeFungibleHandle, Pallet};
@@ -332,8 +338,8 @@
0
}
- fn refungible_extensions(&self) -> Option<&dyn pallet_common::RefungibleExtensions<T>> {
- None
+ fn xcm_extensions(&self) -> Option<&dyn pallet_common::XcmExtensions<T>> {
+ Some(self)
}
fn set_allowance_for_all(
@@ -360,3 +366,72 @@
fail!(<CommonError<T>>::UnsupportedOperation);
}
}
+
+impl<T: Config> pallet_common::XcmExtensions<T> for NativeFungibleHandle<T> {
+ fn create_item_internal(
+ &self,
+ _depositor: &<T>::CrossAccountId,
+ to: <T>::CrossAccountId,
+ data: up_data_structs::CreateItemData,
+ _nesting_budget: &dyn Budget,
+ ) -> Result<TokenId, sp_runtime::DispatchError> {
+ match &data {
+ up_data_structs::CreateItemData::Fungible(fungible_data) => {
+ T::Mutate::mint_into(
+ to.as_sub(),
+ fungible_data
+ .value
+ .try_into()
+ .map_err(|_| sp_runtime::ArithmeticError::Overflow)?,
+ )?;
+
+ Ok(TokenId::default())
+ }
+ _ => {
+ fail!(<CommonError<T>>::NotFungibleDataUsedToMintFungibleCollectionToken)
+ }
+ }
+ }
+
+ fn transfer_item_internal(
+ &self,
+ _depositor: &<T>::CrossAccountId,
+ from: &<T>::CrossAccountId,
+ to: &<T>::CrossAccountId,
+ token: TokenId,
+ amount: u128,
+ _nesting_budget: &dyn Budget,
+ ) -> sp_runtime::DispatchResult {
+ ensure!(
+ token == TokenId::default(),
+ <CommonError<T>>::FungibleItemsHaveNoId
+ );
+
+ <Pallet<T>>::transfer(from, to, amount)
+ .map(|_| ())
+ .map_err(|post_info| post_info.error)
+ }
+
+ fn burn_item_internal(
+ &self,
+ from: T::CrossAccountId,
+ token: TokenId,
+ amount: u128,
+ ) -> sp_runtime::DispatchResult {
+ ensure!(
+ token == TokenId::default(),
+ <CommonError<T>>::FungibleItemsHaveNoId
+ );
+
+ T::Mutate::burn_from(
+ from.as_sub(),
+ amount
+ .try_into()
+ .map_err(|_| sp_runtime::ArithmeticError::Overflow)?,
+ Precision::Exact,
+ Fortitude::Polite,
+ )?;
+
+ Ok(())
+ }
+}
pallets/common/src/benchmarking.rsdiffbeforeafterboth--- a/pallets/common/src/benchmarking.rs
+++ b/pallets/common/src/benchmarking.rs
@@ -33,7 +33,7 @@
MAX_COLLECTION_NAME_LENGTH, MAX_PROPERTIES_PER_ITEM, MAX_TOKEN_PREFIX_LENGTH,
};
-use crate::{BenchmarkPropertyWriter, CollectionHandle, Config, Pallet};
+use crate::{BenchmarkPropertyWriter, CollectionHandle, CollectionIssuer, Config, Pallet};
const SEED: u32 = 1;
@@ -120,7 +120,9 @@
create_collection_raw(
owner,
CollectionMode::NFT,
- |owner: T::CrossAccountId, data| <Pallet<T>>::init_collection(owner.clone(), owner, data),
+ |owner: T::CrossAccountId, data| {
+ <Pallet<T>>::init_collection(owner.clone(), CollectionIssuer::User(owner), data)
+ },
|h| h,
)
}
pallets/common/src/dispatch.rsdiffbeforeafterboth--- a/pallets/common/src/dispatch.rs
+++ b/pallets/common/src/dispatch.rs
@@ -11,7 +11,7 @@
use sp_weights::Weight;
use up_data_structs::{CollectionId, CreateCollectionData};
-use crate::{pallet::Config, CommonCollectionOperations};
+use crate::{pallet::Config, CollectionIssuer, CommonCollectionOperations};
// TODO: move to benchmarking
/// Price of [`dispatch_tx`] call with noop `call` argument
@@ -69,13 +69,14 @@
/// Check if the collection is internal.
fn check_is_internal(&self) -> DispatchResult;
- /// Create a collection. The collection will be created according to the value of [`data.mode`](CreateCollectionData::mode).
+ /// Create a regular collection. The collection will be created according to the value of [`data.mode`](CreateCollectionData::mode).
///
/// * `sender` - The user who will become the owner of the collection.
+ /// * `issuer` - An entity that creates the collection.
/// * `data` - Description of the created collection.
fn create(
sender: T::CrossAccountId,
- payer: T::CrossAccountId,
+ issuer: CollectionIssuer<T::CrossAccountId>,
data: CreateCollectionData<T::CrossAccountId>,
) -> Result<CollectionId, DispatchError>;
pallets/common/src/lib.rsdiffbeforeafterboth--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -80,15 +80,16 @@
use sp_std::vec::Vec;
use sp_weights::Weight;
use up_data_structs::{
- budget::Budget, AccessMode, Collection, CollectionId, CollectionLimits, CollectionMode,
- CollectionPermissions, CollectionProperties as CollectionPropertiesT, CollectionStats,
- CreateCollectionData, CreateItemData, CreateItemExData, PhantomType, PropertiesError,
- PropertiesPermissionMap, Property, PropertyKey, PropertyKeyPermission, PropertyPermission,
- PropertyScope, PropertyValue, RpcCollection, RpcCollectionFlags, SponsoringRateLimit,
- SponsorshipState, TokenChild, TokenData, TokenId, TokenOwnerError, TokenProperties,
- TrySetProperty, COLLECTION_ADMINS_LIMIT, COLLECTION_NUMBER_LIMIT, CUSTOM_DATA_LIMIT,
- FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP,
- MAX_TOKEN_PREFIX_LENGTH, NFT_SPONSOR_TRANSFER_TIMEOUT, REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+ budget::Budget, mapping::TokenAddressMapping, AccessMode, Collection, CollectionId,
+ CollectionLimits, CollectionMode, CollectionPermissions,
+ CollectionProperties as CollectionPropertiesT, CollectionStats, CreateCollectionData,
+ CreateItemData, CreateItemExData, PhantomType, PropertiesError, PropertiesPermissionMap,
+ Property, PropertyKey, PropertyKeyPermission, PropertyPermission, PropertyScope, PropertyValue,
+ RpcCollection, RpcCollectionFlags, SponsoringRateLimit, SponsorshipState, TokenChild,
+ TokenData, TokenId, TokenOwnerError, TokenProperties, TrySetProperty, COLLECTION_ADMINS_LIMIT,
+ COLLECTION_NUMBER_LIMIT, CUSTOM_DATA_LIMIT, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+ MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, MAX_TOKEN_PREFIX_LENGTH,
+ NFT_SPONSOR_TRANSFER_TIMEOUT, REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
};
use up_pov_estimate_rpc::PovInfo;
@@ -786,6 +787,9 @@
/// Fungible tokens hold no ID, and the default value of TokenId for a fungible collection is 0.
FungibleItemsHaveNoId,
+
+ /// Not Fungible item data used to mint in Fungible collection.
+ NotFungibleDataUsedToMintFungibleCollectionToken,
}
/// Storage of the count of created collections. Essentially contains the last collection ID.
@@ -940,6 +944,15 @@
}
}
+/// An issuer of a collection.
+pub enum CollectionIssuer<CrossAccountId> {
+ /// A user who creates the collection.
+ User(CrossAccountId),
+
+ /// The internal mechanisms are creating the collection.
+ Internals,
+}
+
fn check_token_permissions<T: Config>(
collection_admin_permitted: bool,
token_owner_permitted: bool,
@@ -1124,33 +1137,36 @@
/// Create new collection.
///
/// * `owner` - The owner of the collection.
- /// * `data` - Description of the created collection.
- /// * `flags` - Extra flags to store.
+ /// * `issuer` - An entity that creates the collection.
+ /// * `is_special_collection` -- Whether this collection is a special one, i.e. can have special flags set.
pub fn init_collection(
owner: T::CrossAccountId,
- payer: T::CrossAccountId,
+ issuer: CollectionIssuer<T::CrossAccountId>,
data: CreateCollectionData<T::CrossAccountId>,
) -> Result<CollectionId, DispatchError> {
- ensure!(data.flags.is_allowed_for_user(), <Error<T>>::NoPermission);
- Self::init_collection_internal(owner, payer, data)
- }
+ match issuer {
+ CollectionIssuer::User(payer) => {
+ ensure!(data.flags.is_allowed_for_user(), <Error<T>>::NoPermission);
+
+ // Take a (non-refundable) deposit of collection creation
+ let mut imbalance = <Debt<T::AccountId, <T as Config>::Currency>>::zero();
+ imbalance.subsume(<T as Config>::Currency::deposit(
+ &T::TreasuryAccountId::get(),
+ T::CollectionCreationPrice::get(),
+ Precision::Exact,
+ )?);
+ let credit = <T as Config>::Currency::settle(
+ payer.as_sub(),
+ imbalance,
+ Preservation::Preserve,
+ )
+ .map_err(|_| Error::<T>::NotSufficientFounds)?;
- /// Initializes the collection with ForeignCollection flag. Returns [CollectionId] on success, [DispatchError] otherwise.
- pub fn init_foreign_collection(
- owner: T::CrossAccountId,
- payer: T::CrossAccountId,
- mut data: CreateCollectionData<T::CrossAccountId>,
- ) -> Result<CollectionId, DispatchError> {
- data.flags.foreign = true;
- let id = Self::init_collection_internal(owner, payer, data)?;
- Ok(id)
- }
+ debug_assert!(credit.peek().is_zero());
+ }
+ CollectionIssuer::Internals => {}
+ }
- fn init_collection_internal(
- owner: T::CrossAccountId,
- payer: T::CrossAccountId,
- data: CreateCollectionData<T::CrossAccountId>,
- ) -> Result<CollectionId, DispatchError> {
{
ensure!(
data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH as usize,
@@ -1225,21 +1241,6 @@
);
<AdminAmount<T>>::insert(id, admin_amount);
- // Take a (non-refundable) deposit of collection creation
- {
- let mut imbalance = <Debt<T::AccountId, <T as Config>::Currency>>::zero();
- imbalance.subsume(<T as Config>::Currency::deposit(
- &T::TreasuryAccountId::get(),
- T::CollectionCreationPrice::get(),
- Precision::Exact,
- )?);
- let credit =
- <T as Config>::Currency::settle(payer.as_sub(), imbalance, Preservation::Preserve)
- .map_err(|_| Error::<T>::NotSufficientFounds)?;
-
- debug_assert!(credit.peek().is_zero())
- }
-
<CreatedCollectionCount<T>>::put(created_count);
<Pallet<T>>::deposit_event(Event::CollectionCreated(
id,
@@ -2293,7 +2294,14 @@
) -> u128;
/// Get extension for RFT collection.
- fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>>;
+ fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>> {
+ None
+ }
+
+ /// Get XCM extensions.
+ fn xcm_extensions(&self) -> Option<&dyn XcmExtensions<T>> {
+ None
+ }
/// The `operator` is allowed to transfer all tokens of the `owner` on their behalf.
/// * `owner` - Token owner
@@ -2333,6 +2341,117 @@
) -> DispatchResultWithPostInfo;
}
+/// XCM extensions for fungible and NFT collections
+pub trait XcmExtensions<T>
+where
+ T: Config,
+{
+ /// Does the token have children?
+ fn token_has_children(&self, _token: TokenId) -> bool {
+ false
+ }
+
+ /// Create a collection's item using a transaction.
+ ///
+ /// This function performs additional XCM-related checks before the actual creation.
+ ///
+ /// The `transactional` attribute is needed because the inbound XCM messages
+ /// are processed in a non-transactional context.
+ /// To perform the needed logic, we use the internal pallets' functions
+ /// that are not inherently safe to use outside a transaction.
+ ///
+ /// This requirement is temporary until XCM message processing becomes transactional:
+ /// https://github.com/paritytech/polkadot-sdk/issues/490
+ #[transactional]
+ fn create_item(
+ &self,
+ depositor: &T::CrossAccountId,
+ to: T::CrossAccountId,
+ data: CreateItemData,
+ nesting_budget: &dyn Budget,
+ ) -> Result<TokenId, DispatchError> {
+ if T::CrossTokenAddressMapping::is_token_address(&to) {
+ return unsupported!(T);
+ }
+
+ self.create_item_internal(depositor, to, data, nesting_budget)
+ }
+
+ /// Create a collection's item.
+ fn create_item_internal(
+ &self,
+ depositor: &T::CrossAccountId,
+ to: T::CrossAccountId,
+ data: CreateItemData,
+ nesting_budget: &dyn Budget,
+ ) -> Result<TokenId, DispatchError>;
+
+ /// Transfer an item from the `from` account to the `to` account using a transaction.
+ ///
+ /// This function performs additional XCM-related checks before the actual transfer.
+ ///
+ /// The `transactional` attribute is needed because the inbound XCM messages
+ /// are processed in a non-transactional context.
+ /// To perform the needed logic, we use the internal pallets' functions
+ /// that are not inherently safe to use outside a transaction.
+ ///
+ /// This requirement is temporary until XCM message processing becomes transactional:
+ /// https://github.com/paritytech/polkadot-sdk/issues/490
+ #[transactional]
+ fn transfer_item(
+ &self,
+ depositor: &T::CrossAccountId,
+ from: &T::CrossAccountId,
+ to: &T::CrossAccountId,
+ token: TokenId,
+ amount: u128,
+ nesting_budget: &dyn Budget,
+ ) -> DispatchResult {
+ if T::CrossTokenAddressMapping::is_token_address(to) {
+ return unsupported!(T);
+ }
+
+ if self.token_has_children(token) {
+ return unsupported!(T);
+ }
+
+ self.transfer_item_internal(depositor, from, to, token, amount, nesting_budget)
+ }
+
+ /// Transfer an item from the `from` account to the `to` account.
+ fn transfer_item_internal(
+ &self,
+ depositor: &T::CrossAccountId,
+ from: &T::CrossAccountId,
+ to: &T::CrossAccountId,
+ token: TokenId,
+ amount: u128,
+ nesting_budget: &dyn Budget,
+ ) -> DispatchResult;
+
+ /// Burn a collection's item using a transaction.
+ ///
+ /// The `transactional` attribute is needed because the inbound XCM messages
+ /// are processed in a non-transactional context.
+ /// To perform the needed logic, we use the internal pallets' functions
+ /// that are not inherently safe to use outside a transaction.
+ ///
+ /// This requirement is temporary until XCM message processing becomes transactional:
+ /// https://github.com/paritytech/polkadot-sdk/issues/490
+ #[transactional]
+ fn burn_item(&self, from: T::CrossAccountId, token: TokenId, amount: u128) -> DispatchResult {
+ self.burn_item_internal(from, token, amount)
+ }
+
+ /// Burn a collection's item.
+ fn burn_item_internal(
+ &self,
+ from: T::CrossAccountId,
+ token: TokenId,
+ amount: u128,
+ ) -> DispatchResult;
+}
+
/// Merge [`DispatchResult`] with [`Weight`] into [`DispatchResultWithPostInfo`].
///
/// Used for [`CommonCollectionOperations`] implementations and flexible enough to do so.
pallets/foreign-assets/Cargo.tomldiffbeforeafterboth--- a/pallets/foreign-assets/Cargo.toml
+++ b/pallets/foreign-assets/Cargo.toml
@@ -12,7 +12,6 @@
frame-support = { workspace = true }
frame-system = { workspace = true }
log = { workspace = true }
-orml-tokens = { workspace = true }
pallet-balances = { features = ["insecure_zero_ed"], workspace = true }
pallet-common = { workspace = true }
pallet-fungible = { workspace = true }
@@ -30,7 +29,6 @@
"frame-support/std",
"frame-system/std",
"log/std",
- "orml-tokens/std",
"pallet-balances/std",
"pallet-common/std",
"pallet-fungible/std",
pallets/foreign-assets/src/benchmarking.rsdiffbeforeafterboth--- a/pallets/foreign-assets/src/benchmarking.rs
+++ b/pallets/foreign-assets/src/benchmarking.rs
@@ -16,88 +16,36 @@
#![allow(missing_docs)]
-use frame_benchmarking::{account, v2::*};
-use frame_support::traits::Currency;
+use frame_benchmarking::v2::*;
use frame_system::RawOrigin;
-use sp_std::{boxed::Box, vec, vec::Vec};
-use staging_xcm::{opaque::latest::Junction::Parachain, v3::Junctions::X1, VersionedMultiLocation};
+use pallet_common::benchmarking::{create_data, create_u16_data};
+use sp_std::{boxed::Box, vec};
+use staging_xcm::prelude::*;
+use up_data_structs::{MAX_COLLECTION_NAME_LENGTH, MAX_TOKEN_PREFIX_LENGTH};
-use super::{Call, Config, Pallet};
-use crate::AssetMetadata;
+use super::{Call, Config, ForeignCollectionMode, Pallet};
-fn bounded<T: TryFrom<Vec<u8>>>(slice: &[u8]) -> T {
- T::try_from(slice.to_vec())
- .map_err(|_| "slice doesn't fit")
- .unwrap()
-}
-
#[benchmarks]
mod benchmarks {
+
use super::*;
#[benchmark]
- fn register_foreign_asset() -> Result<(), BenchmarkError> {
- let owner: T::AccountId = account("user", 0, 1);
- let location: VersionedMultiLocation = VersionedMultiLocation::from(X1(Parachain(1000)));
- let metadata: AssetMetadata<
- <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance,
- > = AssetMetadata {
- name: bounded(b"name"),
- symbol: bounded(b"symbol"),
- decimals: 18,
- minimal_balance: 1u32.into(),
- };
- let mut balance: <<T as Config>::Currency as Currency<
- <T as frame_system::Config>::AccountId,
- >>::Balance = 4_000_000_000u32.into();
- balance = balance * balance;
- <T as Config>::Currency::make_free_balance_be(&owner, balance);
+ fn force_register_foreign_asset() -> Result<(), BenchmarkError> {
+ let location =
+ MultiLocation::from(X3(Parachain(1000), PalletInstance(42), GeneralIndex(1)));
+ let name = create_u16_data::<MAX_COLLECTION_NAME_LENGTH>();
+ let token_prefix = create_data::<MAX_TOKEN_PREFIX_LENGTH>();
+ let mode = ForeignCollectionMode::NFT;
#[extrinsic_call]
_(
RawOrigin::Root,
- owner,
- Box::new(location),
- Box::new(metadata),
+ Box::new(location.into()),
+ name,
+ token_prefix,
+ mode,
);
-
- Ok(())
- }
-
- #[benchmark]
- fn update_foreign_asset() -> Result<(), BenchmarkError> {
- let owner: T::AccountId = account("user", 0, 1);
- let location: VersionedMultiLocation = VersionedMultiLocation::from(X1(Parachain(2000)));
- let metadata: AssetMetadata<
- <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance,
- > = AssetMetadata {
- name: bounded(b"name"),
- symbol: bounded(b"symbol"),
- decimals: 18,
- minimal_balance: 1u32.into(),
- };
- let metadata2: AssetMetadata<
- <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance,
- > = AssetMetadata {
- name: bounded(b"name2"),
- symbol: bounded(b"symbol2"),
- decimals: 18,
- minimal_balance: 1u32.into(),
- };
- let mut balance: <<T as Config>::Currency as Currency<
- <T as frame_system::Config>::AccountId,
- >>::Balance = 4_000_000_000u32.into();
- balance = balance * balance;
- <T as Config>::Currency::make_free_balance_be(&owner, balance);
- Pallet::<T>::register_foreign_asset(
- RawOrigin::Root.into(),
- owner,
- Box::new(location.clone()),
- Box::new(metadata),
- )?;
-
- #[extrinsic_call]
- _(RawOrigin::Root, 0, Box::new(location), Box::new(metadata2));
Ok(())
}
pallets/foreign-assets/src/impl_fungibles.rsdiffbeforeafterboth--- a/pallets/foreign-assets/src/impl_fungibles.rs
+++ /dev/null
@@ -1,499 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-//! Implementations for fungibles trait.
-
-use frame_support::traits::tokens::{
- DepositConsequence, Fortitude, Precision, Preservation, Provenance, WithdrawConsequence,
-};
-use frame_system::Config as SystemConfig;
-use pallet_common::{CollectionHandle, CommonCollectionOperations};
-use pallet_fungible::FungibleHandle;
-use sp_runtime::traits::{CheckedAdd, CheckedSub};
-use up_data_structs::budget;
-
-use super::*;
-
-impl<T: Config> fungibles::Inspect<<T as SystemConfig>::AccountId> for Pallet<T>
-where
- T: orml_tokens::Config<CurrencyId = AssetId>,
- BalanceOf<T>: From<<T as pallet_balances::Config>::Balance>,
- BalanceOf<T>: From<<T as orml_tokens::Config>::Balance>,
- <T as pallet_balances::Config>::Balance: From<BalanceOf<T>>,
- <T as orml_tokens::Config>::Balance: From<BalanceOf<T>>,
-{
- type AssetId = AssetId;
- type Balance = BalanceOf<T>;
-
- fn total_issuance(asset: Self::AssetId) -> Self::Balance {
- log::trace!(target: "fassets::impl_foreign_assets", "impl_fungible total_issuance");
-
- match asset {
- AssetId::NativeAssetId(NativeCurrency::Here) => {
- <pallet_balances::Pallet<T> as fungible::Inspect<T::AccountId>>::total_issuance()
- .into()
- }
- AssetId::NativeAssetId(NativeCurrency::Parent) => {
- <orml_tokens::Pallet<T> as fungibles::Inspect<T::AccountId>>::total_issuance(
- AssetId::NativeAssetId(NativeCurrency::Parent),
- )
- .into()
- }
- AssetId::ForeignAssetId(fid) => {
- let target_collection_id = match <AssetBinding<T>>::get(fid) {
- Some(v) => v,
- None => return Zero::zero(),
- };
- let collection_handle = match <CollectionHandle<T>>::try_get(target_collection_id) {
- Ok(v) => v,
- Err(_) => return Zero::zero(),
- };
- let collection = FungibleHandle::cast(collection_handle);
- Self::Balance::try_from(collection.total_supply()).unwrap_or(Zero::zero())
- }
- }
- }
-
- fn minimum_balance(asset: Self::AssetId) -> Self::Balance {
- log::trace!(target: "fassets::impl_foreign_assets", "impl_fungible minimum_balance");
- match asset {
- AssetId::NativeAssetId(NativeCurrency::Here) => {
- <pallet_balances::Pallet<T> as fungible::Inspect<T::AccountId>>::minimum_balance()
- .into()
- }
- AssetId::NativeAssetId(NativeCurrency::Parent) => {
- <orml_tokens::Pallet<T> as fungibles::Inspect<T::AccountId>>::minimum_balance(
- AssetId::NativeAssetId(NativeCurrency::Parent),
- )
- .into()
- }
- AssetId::ForeignAssetId(fid) => AssetMetadatas::<T>::get(AssetId::ForeignAssetId(fid))
- .map(|x| x.minimal_balance)
- .unwrap_or_else(Zero::zero),
- }
- }
-
- fn balance(asset: Self::AssetId, who: &<T as SystemConfig>::AccountId) -> Self::Balance {
- log::trace!(target: "fassets::impl_foreign_assets", "impl_fungible balance");
- match asset {
- AssetId::NativeAssetId(NativeCurrency::Here) => {
- <pallet_balances::Pallet<T> as fungible::Inspect<T::AccountId>>::balance(who).into()
- }
- AssetId::NativeAssetId(NativeCurrency::Parent) => {
- <orml_tokens::Pallet<T> as fungibles::Inspect<T::AccountId>>::balance(
- AssetId::NativeAssetId(NativeCurrency::Parent),
- who,
- )
- .into()
- }
- AssetId::ForeignAssetId(fid) => {
- let target_collection_id = match <AssetBinding<T>>::get(fid) {
- Some(v) => v,
- None => return Zero::zero(),
- };
- let collection_handle = match <CollectionHandle<T>>::try_get(target_collection_id) {
- Ok(v) => v,
- Err(_) => return Zero::zero(),
- };
- let collection = FungibleHandle::cast(collection_handle);
- Self::Balance::try_from(
- collection.balance(T::CrossAccountId::from_sub(who.clone()), TokenId(0)),
- )
- .unwrap_or(Zero::zero())
- }
- }
- }
-
- fn total_balance(asset: Self::AssetId, who: &<T as SystemConfig>::AccountId) -> Self::Balance {
- Self::balance(asset, who)
- }
-
- fn reducible_balance(
- asset: Self::AssetId,
- who: &<T as SystemConfig>::AccountId,
- preservation: Preservation,
- fortitude: Fortitude,
- ) -> Self::Balance {
- log::trace!(target: "fassets::impl_foreign_assets", "impl_fungible reducible_balance");
-
- match asset {
- AssetId::NativeAssetId(NativeCurrency::Here) => {
- <pallet_balances::Pallet<T> as fungible::Inspect<T::AccountId>>::reducible_balance(
- who,
- preservation,
- fortitude,
- )
- .into()
- }
- AssetId::NativeAssetId(NativeCurrency::Parent) => {
- <orml_tokens::Pallet<T> as fungibles::Inspect<T::AccountId>>::reducible_balance(
- AssetId::NativeAssetId(NativeCurrency::Parent),
- who,
- preservation,
- fortitude,
- )
- .into()
- }
- _ => Self::balance(asset, who),
- }
- }
-
- fn can_deposit(
- asset: Self::AssetId,
- who: &<T as SystemConfig>::AccountId,
- amount: Self::Balance,
- provenance: Provenance,
- ) -> DepositConsequence {
- log::trace!(target: "fassets::impl_foreign_assets", "impl_fungible can_deposit");
-
- match asset {
- AssetId::NativeAssetId(NativeCurrency::Here) => {
- <pallet_balances::Pallet<T> as fungible::Inspect<T::AccountId>>::can_deposit(
- who,
- amount.into(),
- provenance,
- )
- }
- AssetId::NativeAssetId(NativeCurrency::Parent) => {
- <orml_tokens::Pallet<T> as fungibles::Inspect<T::AccountId>>::can_deposit(
- AssetId::NativeAssetId(NativeCurrency::Parent),
- who,
- amount.into(),
- provenance,
- )
- }
- _ => {
- if amount.is_zero() {
- return DepositConsequence::Success;
- }
-
- let extential_deposit_value = T::ExistentialDeposit::get();
- let ed_value: u128 = match extential_deposit_value.try_into() {
- Ok(val) => val,
- Err(_) => return DepositConsequence::CannotCreate,
- };
- let extential_deposit: Self::Balance = match ed_value.try_into() {
- Ok(val) => val,
- Err(_) => return DepositConsequence::CannotCreate,
- };
-
- let new_total_balance = match Self::balance(asset, who).checked_add(&amount) {
- Some(x) => x,
- None => return DepositConsequence::Overflow,
- };
-
- if new_total_balance < extential_deposit {
- return DepositConsequence::BelowMinimum;
- }
-
- DepositConsequence::Success
- }
- }
- }
-
- fn can_withdraw(
- asset: Self::AssetId,
- who: &<T as SystemConfig>::AccountId,
- amount: Self::Balance,
- ) -> WithdrawConsequence<Self::Balance> {
- log::trace!(target: "fassets::impl_foreign_assets", "impl_fungible can_withdraw");
- let value: u128 = match amount.try_into() {
- Ok(val) => val,
- Err(_) => return WithdrawConsequence::UnknownAsset,
- };
-
- match asset {
- AssetId::NativeAssetId(NativeCurrency::Here) => {
- let this_amount: <T as pallet_balances::Config>::Balance = match value.try_into() {
- Ok(val) => val,
- Err(_) => {
- return WithdrawConsequence::UnknownAsset;
- }
- };
- match <pallet_balances::Pallet<T> as fungible::Inspect<T::AccountId>>::can_withdraw(
- who,
- this_amount,
- ) {
- WithdrawConsequence::BalanceLow => WithdrawConsequence::BalanceLow,
- WithdrawConsequence::WouldDie => WithdrawConsequence::WouldDie,
- WithdrawConsequence::UnknownAsset => WithdrawConsequence::UnknownAsset,
- WithdrawConsequence::Underflow => WithdrawConsequence::Underflow,
- WithdrawConsequence::Overflow => WithdrawConsequence::Overflow,
- WithdrawConsequence::Frozen => WithdrawConsequence::Frozen,
- WithdrawConsequence::Success => WithdrawConsequence::Success,
- _ => WithdrawConsequence::BalanceLow,
- }
- }
- AssetId::NativeAssetId(NativeCurrency::Parent) => {
- let parent_amount: <T as orml_tokens::Config>::Balance = match value.try_into() {
- Ok(val) => val,
- Err(_) => {
- return WithdrawConsequence::UnknownAsset;
- }
- };
- match <orml_tokens::Pallet<T> as fungibles::Inspect<T::AccountId>>::can_withdraw(
- AssetId::NativeAssetId(NativeCurrency::Parent),
- who,
- parent_amount,
- ) {
- WithdrawConsequence::BalanceLow => WithdrawConsequence::BalanceLow,
- WithdrawConsequence::WouldDie => WithdrawConsequence::WouldDie,
- WithdrawConsequence::UnknownAsset => WithdrawConsequence::UnknownAsset,
- WithdrawConsequence::Underflow => WithdrawConsequence::Underflow,
- WithdrawConsequence::Overflow => WithdrawConsequence::Overflow,
- WithdrawConsequence::Frozen => WithdrawConsequence::Frozen,
- WithdrawConsequence::Success => WithdrawConsequence::Success,
- _ => WithdrawConsequence::BalanceLow,
- }
- }
- _ => match Self::balance(asset, who).checked_sub(&amount) {
- Some(_) => WithdrawConsequence::Success,
- None => WithdrawConsequence::BalanceLow,
- },
- }
- }
-
- fn asset_exists(asset: AssetId) -> bool {
- match asset {
- AssetId::NativeAssetId(_) => true,
- AssetId::ForeignAssetId(fid) => <AssetBinding<T>>::contains_key(fid),
- }
- }
-}
-
-impl<T: Config> fungibles::Mutate<<T as SystemConfig>::AccountId> for Pallet<T>
-where
- T: orml_tokens::Config<CurrencyId = AssetId>,
- BalanceOf<T>: From<<T as pallet_balances::Config>::Balance>,
- BalanceOf<T>: From<<T as orml_tokens::Config>::Balance>,
- <T as pallet_balances::Config>::Balance: From<BalanceOf<T>>,
- <T as orml_tokens::Config>::Balance: From<BalanceOf<T>>,
- u128: From<BalanceOf<T>>,
-{
- fn mint_into(
- asset: Self::AssetId,
- who: &<T as SystemConfig>::AccountId,
- amount: Self::Balance,
- ) -> Result<BalanceOf<T>, DispatchError> {
- //Self::do_mint(asset, who, amount, None)
- log::trace!(target: "fassets::impl_foreign_assets", "impl_fungible mint_into {:?}", asset);
-
- match asset {
- AssetId::NativeAssetId(NativeCurrency::Here) => {
- <pallet_balances::Pallet<T> as fungible::Mutate<T::AccountId>>::mint_into(
- who,
- amount.into(),
- )
- .map(Into::into)
- }
- AssetId::NativeAssetId(NativeCurrency::Parent) => {
- <orml_tokens::Pallet<T> as fungibles::Mutate<T::AccountId>>::mint_into(
- AssetId::NativeAssetId(NativeCurrency::Parent),
- who,
- amount.into(),
- )
- .map(Into::into)
- }
- AssetId::ForeignAssetId(fid) => {
- let target_collection_id = match <AssetBinding<T>>::get(fid) {
- Some(v) => v,
- None => {
- return Err(DispatchError::Other(
- "Associated collection not found for asset",
- ))
- }
- };
- let collection =
- FungibleHandle::cast(<CollectionHandle<T>>::try_get(target_collection_id)?);
- let account = T::CrossAccountId::from_sub(who.clone());
-
- let amount_data: pallet_fungible::CreateItemData<T> =
- (account.clone(), amount.into());
-
- pallet_fungible::Pallet::<T>::create_item_foreign(
- &collection,
- &account,
- amount_data,
- &budget::Value::new(0),
- )?;
-
- Ok(amount)
- }
- }
- }
-
- fn burn_from(
- asset: Self::AssetId,
- who: &<T as SystemConfig>::AccountId,
- amount: Self::Balance,
- precision: Precision,
- fortitude: Fortitude,
- ) -> Result<Self::Balance, DispatchError> {
- // let f = DebitFlags { keep_alive: false, best_effort: false };
- log::trace!(target: "fassets::impl_foreign_assets", "impl_fungible burn_from");
-
- match asset {
- AssetId::NativeAssetId(NativeCurrency::Here) => {
- <pallet_balances::Pallet<T> as fungible::Mutate<T::AccountId>>::burn_from(
- who,
- amount.into(),
- precision,
- fortitude,
- )
- .map(Into::into)
- }
- AssetId::NativeAssetId(NativeCurrency::Parent) => {
- <orml_tokens::Pallet<T> as fungibles::Mutate<T::AccountId>>::burn_from(
- AssetId::NativeAssetId(NativeCurrency::Parent),
- who,
- amount.into(),
- precision,
- fortitude,
- )
- .map(Into::into)
- }
- AssetId::ForeignAssetId(fid) => {
- let target_collection_id = match <AssetBinding<T>>::get(fid) {
- Some(v) => v,
- None => {
- return Err(DispatchError::Other(
- "Associated collection not found for asset",
- ))
- }
- };
- let collection =
- FungibleHandle::cast(<CollectionHandle<T>>::try_get(target_collection_id)?);
- pallet_fungible::Pallet::<T>::burn_foreign(
- &collection,
- &T::CrossAccountId::from_sub(who.clone()),
- amount.into(),
- )?;
-
- Ok(amount)
- }
- }
- }
-
- fn transfer(
- asset: Self::AssetId,
- source: &<T as SystemConfig>::AccountId,
- dest: &<T as SystemConfig>::AccountId,
- amount: Self::Balance,
- preservation: Preservation,
- ) -> Result<Self::Balance, DispatchError> {
- // let f = TransferFlags { keep_alive, best_effort: false, burn_dust: false };
- log::trace!(target: "fassets::impl_foreign_assets", "impl_fungible transfer");
-
- match asset {
- AssetId::NativeAssetId(NativeCurrency::Here) => {
- match <pallet_balances::Pallet<T> as fungible::Mutate<T::AccountId>>::transfer(
- source,
- dest,
- amount.into(),
- preservation,
- ) {
- Ok(_) => Ok(amount),
- Err(_) => Err(DispatchError::Other(
- "Bad amount to relay chain value conversion",
- )),
- }
- }
- AssetId::NativeAssetId(NativeCurrency::Parent) => {
- match <orml_tokens::Pallet<T> as fungibles::Mutate<T::AccountId>>::transfer(
- AssetId::NativeAssetId(NativeCurrency::Parent),
- source,
- dest,
- amount.into(),
- preservation,
- ) {
- Ok(_) => Ok(amount),
- Err(e) => Err(e),
- }
- }
- AssetId::ForeignAssetId(fid) => {
- let target_collection_id = match <AssetBinding<T>>::get(fid) {
- Some(v) => v,
- None => {
- return Err(DispatchError::Other(
- "Associated collection not found for asset",
- ))
- }
- };
- let collection =
- FungibleHandle::cast(<CollectionHandle<T>>::try_get(target_collection_id)?);
-
- pallet_fungible::Pallet::<T>::transfer(
- &collection,
- &T::CrossAccountId::from_sub(source.clone()),
- &T::CrossAccountId::from_sub(dest.clone()),
- amount.into(),
- &budget::Value::new(0),
- )
- .map_err(|e| e.error)?;
-
- Ok(amount)
- }
- }
- }
-}
-
-#[cfg(not(debug_assertions))]
-extern "C" {
- // This function does not exists, thus compilation will fail, if its call is
- // not optimized away, which is only possible if it's not called at all.
- //
- // not(debug_assertions) is used to ensure compiler is dropping unused functions, as
- // this option is enabled in release by defailt
- //
- // FIXME: maybe use build.rs, to ensure it will fail even in release with debug_assertions
- // enabled?
- fn unbalanced_fungible_is_called();
-}
-macro_rules! ensure_balanced {
- () => {{
- #[cfg(debug_assertions)]
- panic!("unbalanced fungible methods should not be used");
- #[cfg(not(debug_assertions))]
- {
- unsafe { unbalanced_fungible_is_called() };
- unreachable!();
- }
- }};
-}
-
-impl<T: Config> fungibles::Unbalanced<<T as SystemConfig>::AccountId> for Pallet<T>
-where
- T: orml_tokens::Config<CurrencyId = AssetId>,
- BalanceOf<T>: From<<T as pallet_balances::Config>::Balance>,
- BalanceOf<T>: From<<T as orml_tokens::Config>::Balance>,
- <T as pallet_balances::Config>::Balance: From<BalanceOf<T>>,
- <T as orml_tokens::Config>::Balance: From<BalanceOf<T>>,
- u128: From<BalanceOf<T>>,
-{
- fn handle_dust(_dust: fungibles::Dust<<T as SystemConfig>::AccountId, Self>) {
- ensure_balanced!();
- }
- fn write_balance(
- _asset: Self::AssetId,
- _who: &<T as SystemConfig>::AccountId,
- _amount: Self::Balance,
- ) -> Result<Option<Self::Balance>, DispatchError> {
- ensure_balanced!();
- }
- fn set_total_issuance(_asset: Self::AssetId, _amount: Self::Balance) {
- ensure_balanced!();
- }
-}
pallets/foreign-assets/src/lib.rsdiffbeforeafterboth--- a/pallets/foreign-assets/src/lib.rs
+++ b/pallets/foreign-assets/src/lib.rs
@@ -14,113 +14,38 @@
// You should have received a copy of the GNU General Public License
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-//! # Foreign assets
-//!
-//! - [`Config`]
-//! - [`Call`]
-//! - [`Pallet`]
+//! # Foreign Assets
//!
//! ## Overview
//!
-//! The foreign assests pallet provides functions for:
-//!
-//! - Local and foreign assets management. The foreign assets can be updated without runtime upgrade.
-//! - Bounds between asset and target collection for cross chain transfer and inner transfers.
-//!
-//! ## Overview
-//!
-//! Under construction
+//! The Foreign Assets is a proxy that maps XCM operations to the Unique Network's pallets logic.
#![cfg_attr(not(feature = "std"), no_std)]
#![allow(clippy::unused_unit)]
-use frame_support::{
- dispatch::DispatchResult,
- ensure,
- pallet_prelude::*,
- traits::{fungible, fungibles, Currency, EnsureOrigin},
-};
+use core::ops::Deref;
+
+use frame_support::{dispatch::DispatchResult, pallet_prelude::*, traits::EnsureOrigin, PalletId};
use frame_system::pallet_prelude::*;
-use pallet_common::erc::CrossAccountId;
-use pallet_fungible::Pallet as PalletFungible;
-use scale_info::TypeInfo;
-use serde::{Deserialize, Serialize};
-use sp_runtime::{
- traits::{One, Zero},
- ArithmeticError,
+use pallet_common::{
+ dispatch::CollectionDispatch, erc::CrossAccountId, XcmExtensions, NATIVE_FUNGIBLE_COLLECTION_ID,
};
-use sp_std::{boxed::Box, vec::Vec};
-use staging_xcm::{latest::MultiLocation, VersionedMultiLocation};
-// NOTE: MultiLocation is used in storages, we will need to do migration if upgrade the
-// MultiLocation to the XCM v3.
+use sp_runtime::traits::AccountIdConversion;
+use sp_std::{boxed::Box, vec, vec::Vec};
use staging_xcm::{
opaque::latest::{prelude::XcmError, Weight},
- v3::XcmContext,
+ v3::{prelude::*, MultiAsset, XcmContext},
+ VersionedAssetId,
};
-use staging_xcm_executor::{traits::WeightTrader, Assets};
-use up_data_structs::{CollectionId, CollectionMode, CreateCollectionData, TokenId};
-
-// TODO: Move to primitives
-// Id of native currency.
-// 0 - QTZ\UNQ
-// 1 - KSM\DOT
-#[derive(
- Clone,
- Copy,
- Eq,
- PartialEq,
- PartialOrd,
- Ord,
- MaxEncodedLen,
- RuntimeDebug,
- Encode,
- Decode,
- TypeInfo,
- Serialize,
- Deserialize,
-)]
-pub enum NativeCurrency {
- Here = 0,
- Parent = 1,
-}
-
-#[derive(
- Clone,
- Copy,
- Eq,
- PartialEq,
- PartialOrd,
- Ord,
- MaxEncodedLen,
- RuntimeDebug,
- Encode,
- Decode,
- TypeInfo,
- Serialize,
- Deserialize,
-)]
-pub enum AssetId {
- ForeignAssetId(ForeignAssetId),
- NativeAssetId(NativeCurrency),
-}
-
-pub trait TryAsForeign<T, F> {
- fn try_as_foreign(asset: T) -> Option<F>;
-}
-
-impl TryAsForeign<AssetId, ForeignAssetId> for AssetId {
- fn try_as_foreign(asset: AssetId) -> Option<ForeignAssetId> {
- match asset {
- Self::ForeignAssetId(id) => Some(id),
- _ => None,
- }
- }
-}
-
-pub type ForeignAssetId = u32;
-pub type CurrencyId = AssetId;
+use staging_xcm_executor::{
+ traits::{ConvertLocation, Error as XcmExecutorError, TransactAsset, WeightTrader},
+ Assets,
+};
+use up_data_structs::{
+ budget::ZeroBudget, CollectionId, CollectionMode, CollectionName, CollectionTokenPrefix,
+ CreateCollectionData, CreateFungibleData, CreateItemData, TokenId,
+};
-mod impl_fungibles;
pub mod weights;
#[cfg(feature = "runtime-benchmarks")]
@@ -128,44 +53,12 @@
pub use module::*;
pub use weights::WeightInfo;
-
-/// Type alias for currency balance.
-pub type BalanceOf<T> =
- <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
-
-/// A mapping between ForeignAssetId and AssetMetadata.
-pub trait AssetIdMapping<ForeignAssetId, MultiLocation, AssetMetadata> {
- /// Returns the AssetMetadata associated with a given ForeignAssetId.
- fn get_asset_metadata(foreign_asset_id: ForeignAssetId) -> Option<AssetMetadata>;
- /// Returns the MultiLocation associated with a given ForeignAssetId.
- fn get_multi_location(foreign_asset_id: ForeignAssetId) -> Option<MultiLocation>;
- /// Returns the CurrencyId associated with a given MultiLocation.
- fn get_currency_id(multi_location: MultiLocation) -> Option<CurrencyId>;
-}
-
-pub struct XcmForeignAssetIdMapping<T>(sp_std::marker::PhantomData<T>);
-
-impl<T: Config> AssetIdMapping<ForeignAssetId, MultiLocation, AssetMetadata<BalanceOf<T>>>
- for XcmForeignAssetIdMapping<T>
-{
- fn get_asset_metadata(foreign_asset_id: ForeignAssetId) -> Option<AssetMetadata<BalanceOf<T>>> {
- log::trace!(target: "fassets::asset_metadatas", "call");
- Pallet::<T>::asset_metadatas(AssetId::ForeignAssetId(foreign_asset_id))
- }
-
- fn get_multi_location(foreign_asset_id: ForeignAssetId) -> Option<MultiLocation> {
- log::trace!(target: "fassets::get_multi_location", "call");
- Pallet::<T>::foreign_asset_locations(foreign_asset_id)
- }
-
- fn get_currency_id(multi_location: MultiLocation) -> Option<CurrencyId> {
- log::trace!(target: "fassets::get_currency_id", "call");
- Pallet::<T>::location_to_currency_ids(multi_location).map(AssetId::ForeignAssetId)
- }
-}
#[frame_support::pallet]
pub mod module {
+ use pallet_common::CollectionIssuer;
+ use up_data_structs::CollectionDescription;
+
use super::*;
#[pallet::config]
@@ -173,44 +66,34 @@
frame_system::Config
+ pallet_common::Config
+ pallet_fungible::Config
- + orml_tokens::Config
+ pallet_balances::Config
{
/// The overarching event type.
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
- /// Currency type for withdraw and balance storage.
- type Currency: Currency<Self::AccountId>;
+ /// Origin for force registering of a foreign asset.
+ type ForceRegisterOrigin: EnsureOrigin<Self::RuntimeOrigin>;
- /// Required origin for registering asset.
- type RegisterOrigin: EnsureOrigin<Self::RuntimeOrigin>;
+ /// The ID of the foreign assets pallet.
+ type PalletId: Get<PalletId>;
+
+ /// Self-location of this parachain.
+ type SelfLocation: Get<MultiLocation>;
+
+ /// The converter from a MultiLocation to a CrossAccountId.
+ type LocationToAccountId: ConvertLocation<Self::CrossAccountId>;
/// Weight information for the extrinsics in this module.
type WeightInfo: WeightInfo;
- }
-
- pub type AssetName = BoundedVec<u8, ConstU32<32>>;
- pub type AssetSymbol = BoundedVec<u8, ConstU32<7>>;
-
- #[derive(Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen)]
- pub struct AssetMetadata<Balance> {
- pub name: AssetName,
- pub symbol: AssetSymbol,
- pub decimals: u8,
- pub minimal_balance: Balance,
}
#[pallet::error]
pub enum Error<T> {
- /// The given location could not be used (e.g. because it cannot be expressed in the
- /// desired version of XCM).
- BadLocation,
- /// MultiLocation existed
- MultiLocationExisted,
- /// AssetId not exists
- AssetIdNotExists,
- /// AssetId exists
- AssetIdExisted,
+ /// The foreign asset is already registered.
+ ForeignAssetAlreadyRegistered,
+
+ /// The given asset ID could not be converted into the current XCM version.
+ BadForeignAssetId,
}
#[pallet::event]
@@ -218,64 +101,46 @@
pub enum Event<T: Config> {
/// The foreign asset registered.
ForeignAssetRegistered {
- asset_id: ForeignAssetId,
- asset_address: MultiLocation,
- metadata: AssetMetadata<BalanceOf<T>>,
- },
- /// The foreign asset updated.
- ForeignAssetUpdated {
- asset_id: ForeignAssetId,
- asset_address: MultiLocation,
- metadata: AssetMetadata<BalanceOf<T>>,
- },
- /// The asset registered.
- AssetRegistered {
- asset_id: AssetId,
- metadata: AssetMetadata<BalanceOf<T>>,
- },
- /// The asset updated.
- AssetUpdated {
- asset_id: AssetId,
- metadata: AssetMetadata<BalanceOf<T>>,
+ collection_id: CollectionId,
+ asset_id: Box<VersionedAssetId>,
},
}
- /// Next available Foreign AssetId ID.
- ///
- /// NextForeignAssetId: ForeignAssetId
+ /// The corresponding collections of foreign assets.
#[pallet::storage]
- #[pallet::getter(fn next_foreign_asset_id)]
- pub type NextForeignAssetId<T: Config> = StorageValue<_, ForeignAssetId, ValueQuery>;
- /// The storages for MultiLocations.
- ///
- /// ForeignAssetLocations: map ForeignAssetId => Option<MultiLocation>
- #[pallet::storage]
- #[pallet::getter(fn foreign_asset_locations)]
- pub type ForeignAssetLocations<T: Config> =
- StorageMap<_, Twox64Concat, ForeignAssetId, staging_xcm::v3::MultiLocation, OptionQuery>;
+ #[pallet::getter(fn foreign_asset_to_collection)]
+ pub type ForeignAssetToCollection<T: Config> =
+ StorageMap<_, Twox64Concat, staging_xcm::v3::AssetId, CollectionId, OptionQuery>;
- /// The storages for CurrencyIds.
- ///
- /// LocationToCurrencyIds: map MultiLocation => Option<ForeignAssetId>
+ /// The corresponding foreign assets of collections.
#[pallet::storage]
- #[pallet::getter(fn location_to_currency_ids)]
- pub type LocationToCurrencyIds<T: Config> =
- StorageMap<_, Twox64Concat, staging_xcm::v3::MultiLocation, ForeignAssetId, OptionQuery>;
+ #[pallet::getter(fn collection_to_foreign_asset)]
+ pub type CollectionToForeignAsset<T: Config> =
+ StorageMap<_, Twox64Concat, CollectionId, staging_xcm::v3::AssetId, OptionQuery>;
- /// The storages for AssetMetadatas.
- ///
- /// AssetMetadatas: map AssetIds => Option<AssetMetadata>
+ /// The correponding NFT token id of reserve NFTs
#[pallet::storage]
- #[pallet::getter(fn asset_metadatas)]
- pub type AssetMetadatas<T: Config> =
- StorageMap<_, Twox64Concat, AssetId, AssetMetadata<BalanceOf<T>>, OptionQuery>;
+ #[pallet::getter(fn foreign_reserve_asset_instance_to_token_id)]
+ pub type ForeignReserveAssetInstanceToTokenId<T: Config> = StorageDoubleMap<
+ Hasher1 = Twox64Concat,
+ Key1 = CollectionId,
+ Hasher2 = Blake2_128Concat,
+ Key2 = staging_xcm::v3::AssetInstance,
+ Value = TokenId,
+ QueryKind = OptionQuery,
+ >;
- /// The storages for assets to fungible collection binding
- ///
+ /// The correponding reserve NFT of a token ID
#[pallet::storage]
- #[pallet::getter(fn asset_binding)]
- pub type AssetBinding<T: Config> =
- StorageMap<_, Twox64Concat, ForeignAssetId, CollectionId, OptionQuery>;
+ #[pallet::getter(fn token_id_to_foreign_reserve_asset_instance)]
+ pub type TokenIdToForeignReserveAssetInstance<T: Config> = StorageDoubleMap<
+ Hasher1 = Twox64Concat,
+ Key1 = CollectionId,
+ Hasher2 = Blake2_128Concat,
+ Key2 = TokenId,
+ Value = staging_xcm::v3::AssetInstance,
+ QueryKind = OptionQuery,
+ >;
#[pallet::pallet]
pub struct Pallet<T>(_);
@@ -284,196 +149,432 @@
impl<T: Config> Pallet<T> {
#[pallet::call_index(0)]
#[pallet::weight(<T as Config>::WeightInfo::register_foreign_asset())]
- pub fn register_foreign_asset(
+ pub fn force_register_foreign_asset(
origin: OriginFor<T>,
- owner: T::AccountId,
- location: Box<VersionedMultiLocation>,
- metadata: Box<AssetMetadata<BalanceOf<T>>>,
+ versioned_asset_id: Box<VersionedAssetId>,
+ name: CollectionName,
+ token_prefix: CollectionTokenPrefix,
+ mode: ForeignCollectionMode,
) -> DispatchResult {
- T::RegisterOrigin::ensure_origin(origin.clone())?;
+ T::ForceRegisterOrigin::ensure_origin(origin.clone())?;
- let location: MultiLocation = (*location)
+ let asset_id: AssetId = versioned_asset_id
+ .as_ref()
+ .clone()
.try_into()
- .map_err(|()| Error::<T>::BadLocation)?;
+ .map_err(|()| Error::<T>::BadForeignAssetId)?;
- let md = metadata.clone();
- let name: Vec<u16> = md.name.into_iter().map(|x| x as u16).collect::<Vec<u16>>();
- let mut description: Vec<u16> = "Foreign assets collection for "
+ ensure!(
+ !<ForeignAssetToCollection<T>>::contains_key(asset_id),
+ <Error<T>>::ForeignAssetAlreadyRegistered,
+ );
+
+ let foreign_collection_owner = Self::pallet_account();
+
+ let description: CollectionDescription = "Foreign Assets Collection"
.encode_utf16()
- .collect::<Vec<u16>>();
- description.append(&mut name.clone());
+ .collect::<Vec<_>>()
+ .try_into()
+ .expect("description length < max description length; qed");
+
+ let collection_id = T::CollectionDispatch::create(
+ foreign_collection_owner,
+ CollectionIssuer::Internals,
+ CreateCollectionData {
+ name,
+ token_prefix,
+ description,
+ mode: mode.into(),
+ ..Default::default()
+ },
+ )?;
- let data: CreateCollectionData<T::CrossAccountId> = CreateCollectionData {
- name: name.try_into().unwrap(),
- description: description.try_into().unwrap(),
- mode: CollectionMode::Fungible(md.decimals),
- ..Default::default()
- };
- let owner = T::CrossAccountId::from_sub(owner);
- let bounded_collection_id =
- <PalletFungible<T>>::init_foreign_collection(owner.clone(), owner, data)?;
- let foreign_asset_id =
- Self::do_register_foreign_asset(&location, &metadata, bounded_collection_id)?;
+ <ForeignAssetToCollection<T>>::insert(asset_id, collection_id);
+ <CollectionToForeignAsset<T>>::insert(collection_id, asset_id);
Self::deposit_event(Event::<T>::ForeignAssetRegistered {
- asset_id: foreign_asset_id,
- asset_address: location,
- metadata: *metadata,
+ collection_id,
+ asset_id: versioned_asset_id,
});
- Ok(())
- }
-
- #[pallet::call_index(1)]
- #[pallet::weight(<T as Config>::WeightInfo::update_foreign_asset())]
- pub fn update_foreign_asset(
- origin: OriginFor<T>,
- foreign_asset_id: ForeignAssetId,
- location: Box<VersionedMultiLocation>,
- metadata: Box<AssetMetadata<BalanceOf<T>>>,
- ) -> DispatchResult {
- T::RegisterOrigin::ensure_origin(origin)?;
-
- let location: MultiLocation = (*location)
- .try_into()
- .map_err(|()| Error::<T>::BadLocation)?;
- Self::do_update_foreign_asset(foreign_asset_id, &location, &metadata)?;
- Self::deposit_event(Event::<T>::ForeignAssetUpdated {
- asset_id: foreign_asset_id,
- asset_address: location,
- metadata: *metadata,
- });
Ok(())
}
}
}
impl<T: Config> Pallet<T> {
- fn get_next_foreign_asset_id() -> Result<ForeignAssetId, DispatchError> {
- NextForeignAssetId::<T>::try_mutate(|current| -> Result<ForeignAssetId, DispatchError> {
- let id = *current;
- *current = current
- .checked_add(One::one())
- .ok_or(ArithmeticError::Overflow)?;
- Ok(id)
- })
+ fn pallet_account() -> T::CrossAccountId {
+ let owner: T::AccountId = T::PalletId::get().into_account_truncating();
+ T::CrossAccountId::from_sub(owner)
}
- fn do_register_foreign_asset(
- location: &MultiLocation,
- metadata: &AssetMetadata<BalanceOf<T>>,
- bounded_collection_id: CollectionId,
- ) -> Result<ForeignAssetId, DispatchError> {
- let foreign_asset_id = Self::get_next_foreign_asset_id()?;
- LocationToCurrencyIds::<T>::try_mutate(location, |maybe_currency_ids| -> DispatchResult {
- ensure!(
- maybe_currency_ids.is_none(),
- Error::<T>::MultiLocationExisted
- );
- *maybe_currency_ids = Some(foreign_asset_id);
- // *maybe_currency_ids = Some(CurrencyId::ForeignAsset(foreign_asset_id));
+ /// Converts a concrete asset ID (the asset multilocation) to a local collection on Unique Network.
+ ///
+ /// The multilocation corresponds to a local collection if:
+ /// * It is `Here` location that corresponds to the native token of this parachain.
+ /// * It is `../Parachain(<Unique Network Para ID>)` that also corresponds to the native token of this parachain.
+ /// * It is `../Parachain(<Unique Network Para ID>)/GeneralIndex(<Collection ID>)` that corresponds
+ /// to the collection with the ID equal to `<Collection ID>`. The `<Collection ID>` must be in the valid range,
+ /// otherwise `None` is returned.
+ /// * It is `GeneralIndex(<Collection ID>)`. Same as the last one above.
+ ///
+ /// If the multilocation doesn't match the patterns listed above,
+ /// or the `<Collection ID>` points to a foreign collection,
+ /// `None` is returned, identifying that the given multilocation doesn't correspond to a local collection.
+ fn local_asset_id_to_collection(asset_id: &AssetId) -> Option<CollectionLocality> {
+ let AssetId::Concrete(asset_location) = asset_id else {
+ return None;
+ };
- ForeignAssetLocations::<T>::try_mutate(
- foreign_asset_id,
- |maybe_location| -> DispatchResult {
- ensure!(maybe_location.is_none(), Error::<T>::MultiLocationExisted);
- *maybe_location = Some(*location);
+ let self_location = T::SelfLocation::get();
- AssetMetadatas::<T>::try_mutate(
- AssetId::ForeignAssetId(foreign_asset_id),
- |maybe_asset_metadatas| -> DispatchResult {
- ensure!(maybe_asset_metadatas.is_none(), Error::<T>::AssetIdExisted);
- *maybe_asset_metadatas = Some(metadata.clone());
- Ok(())
- },
- )
- },
- )?;
+ if *asset_location == Here.into() || *asset_location == self_location {
+ return Some(CollectionLocality::Local(NATIVE_FUNGIBLE_COLLECTION_ID));
+ }
+
+ let prefix = if asset_location.parents == 0 {
+ &Here
+ } else if asset_location.parents == self_location.parents {
+ &self_location.interior
+ } else {
+ return None;
+ };
+
+ let GeneralIndex(collection_id) = asset_location.interior.match_and_split(prefix)? else {
+ return None;
+ };
- AssetBinding::<T>::try_mutate(foreign_asset_id, |collection_id| -> DispatchResult {
- *collection_id = Some(bounded_collection_id);
- Ok(())
- })
- })?;
+ let collection_id = CollectionId((*collection_id).try_into().ok()?);
- Ok(foreign_asset_id)
+ Self::collection_to_foreign_asset(collection_id)
+ .is_none()
+ .then_some(CollectionLocality::Local(collection_id))
+ }
+
+ /// Converts an asset ID to a Unique Network's collection locality (either foreign or a local one).
+ ///
+ /// The function will check if the asset's reserve location has the corresponding
+ /// foreign collection on Unique Network,
+ /// and will return the "foreign" locality containing the collection ID if found.
+ ///
+ /// If no corresponding foreign collection is found, the function will check
+ /// if the asset's reserve location corresponds to a local collection.
+ /// If the local collection is found, the "local" locality with the collection ID is returned.
+ ///
+ /// If all of the above have failed, the `AssetIdConversionFailed` error will be returned.
+ fn asset_to_collection(asset_id: &AssetId) -> Result<CollectionLocality, XcmError> {
+ Self::foreign_asset_to_collection(asset_id)
+ .map(CollectionLocality::Foreign)
+ .or_else(|| Self::local_asset_id_to_collection(asset_id))
+ .ok_or_else(|| XcmExecutorError::AssetIdConversionFailed.into())
}
- fn do_update_foreign_asset(
- foreign_asset_id: ForeignAssetId,
- location: &MultiLocation,
- metadata: &AssetMetadata<BalanceOf<T>>,
+ /// Converts an XCM asset instance of local collection to the Unique Network's token ID.
+ ///
+ /// The asset instance corresponds to the Unique Network's token ID if it is in the following format:
+ /// `AssetInstance::Index(<token ID>)`.
+ ///
+ /// If the asset instance is not in the valid format or the `<token ID>` can't fit into the valid token ID,
+ /// `None` will be returned.
+ ///
+ /// Note: this function can return `Some` containing the token ID of a non-existing NFT.
+ /// It returns `None` when it failed to convert the `asset_instance` to a local ID.
+ fn local_asset_instance_to_token_id(asset_instance: &AssetInstance) -> Option<TokenId> {
+ match asset_instance {
+ AssetInstance::Index(token_id) => Some(TokenId((*token_id).try_into().ok()?)),
+ _ => None,
+ }
+ }
+
+ /// Obtains the token ID of the `asset_instance` in the collection.
+ ///
+ /// Note: this function can return `Some` containing the token ID of a non-existing NFT.
+ /// It returns `None` when it failed to convert the `asset_instance` to a local ID.
+ fn asset_instance_to_token_id(
+ collection_locality: CollectionLocality,
+ asset_instance: &AssetInstance,
+ ) -> Option<TokenId> {
+ match collection_locality {
+ CollectionLocality::Local(_) => Self::local_asset_instance_to_token_id(asset_instance),
+ CollectionLocality::Foreign(collection_id) => {
+ Self::foreign_reserve_asset_instance_to_token_id(collection_id, asset_instance)
+ }
+ }
+ }
+
+ /// Creates a foreign item in the the collection.
+ fn create_foreign_asset_instance(
+ xcm_ext: &dyn XcmExtensions<T>,
+ collection_id: CollectionId,
+ asset_instance: &AssetInstance,
+ to: T::CrossAccountId,
) -> DispatchResult {
- ForeignAssetLocations::<T>::try_mutate(
- foreign_asset_id,
- |maybe_multi_locations| -> DispatchResult {
- let old_multi_locations = maybe_multi_locations
- .as_mut()
- .ok_or(Error::<T>::AssetIdNotExists)?;
+ let derivative_token_id = xcm_ext.create_item(
+ &Self::pallet_account(),
+ to,
+ CreateItemData::NFT(Default::default()),
+ &ZeroBudget,
+ )?;
+
+ <ForeignReserveAssetInstanceToTokenId<T>>::insert(
+ collection_id,
+ asset_instance,
+ derivative_token_id,
+ );
+
+ <TokenIdToForeignReserveAssetInstance<T>>::insert(
+ collection_id,
+ derivative_token_id,
+ asset_instance,
+ );
+
+ Ok(())
+ }
+
+ /// Deposits an asset instance to the `to` account.
+ ///
+ /// Either transfers an existing item from the pallet's account
+ /// or creates a foreign item.
+ fn deposit_asset_instance(
+ xcm_ext: &dyn XcmExtensions<T>,
+ collection_locality: CollectionLocality,
+ asset_instance: &AssetInstance,
+ to: T::CrossAccountId,
+ ) -> XcmResult {
+ let token_id = Self::asset_instance_to_token_id(collection_locality, asset_instance);
+
+ let deposit_result = match (collection_locality, token_id) {
+ (_, Some(token_id)) => {
+ let depositor = &Self::pallet_account();
+ let from = depositor;
+ let amount = 1;
+
+ xcm_ext.transfer_item(depositor, from, &to, token_id, amount, &ZeroBudget)
+ }
+ (CollectionLocality::Foreign(collection_id), None) => {
+ Self::create_foreign_asset_instance(xcm_ext, collection_id, asset_instance, to)
+ }
+ (CollectionLocality::Local(_), None) => {
+ return Err(XcmExecutorError::InstanceConversionFailed.into());
+ }
+ };
+
+ deposit_result
+ .map_err(|_| XcmError::FailedToTransactAsset("non-fungible item deposit failed"))
+ }
- AssetMetadatas::<T>::try_mutate(
- AssetId::ForeignAssetId(foreign_asset_id),
- |maybe_asset_metadatas| -> DispatchResult {
- ensure!(
- maybe_asset_metadatas.is_some(),
- Error::<T>::AssetIdNotExists
- );
+ /// Withdraws an asset instance from the `from` account.
+ ///
+ /// Transfers the asset instance to the pallet's account.
+ fn withdraw_asset_instance(
+ xcm_ext: &dyn XcmExtensions<T>,
+ collection_locality: CollectionLocality,
+ asset_instance: &AssetInstance,
+ from: T::CrossAccountId,
+ ) -> XcmResult {
+ let token_id = Self::asset_instance_to_token_id(collection_locality, asset_instance)
+ .ok_or(XcmExecutorError::InstanceConversionFailed)?;
+
+ let depositor = &from;
+ let to = Self::pallet_account();
+ let amount = 1;
+ xcm_ext
+ .transfer_item(depositor, &from, &to, token_id, amount, &ZeroBudget)
+ .map_err(|_| XcmError::FailedToTransactAsset("non-fungible item withdraw failed"))?;
+
+ Ok(())
+ }
+}
+
+impl<T: Config> TransactAsset for Pallet<T> {
+ fn can_check_in(
+ _origin: &MultiLocation,
+ _what: &MultiAsset,
+ _context: &XcmContext,
+ ) -> XcmResult {
+ Err(XcmError::Unimplemented)
+ }
+
+ fn check_in(_origin: &MultiLocation, _what: &MultiAsset, _context: &XcmContext) {}
+
+ fn can_check_out(
+ _dest: &MultiLocation,
+ _what: &MultiAsset,
+ _context: &XcmContext,
+ ) -> XcmResult {
+ Err(XcmError::Unimplemented)
+ }
+
+ fn check_out(_dest: &MultiLocation, _what: &MultiAsset, _context: &XcmContext) {}
+
+ fn deposit_asset(
+ what: &MultiAsset,
+ to: &MultiLocation,
+ _context: Option<&XcmContext>,
+ ) -> XcmResult {
+ let to = T::LocationToAccountId::convert_location(to)
+ .ok_or(XcmExecutorError::AccountIdConversionFailed)?;
+
+ let collection_locality = Self::asset_to_collection(&what.id)?;
+ let dispatch = T::CollectionDispatch::dispatch(*collection_locality)
+ .map_err(|_| XcmExecutorError::AssetIdConversionFailed)?;
+
+ let collection = dispatch.as_dyn();
+ let xcm_ext = collection.xcm_extensions().ok_or(XcmError::Unimplemented)?;
- // modify location
- if location != old_multi_locations {
- LocationToCurrencyIds::<T>::remove(*old_multi_locations);
- LocationToCurrencyIds::<T>::try_mutate(
- location,
- |maybe_currency_ids| -> DispatchResult {
- ensure!(
- maybe_currency_ids.is_none(),
- Error::<T>::MultiLocationExisted
- );
- // *maybe_currency_ids = Some(CurrencyId::ForeignAsset(foreign_asset_id));
- *maybe_currency_ids = Some(foreign_asset_id);
- Ok(())
- },
- )?;
- }
- *maybe_asset_metadatas = Some(metadata.clone());
- *old_multi_locations = *location;
- Ok(())
- },
+ match what.fun {
+ Fungibility::Fungible(amount) => xcm_ext
+ .create_item(
+ &Self::pallet_account(),
+ to,
+ CreateItemData::Fungible(CreateFungibleData { value: amount }),
+ &ZeroBudget,
)
- },
- )
+ .map(|_| ())
+ .map_err(|_| XcmError::FailedToTransactAsset("fungible item deposit failed")),
+
+ Fungibility::NonFungible(asset_instance) => {
+ Self::deposit_asset_instance(xcm_ext, collection_locality, &asset_instance, to)
+ }
+ }
+ }
+
+ fn withdraw_asset(
+ what: &MultiAsset,
+ from: &MultiLocation,
+ _maybe_context: Option<&XcmContext>,
+ ) -> Result<staging_xcm_executor::Assets, XcmError> {
+ let from = T::LocationToAccountId::convert_location(from)
+ .ok_or(XcmExecutorError::AccountIdConversionFailed)?;
+
+ let collection_locality = Self::asset_to_collection(&what.id)?;
+ let dispatch = T::CollectionDispatch::dispatch(*collection_locality)
+ .map_err(|_| XcmExecutorError::AssetIdConversionFailed)?;
+
+ let collection = dispatch.as_dyn();
+ let xcm_ext = collection.xcm_extensions().ok_or(XcmError::NoPermission)?;
+
+ match what.fun {
+ Fungibility::Fungible(amount) => xcm_ext
+ .burn_item(from, TokenId::default(), amount)
+ .map_err(|_| XcmError::FailedToTransactAsset("fungible item withdraw failed"))?,
+
+ Fungibility::NonFungible(asset_instance) => {
+ Self::withdraw_asset_instance(xcm_ext, collection_locality, &asset_instance, from)?;
+ }
+ }
+
+ Ok(what.clone().into())
+ }
+
+ fn internal_transfer_asset(
+ what: &MultiAsset,
+ from: &MultiLocation,
+ to: &MultiLocation,
+ _context: &XcmContext,
+ ) -> Result<staging_xcm_executor::Assets, XcmError> {
+ let from = T::LocationToAccountId::convert_location(from)
+ .ok_or(XcmExecutorError::AccountIdConversionFailed)?;
+
+ let to = T::LocationToAccountId::convert_location(to)
+ .ok_or(XcmExecutorError::AccountIdConversionFailed)?;
+
+ let collection_locality = Self::asset_to_collection(&what.id)?;
+
+ let dispatch = T::CollectionDispatch::dispatch(*collection_locality)
+ .map_err(|_| XcmExecutorError::AssetIdConversionFailed)?;
+ let collection = dispatch.as_dyn();
+ let xcm_ext = collection.xcm_extensions().ok_or(XcmError::NoPermission)?;
+
+ let depositor = &from;
+
+ let token_id;
+ let amount;
+ let map_error: fn(DispatchError) -> XcmError;
+
+ match what.fun {
+ Fungibility::Fungible(fungible_amount) => {
+ token_id = TokenId::default();
+ amount = fungible_amount;
+ map_error = |_| XcmError::FailedToTransactAsset("fungible item transfer failed");
+ }
+
+ Fungibility::NonFungible(asset_instance) => {
+ token_id = Self::asset_instance_to_token_id(collection_locality, &asset_instance)
+ .ok_or(XcmExecutorError::InstanceConversionFailed)?;
+
+ amount = 1;
+ map_error = |_| XcmError::FailedToTransactAsset("non-fungible item transfer failed")
+ }
+ }
+
+ xcm_ext
+ .transfer_item(depositor, &from, &to, token_id, amount, &ZeroBudget)
+ .map_err(map_error)?;
+
+ Ok(what.clone().into())
}
}
-pub use frame_support::{
- traits::{
- fungibles::Balanced, tokens::currency::Currency as CurrencyT, OnUnbalanced as OnUnbalancedT,
- },
- weights::{WeightToFee, WeightToFeePolynomial},
-};
+#[derive(Clone, Copy)]
+pub enum CollectionLocality {
+ Local(CollectionId),
+ Foreign(CollectionId),
+}
+
+impl Deref for CollectionLocality {
+ type Target = CollectionId;
-pub struct FreeForAll<
- WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,
- AssetId: Get<MultiLocation>,
- AccountId,
- Currency: CurrencyT<AccountId>,
- OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,
->(
- Weight,
- Currency::Balance,
- PhantomData<(WeightToFee, AssetId, AccountId, Currency, OnUnbalanced)>,
-);
+ fn deref(&self) -> &Self::Target {
+ match self {
+ Self::Local(id) => id,
+ Self::Foreign(id) => id,
+ }
+ }
+}
-impl<
- WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,
- AssetId: Get<MultiLocation>,
- AccountId,
- Currency: CurrencyT<AccountId>,
- OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,
- > WeightTrader for FreeForAll<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>
+pub struct CurrencyIdConvert<T: Config>(PhantomData<T>);
+impl<T: Config> sp_runtime::traits::Convert<CollectionId, Option<MultiLocation>>
+ for CurrencyIdConvert<T>
{
+ fn convert(collection_id: CollectionId) -> Option<MultiLocation> {
+ if collection_id == NATIVE_FUNGIBLE_COLLECTION_ID {
+ Some(T::SelfLocation::get())
+ } else {
+ <Pallet<T>>::collection_to_foreign_asset(collection_id)
+ .and_then(|asset_id| match asset_id {
+ AssetId::Concrete(location) => Some(location),
+ _ => None,
+ })
+ .or_else(|| {
+ T::SelfLocation::get()
+ .pushed_with_interior(GeneralIndex(collection_id.0.into()))
+ .ok()
+ })
+ }
+ }
+}
+
+#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]
+pub enum ForeignCollectionMode {
+ NFT,
+ Fungible(u8),
+}
+
+impl From<ForeignCollectionMode> for CollectionMode {
+ fn from(value: ForeignCollectionMode) -> Self {
+ match value {
+ ForeignCollectionMode::NFT => Self::NFT,
+ ForeignCollectionMode::Fungible(decimals) => Self::Fungible(decimals),
+ }
+ }
+}
+
+pub struct FreeForAll;
+
+impl WeightTrader for FreeForAll {
fn new() -> Self {
- Self(Weight::default(), Zero::zero(), PhantomData)
+ Self
}
fn buy_weight(
@@ -484,17 +585,5 @@
) -> Result<Assets, XcmError> {
log::trace!(target: "fassets::weight", "buy_weight weight: {:?}, payment: {:?}", weight, payment);
Ok(payment)
- }
-}
-impl<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced> Drop
- for FreeForAll<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>
-where
- WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,
- AssetId: Get<MultiLocation>,
- Currency: CurrencyT<AccountId>,
- OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,
-{
- fn drop(&mut self) {
- OnUnbalanced::on_unbalanced(Currency::issue(self.1));
}
}
pallets/fungible/src/benchmarking.rsdiffbeforeafterboth--- a/pallets/fungible/src/benchmarking.rs
+++ b/pallets/fungible/src/benchmarking.rs
@@ -15,7 +15,9 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
use frame_benchmarking::{account, v2::*};
-use pallet_common::{bench_init, benchmarking::create_collection_raw};
+use pallet_common::{
+ bench_init, benchmarking::create_collection_raw, CollectionIssuer, Pallet as PalletCommon,
+};
use sp_std::prelude::*;
use up_data_structs::{budget::Unlimited, CollectionMode, MAX_ITEMS_PER_BATCH};
@@ -30,7 +32,9 @@
create_collection_raw(
owner,
CollectionMode::Fungible(0),
- |owner: T::CrossAccountId, data| <Pallet<T>>::init_collection(owner.clone(), owner, data),
+ |owner: T::CrossAccountId, data| {
+ <PalletCommon<T>>::init_collection(owner.clone(), CollectionIssuer::User(owner), data)
+ },
FungibleHandle::cast,
)
}
pallets/fungible/src/common.rsdiffbeforeafterboth--- a/pallets/fungible/src/common.rs
+++ b/pallets/fungible/src/common.rs
@@ -19,7 +19,7 @@
use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, fail, weights::Weight};
use pallet_common::{
weights::WeightInfo as _, with_weight, CommonCollectionOperations, CommonWeightInfo,
- Error as CommonError, RefungibleExtensions, SelfWeightOf as PalletCommonWeightOf,
+ Error as CommonError, SelfWeightOf as PalletCommonWeightOf, XcmExtensions,
};
use sp_runtime::{ArithmeticError, DispatchError};
use sp_std::{vec, vec::Vec};
@@ -114,7 +114,7 @@
<Pallet<T>>::create_item(self, &sender, (to, fungible_data.value), nesting_budget),
<CommonWeights<T>>::create_item(&data),
),
- _ => fail!(<Error<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),
+ _ => fail!(<CommonError<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),
}
}
@@ -133,7 +133,7 @@
.checked_add(data.value)
.ok_or(ArithmeticError::Overflow)?;
}
- _ => fail!(<Error<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),
+ _ => fail!(<CommonError<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),
}
}
@@ -152,7 +152,7 @@
let weight = <CommonWeights<T>>::create_multiple_items_ex(&data);
let data = match data {
up_data_structs::CreateItemExData::Fungible(f) => f,
- _ => fail!(<Error<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),
+ _ => fail!(<CommonError<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),
};
with_weight(
@@ -426,10 +426,6 @@
return 0;
}
<Allowance<T>>::get((self.id, sender, spender))
- }
-
- fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>> {
- None
}
fn total_pieces(&self, token: TokenId) -> Option<u128> {
@@ -439,6 +435,10 @@
<TotalSupply<T>>::try_get(self.id).ok()
}
+ fn xcm_extensions(&self) -> Option<&dyn XcmExtensions<T>> {
+ Some(self)
+ }
+
fn set_allowance_for_all(
&self,
_owner: T::CrossAccountId,
@@ -457,3 +457,57 @@
fail!(<Error<T>>::FungibleTokensAreAlwaysValid)
}
}
+
+impl<T: Config> XcmExtensions<T> for FungibleHandle<T> {
+ fn create_item_internal(
+ &self,
+ depositor: &<T>::CrossAccountId,
+ to: <T>::CrossAccountId,
+ data: CreateItemData,
+ nesting_budget: &dyn Budget,
+ ) -> Result<TokenId, sp_runtime::DispatchError> {
+ match &data {
+ up_data_structs::CreateItemData::Fungible(fungible_data) => {
+ <Pallet<T>>::create_multiple_items(
+ self,
+ depositor,
+ [(to, fungible_data.value)].into_iter().collect(),
+ nesting_budget,
+ )?
+ }
+ _ => fail!(<CommonError<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),
+ }
+
+ Ok(TokenId::default())
+ }
+
+ fn transfer_item_internal(
+ &self,
+ depositor: &<T>::CrossAccountId,
+ from: &<T>::CrossAccountId,
+ to: &<T>::CrossAccountId,
+ token: TokenId,
+ amount: u128,
+ nesting_budget: &dyn Budget,
+ ) -> sp_runtime::DispatchResult {
+ ensure!(
+ token == TokenId::default(),
+ <CommonError<T>>::FungibleItemsHaveNoId
+ );
+
+ <Pallet<T>>::transfer_internal(self, depositor, from, to, amount, nesting_budget)
+ .map(|_| ())
+ .map_err(|post_info| post_info.error)
+ }
+
+ fn burn_item_internal(
+ &self,
+ from: <T>::CrossAccountId,
+ token: TokenId,
+ amount: u128,
+ ) -> sp_runtime::DispatchResult {
+ <Self as CommonCollectionOperations<T>>::burn_item(self, from, token, amount)
+ .map(|_| ())
+ .map_err(|post_info| post_info.error)
+ }
+}
pallets/fungible/src/lib.rsdiffbeforeafterboth--- a/pallets/fungible/src/lib.rs
+++ b/pallets/fungible/src/lib.rs
@@ -95,8 +95,8 @@
use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};
use sp_std::{collections::btree_map::BTreeMap, vec::Vec};
use up_data_structs::{
- budget::Budget, mapping::TokenAddressMapping, AccessMode, CollectionId, CreateCollectionData,
- Property, PropertyKey, TokenId,
+ budget::Budget, mapping::TokenAddressMapping, AccessMode, CollectionId, Property, PropertyKey,
+ TokenId,
};
use weights::WeightInfo;
@@ -121,8 +121,6 @@
#[pallet::error]
pub enum Error<T> {
- /// Not Fungible item data used to mint in Fungible collection.
- NotFungibleDataUsedToMintFungibleCollectionToken,
/// Tried to set data for fungible item.
FungibleItemsDontHaveData,
/// Fungible token does not support nesting.
@@ -212,24 +210,6 @@
/// Pallet implementation for fungible assets
impl<T: Config> Pallet<T> {
- /// Initializes the collection. Returns [CollectionId] on success, [DispatchError] otherwise.
- pub fn init_collection(
- owner: T::CrossAccountId,
- payer: T::CrossAccountId,
- data: CreateCollectionData<T::CrossAccountId>,
- ) -> Result<CollectionId, DispatchError> {
- <PalletCommon<T>>::init_collection(owner, payer, data)
- }
-
- /// Initializes the collection with ForeignCollection flag. Returns [CollectionId] on success, [DispatchError] otherwise.
- pub fn init_foreign_collection(
- owner: T::CrossAccountId,
- payer: T::CrossAccountId,
- data: CreateCollectionData<T::CrossAccountId>,
- ) -> Result<CollectionId, DispatchError> {
- <PalletCommon<T>>::init_foreign_collection(owner, payer, data)
- }
-
/// Destroys a collection.
pub fn destroy_collection(
collection: FungibleHandle<T>,
@@ -293,54 +273,11 @@
let balance = <Balance<T>>::get((collection.id, owner))
.checked_sub(amount)
.ok_or(<CommonError<T>>::TokenValueTooLow)?;
-
- // Foreign collection check
- ensure!(!collection.flags.foreign, <CommonError<T>>::NoPermission);
if collection.permissions.access() == AccessMode::AllowList {
collection.check_allowlist(owner)?;
- }
-
- // =========
-
- if balance == 0 {
- <Balance<T>>::remove((collection.id, owner));
- <PalletStructure<T>>::unnest_if_nested(owner, collection.id, TokenId::default());
- } else {
- <Balance<T>>::insert((collection.id, owner), balance);
}
- <TotalSupply<T>>::insert(collection.id, total_supply);
-
- <PalletEvm<T>>::deposit_log(
- ERC20Events::Transfer {
- from: *owner.as_eth(),
- to: H160::default(),
- value: amount.into(),
- }
- .to_log(collection_id_to_address(collection.id)),
- );
- <PalletCommon<T>>::deposit_event(CommonEvent::ItemDestroyed(
- collection.id,
- TokenId::default(),
- owner.clone(),
- amount,
- ));
- Ok(())
- }
- /// Burns the specified amount of the token.
- pub fn burn_foreign(
- collection: &FungibleHandle<T>,
- owner: &T::CrossAccountId,
- amount: u128,
- ) -> DispatchResult {
- let total_supply = <TotalSupply<T>>::get(collection.id)
- .checked_sub(amount)
- .ok_or(<CommonError<T>>::TokenValueTooLow)?;
-
- let balance = <Balance<T>>::get((collection.id, owner))
- .checked_sub(amount)
- .ok_or(<CommonError<T>>::TokenValueTooLow)?;
// =========
if balance == 0 {
@@ -468,14 +405,25 @@
}
/// Minting tokens for multiple IDs.
- /// It is a utility function used in [`create_multiple_items`][`Pallet::create_multiple_items`]
- /// and [`create_multiple_items_foreign`][`Pallet::create_multiple_items_foreign`]
- pub fn create_multiple_items_common(
+ /// See [`create_item`][`Pallet::create_item`] for more details.
+ pub fn create_multiple_items(
collection: &FungibleHandle<T>,
- sender: &T::CrossAccountId,
+ depositor: &T::CrossAccountId,
data: BTreeMap<T::CrossAccountId, u128>,
nesting_budget: &dyn Budget,
) -> DispatchResult {
+ if !collection.is_owner_or_admin(depositor) {
+ ensure!(
+ collection.permissions.mint_mode(),
+ <CommonError<T>>::PublicMintingNotAllowed
+ );
+ collection.check_allowlist(depositor)?;
+
+ for (owner, _) in data.iter() {
+ collection.check_allowlist(owner)?;
+ }
+ }
+
let total_supply = data
.values()
.copied()
@@ -486,7 +434,7 @@
for (to, _) in data.iter() {
<PalletStructure<T>>::check_nesting(
- sender,
+ depositor,
to,
collection.id,
TokenId::default(),
@@ -532,44 +480,7 @@
Ok(())
}
-
- /// Minting tokens for multiple IDs.
- /// See [`create_item`][`Pallet::create_item`] for more details.
- pub fn create_multiple_items(
- collection: &FungibleHandle<T>,
- sender: &T::CrossAccountId,
- data: BTreeMap<T::CrossAccountId, u128>,
- nesting_budget: &dyn Budget,
- ) -> DispatchResult {
- // Foreign collection check
- ensure!(!collection.flags.foreign, <CommonError<T>>::NoPermission);
- if !collection.is_owner_or_admin(sender) {
- ensure!(
- collection.permissions.mint_mode(),
- <CommonError<T>>::PublicMintingNotAllowed
- );
- collection.check_allowlist(sender)?;
-
- for (owner, _) in data.iter() {
- collection.check_allowlist(owner)?;
- }
- }
-
- Self::create_multiple_items_common(collection, sender, data, nesting_budget)
- }
-
- /// Minting tokens for multiple IDs.
- /// See [`create_item_foreign`][`Pallet::create_item_foreign`] for more details.
- pub fn create_multiple_items_foreign(
- collection: &FungibleHandle<T>,
- sender: &T::CrossAccountId,
- data: BTreeMap<T::CrossAccountId, u128>,
- nesting_budget: &dyn Budget,
- ) -> DispatchResult {
- Self::create_multiple_items_common(collection, sender, data, nesting_budget)
- }
-
fn set_allowance_unchecked(
collection: &FungibleHandle<T>,
owner: &T::CrossAccountId,
@@ -795,24 +706,6 @@
nesting_budget: &dyn Budget,
) -> DispatchResult {
Self::create_multiple_items(
- collection,
- sender,
- [(data.0, data.1)].into_iter().collect(),
- nesting_budget,
- )
- }
-
- /// Creates fungible token.
- ///
- /// - `data`: Contains user who will become the owners of the tokens and amount
- /// of tokens he will receive.
- pub fn create_item_foreign(
- collection: &FungibleHandle<T>,
- sender: &T::CrossAccountId,
- data: CreateItemData<T>,
- nesting_budget: &dyn Budget,
- ) -> DispatchResult {
- Self::create_multiple_items_foreign(
collection,
sender,
[(data.0, data.1)].into_iter().collect(),
pallets/nonfungible/src/benchmarking.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/benchmarking.rs
+++ b/pallets/nonfungible/src/benchmarking.rs
@@ -18,6 +18,7 @@
use pallet_common::{
bench_init,
benchmarking::{create_collection_raw, property_key, property_value},
+ CollectionIssuer, Pallet as PalletCommon,
};
use sp_std::prelude::*;
use up_data_structs::{
@@ -56,7 +57,9 @@
create_collection_raw(
owner,
CollectionMode::NFT,
- |owner: T::CrossAccountId, data| <Pallet<T>>::init_collection(owner.clone(), owner, data),
+ |owner: T::CrossAccountId, data| {
+ <PalletCommon<T>>::init_collection(owner.clone(), CollectionIssuer::User(owner), data)
+ },
NonfungibleHandle::cast,
)
}
pallets/nonfungible/src/common.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/common.rs
+++ b/pallets/nonfungible/src/common.rs
@@ -19,8 +19,8 @@
use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, fail, weights::Weight};
use pallet_common::{
weights::WeightInfo as _, with_weight, write_token_properties_total_weight,
- CommonCollectionOperations, CommonWeightInfo, RefungibleExtensions,
- SelfWeightOf as PalletCommonWeightOf,
+ CommonCollectionOperations, CommonWeightInfo, SelfWeightOf as PalletCommonWeightOf,
+ XcmExtensions,
};
use pallet_structure::Pallet as PalletStructure;
use sp_runtime::DispatchError;
@@ -533,10 +533,6 @@
} else {
0
}
- }
-
- fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>> {
- None
}
fn total_pieces(&self, token: TokenId) -> Option<u128> {
@@ -547,6 +543,10 @@
}
}
+ fn xcm_extensions(&self) -> Option<&dyn XcmExtensions<T>> {
+ Some(self)
+ }
+
fn set_allowance_for_all(
&self,
owner: T::CrossAccountId,
@@ -570,3 +570,53 @@
)
}
}
+
+impl<T: Config> XcmExtensions<T> for NonfungibleHandle<T> {
+ fn token_has_children(&self, token: TokenId) -> bool {
+ <Pallet<T>>::token_has_children(self.id, token)
+ }
+
+ fn create_item_internal(
+ &self,
+ depositor: &<T>::CrossAccountId,
+ to: <T>::CrossAccountId,
+ data: up_data_structs::CreateItemData,
+ nesting_budget: &dyn Budget,
+ ) -> Result<TokenId, sp_runtime::DispatchError> {
+ <Pallet<T>>::create_multiple_items(
+ self,
+ depositor,
+ vec![map_create_data::<T>(data, &to)?],
+ nesting_budget,
+ )?;
+
+ Ok(self.last_token_id())
+ }
+
+ fn transfer_item_internal(
+ &self,
+ depositor: &<T>::CrossAccountId,
+ from: &<T>::CrossAccountId,
+ to: &<T>::CrossAccountId,
+ token: TokenId,
+ amount: u128,
+ nesting_budget: &dyn Budget,
+ ) -> sp_runtime::DispatchResult {
+ ensure!(amount == 1, <Error<T>>::NonfungibleItemsHaveNoAmount);
+
+ <Pallet<T>>::transfer_internal(self, depositor, from, to, token, nesting_budget)
+ .map(|_| ())
+ .map_err(|post_info| post_info.error)
+ }
+
+ fn burn_item_internal(
+ &self,
+ from: T::CrossAccountId,
+ token: TokenId,
+ amount: u128,
+ ) -> sp_runtime::DispatchResult {
+ ensure!(amount == 1, <Error<T>>::NonfungibleItemsHaveNoAmount);
+
+ <Pallet<T>>::burn(self, &from, token)
+ }
+}
pallets/nonfungible/src/lib.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -117,8 +117,8 @@
use sp_std::{collections::btree_map::BTreeMap, vec, vec::Vec};
use up_data_structs::{
budget::Budget, mapping::TokenAddressMapping, AccessMode, AuxPropertyValue, CollectionId,
- CreateCollectionData, CreateNftExData, CustomDataLimit, PropertiesPermissionMap, Property,
- PropertyKey, PropertyKeyPermission, PropertyScope, PropertyValue, TokenChild, TokenId,
+ CreateNftExData, CustomDataLimit, PropertiesPermissionMap, Property, PropertyKey,
+ PropertyKeyPermission, PropertyScope, PropertyValue, TokenChild, TokenId,
TokenProperties as TokenPropertiesT,
};
use weights::WeightInfo;
@@ -383,19 +383,6 @@
// unchecked calls skips any permission checks
impl<T: Config> Pallet<T> {
- /// Create NFT collection
- ///
- /// `init_collection` will take non-refundable deposit for collection creation.
- ///
- /// - `data`: Contains settings for collection limits and permissions.
- pub fn init_collection(
- owner: T::CrossAccountId,
- payer: T::CrossAccountId,
- data: CreateCollectionData<T::CrossAccountId>,
- ) -> Result<CollectionId, DispatchError> {
- <PalletCommon<T>>::init_collection(owner, payer, data)
- }
-
/// Destroy NFT collection
///
/// `destroy_collection` will throw error if collection contains any tokens.
pallets/refungible/src/benchmarking.rsdiffbeforeafterboth--- a/pallets/refungible/src/benchmarking.rs
+++ b/pallets/refungible/src/benchmarking.rs
@@ -20,6 +20,7 @@
use pallet_common::{
bench_init,
benchmarking::{create_collection_raw, property_key, property_value},
+ CollectionIssuer, Pallet as PalletCommon,
};
use sp_std::prelude::*;
use up_data_structs::{
@@ -61,7 +62,9 @@
create_collection_raw(
owner,
CollectionMode::ReFungible,
- |owner: T::CrossAccountId, data| <Pallet<T>>::init_collection(owner.clone(), owner, data),
+ |owner: T::CrossAccountId, data| {
+ <PalletCommon<T>>::init_collection(owner.clone(), CollectionIssuer::User(owner), data)
+ },
RefungibleHandle::cast,
)
}
pallets/refungible/src/lib.rsdiffbeforeafterboth--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -103,7 +103,7 @@
use sp_runtime::{ArithmeticError, DispatchError, DispatchResult, TransactionOutcome};
use sp_std::{collections::btree_map::BTreeMap, vec, vec::Vec};
use up_data_structs::{
- budget::Budget, mapping::TokenAddressMapping, AccessMode, CollectionId, CreateCollectionData,
+ budget::Budget, mapping::TokenAddressMapping, AccessMode, CollectionId,
CreateRefungibleExMultipleOwners, PropertiesPermissionMap, Property, PropertyKey,
PropertyKeyPermission, PropertyScope, PropertyValue, TokenId, TokenOwnerError,
TokenProperties as TokenPropertiesT, MAX_REFUNGIBLE_PIECES,
@@ -296,19 +296,6 @@
// unchecked calls skips any permission checks
impl<T: Config> Pallet<T> {
- /// Create RFT collection
- ///
- /// `init_collection` will take non-refundable deposit for collection creation.
- ///
- /// - `data`: Contains settings for collection limits and permissions.
- pub fn init_collection(
- owner: T::CrossAccountId,
- payer: T::CrossAccountId,
- data: CreateCollectionData<T::CrossAccountId>,
- ) -> Result<CollectionId, DispatchError> {
- <PalletCommon<T>>::init_collection(owner, payer, data)
- }
-
/// Destroy RFT collection
///
/// `destroy_collection` will throw error if collection contains any tokens.
pallets/structure/src/benchmarking.rsdiffbeforeafterboth--- a/pallets/structure/src/benchmarking.rs
+++ b/pallets/structure/src/benchmarking.rs
@@ -16,7 +16,7 @@
use frame_benchmarking::v2::{account, benchmarks, BenchmarkError};
use frame_support::traits::{fungible::Balanced, tokens::Precision, Get};
-use pallet_common::Config as CommonConfig;
+use pallet_common::{CollectionIssuer, Config as CommonConfig};
use pallet_evm::account::CrossAccountId;
use sp_std::vec;
use up_data_structs::{
@@ -44,7 +44,7 @@
.unwrap();
T::CollectionDispatch::create(
caller_cross.clone(),
- caller_cross.clone(),
+ CollectionIssuer::User(caller_cross.clone()),
CreateCollectionData {
mode: CollectionMode::NFT,
..Default::default()
pallets/unique/src/eth/mod.rsdiffbeforeafterboth--- a/pallets/unique/src/eth/mod.rs
+++ b/pallets/unique/src/eth/mod.rs
@@ -26,7 +26,7 @@
dispatch::CollectionDispatch,
erc::{static_property::key, CollectionHelpersEvents},
eth::{self, collection_id_to_address, map_eth_to_id},
- CollectionById, CollectionHandle, Pallet as PalletCommon,
+ CollectionById, CollectionHandle, CollectionIssuer, Pallet as PalletCommon,
};
use pallet_evm::{account::CrossAccountId, OnMethodCall, PrecompileHandle, PrecompileResult};
use pallet_evm_coder_substrate::{
@@ -110,8 +110,12 @@
let collection_helpers_address =
T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());
- let collection_id = T::CollectionDispatch::create(caller, collection_helpers_address, data)
- .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;
+ let collection_id = T::CollectionDispatch::create(
+ caller,
+ CollectionIssuer::User(collection_helpers_address),
+ data,
+ )
+ .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;
let address = pallet_common::eth::collection_id_to_address(collection_id);
Ok(address)
}
@@ -240,8 +244,12 @@
let collection_helpers_address =
T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());
- let collection_id = T::CollectionDispatch::create(caller, collection_helpers_address, data)
- .map_err(dispatch_to_evm::<T>)?;
+ let collection_id = T::CollectionDispatch::create(
+ caller,
+ CollectionIssuer::User(collection_helpers_address),
+ data,
+ )
+ .map_err(dispatch_to_evm::<T>)?;
let address = pallet_common::eth::collection_id_to_address(collection_id);
Ok(address)
@@ -274,8 +282,12 @@
check_sent_amount_equals_collection_creation_price::<T>(value)?;
let collection_helpers_address =
T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());
- let collection_id = T::CollectionDispatch::create(caller, collection_helpers_address, data)
- .map_err(dispatch_to_evm::<T>)?;
+ let collection_id = T::CollectionDispatch::create(
+ caller,
+ CollectionIssuer::User(collection_helpers_address),
+ data,
+ )
+ .map_err(dispatch_to_evm::<T>)?;
let address = pallet_common::eth::collection_id_to_address(collection_id);
Ok(address)
pallets/unique/src/eth/stubs/CollectionHelpers.soldiffbeforeafterboth--- a/pallets/unique/src/eth/stubs/CollectionHelpers.sol
+++ b/pallets/unique/src/eth/stubs/CollectionHelpers.sol
@@ -199,7 +199,7 @@
type CollectionFlags is uint8;
library CollectionFlagsLib {
- /// Tokens in foreign collections can be transferred, but not burnt
+ /// A collection of foreign assets
CollectionFlags constant foreignField = CollectionFlags.wrap(128);
/// Supports ERC721Metadata
CollectionFlags constant erc721metadataField = CollectionFlags.wrap(64);
pallets/unique/src/lib.rsdiffbeforeafterboth--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -93,7 +93,8 @@
use frame_system::{ensure_root, ensure_signed};
use pallet_common::{
dispatch::{dispatch_tx, CollectionDispatch},
- CollectionHandle, CommonWeightInfo, Pallet as PalletCommon, RefungibleExtensionsWeightInfo,
+ CollectionHandle, CollectionIssuer, CommonWeightInfo, Pallet as PalletCommon,
+ RefungibleExtensionsWeightInfo,
};
use pallet_evm::account::CrossAccountId;
use pallet_structure::weights::WeightInfo as StructureWeightInfo;
@@ -401,7 +402,11 @@
// =========
let sender = T::CrossAccountId::from_sub(sender);
- let _id = T::CollectionDispatch::create(sender.clone(), sender, data)?;
+ let _id = T::CollectionDispatch::create(
+ sender.clone(),
+ CollectionIssuer::User(sender),
+ data,
+ )?;
Ok(())
}
primitives/data-structs/src/budget.rsdiffbeforeafterboth--- a/primitives/data-structs/src/budget.rs
+++ b/primitives/data-structs/src/budget.rs
@@ -36,3 +36,10 @@
true
}
}
+
+pub struct ZeroBudget;
+impl Budget for ZeroBudget {
+ fn consume_custom(&self, _calls: u32) -> bool {
+ false
+ }
+}
primitives/data-structs/src/lib.rsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! # Primitives crate.18//!19//! This crate contains types, traits and constants.2021#![cfg_attr(not(feature = "std"), no_std)]2223use core::{24 convert::{TryFrom, TryInto},25 fmt,26 ops::Deref,27};2829use bondrewd::Bitfields;30use derivative::Derivative;31use evm_coder::AbiCoderFlags;32use frame_support::{33 storage::{bounded_btree_map::BoundedBTreeMap, bounded_btree_set::BoundedBTreeSet},34 traits::ConstU32,35 BoundedVec,36};37use parity_scale_codec::{Decode, Encode, EncodeLike, MaxEncodedLen};38use scale_info::TypeInfo;39use serde::{Deserialize, Serialize};40use sp_core::U256;41use sp_runtime::{sp_std::prelude::Vec, ArithmeticError};42use sp_std::collections::btree_set::BTreeSet;4344mod bondrewd_codec;45mod bounded;46pub mod budget;47pub mod mapping;48mod migration;4950/// Maximum of decimal points.51pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;5253/// Maximum pieces for refungible token.54pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;55pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;5657/// Maximum tokens for user.58pub const MAX_TOKEN_OWNERSHIP: u32 = if cfg!(not(feature = "limit-testing")) {59 100_000_00060} else {61 1062};6364/// Maximum for collections can be created.65pub const COLLECTION_NUMBER_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {66 100_00067} else {68 1069};7071/// Maximum for various custom data of token.72pub const CUSTOM_DATA_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {73 204874} else {75 1076};7778/// Maximum admins per collection.79pub const COLLECTION_ADMINS_LIMIT: u32 = 5;8081/// Maximum tokens per collection.82pub const COLLECTION_TOKEN_LIMIT: u32 = u32::MAX;8384/// Maximum tokens per account.85pub const ACCOUNT_TOKEN_OWNERSHIP_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {86 100_000_00087} else {88 1089};9091/// Default timeout for transfer sponsoring NFT item.92pub const NFT_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;93/// Default timeout for transfer sponsoring fungible item.94pub const FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;95/// Default timeout for transfer sponsoring refungible item.96pub const REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;9798/// Default timeout for sponsored approving.99pub const SPONSOR_APPROVE_TIMEOUT: u32 = 5;100101// Schema limits102pub const OFFCHAIN_SCHEMA_LIMIT: u32 = 8192;103pub const VARIABLE_ON_CHAIN_SCHEMA_LIMIT: u32 = 8192;104pub const CONST_ON_CHAIN_SCHEMA_LIMIT: u32 = 32768;105106// TODO: not used. Delete?107pub const COLLECTION_FIELD_LIMIT: u32 = CONST_ON_CHAIN_SCHEMA_LIMIT;108109/// Maximal length of a collection name.110pub const MAX_COLLECTION_NAME_LENGTH: u32 = 64;111112/// Maximal length of a collection description.113pub const MAX_COLLECTION_DESCRIPTION_LENGTH: u32 = 256;114115/// Maximal length of a token prefix.116pub const MAX_TOKEN_PREFIX_LENGTH: u32 = 16;117118/// Maximal length of a property key.119pub const MAX_PROPERTY_KEY_LENGTH: u32 = 256;120121/// Maximal length of a property value.122pub const MAX_PROPERTY_VALUE_LENGTH: u32 = 32768;123124/// A maximum number of token properties.125pub const MAX_PROPERTIES_PER_ITEM: u32 = 64;126127/// Maximal lenght of extended property value.128pub const MAX_AUX_PROPERTY_VALUE_LENGTH: u32 = 2048;129130/// Maximum size for all collection properties.131pub const MAX_COLLECTION_PROPERTIES_SIZE: u32 = 40960;132133/// Maximum size of all token properties.134pub const MAX_TOKEN_PROPERTIES_SIZE: u32 = 32768;135136/// How much items can be created per single137/// create_many call.138pub const MAX_ITEMS_PER_BATCH: u32 = 120;139140/// Used for limit bounded types of token custom data.141pub type CustomDataLimit = ConstU32<CUSTOM_DATA_LIMIT>;142143/// Collection id.144#[derive(145 Encode,146 Decode,147 PartialEq,148 Eq,149 PartialOrd,150 Ord,151 Clone,152 Copy,153 Debug,154 Default,155 TypeInfo,156 MaxEncodedLen,157 Serialize,158 Deserialize,159)]160pub struct CollectionId(pub u32);161impl EncodeLike<u32> for CollectionId {}162impl EncodeLike<CollectionId> for u32 {}163164impl From<u32> for CollectionId {165 fn from(value: u32) -> Self {166 Self(value)167 }168}169170impl Deref for CollectionId {171 type Target = u32;172173 fn deref(&self) -> &Self::Target {174 &self.0175 }176}177178/// Token id.179#[derive(180 Encode,181 Decode,182 PartialEq,183 Eq,184 PartialOrd,185 Ord,186 Clone,187 Copy,188 Debug,189 Default,190 TypeInfo,191 MaxEncodedLen,192 Serialize,193 Deserialize,194)]195pub struct TokenId(pub u32);196impl EncodeLike<u32> for TokenId {}197impl EncodeLike<TokenId> for u32 {}198199impl TokenId {200 /// Try to get next token id.201 ///202 /// If next id cause overflow, then [`ArithmeticError::Overflow`] returned.203 pub fn try_next(self) -> Result<TokenId, ArithmeticError> {204 self.0205 .checked_add(1)206 .ok_or(ArithmeticError::Overflow)207 .map(Self)208 }209}210211impl From<TokenId> for U256 {212 fn from(t: TokenId) -> Self {213 t.0.into()214 }215}216217impl TryFrom<U256> for TokenId {218 type Error = &'static str;219220 fn try_from(value: U256) -> Result<Self, Self::Error> {221 Ok(TokenId(value.try_into().map_err(|_| "too large token id")?))222 }223}224225/// Token data.226#[struct_versioning::versioned(version = 2, upper)]227#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, Serialize, Deserialize)]228pub struct TokenData<CrossAccountId> {229 /// Properties of token.230 pub properties: Vec<Property>,231232 /// Token owner.233 pub owner: Option<CrossAccountId>,234235 /// Token pieces.236 #[version(2.., upper(0))]237 pub pieces: u128,238}239240// TODO: unused type241pub struct OverflowError;242impl From<OverflowError> for &'static str {243 fn from(_: OverflowError) -> Self {244 "overflow occured"245 }246}247248/// Alias for decimal points type.249pub type DecimalPoints = u8;250251/// Collection mode.252///253/// Collection can represent various types of tokens.254/// Each collection can contain only one type of tokens at a time.255/// This type helps to understand which tokens the collection contains.256#[derive(257 Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen, Serialize, Deserialize,258)]259pub enum CollectionMode {260 /// Non fungible tokens.261 NFT,262 /// Fungible tokens.263 Fungible(DecimalPoints),264 /// Refungible tokens.265 ReFungible,266}267268impl CollectionMode {269 /// Get collection mod as number.270 pub fn id(&self) -> u8 {271 match self {272 CollectionMode::NFT => 1,273 CollectionMode::Fungible(_) => 2,274 CollectionMode::ReFungible => 3,275 }276 }277}278279// TODO: unused trait280pub trait SponsoringResolve<AccountId, Call> {281 fn resolve(who: &AccountId, call: &Call) -> Option<AccountId>;282}283284/// Access mode for some token operations.285#[derive(286 Encode,287 Decode,288 Eq,289 Debug,290 Clone,291 Copy,292 PartialEq,293 TypeInfo,294 MaxEncodedLen,295 Serialize,296 Deserialize,297)]298pub enum AccessMode {299 /// Access grant for owner and admins. Used as default.300 Normal,301 /// Like a [`Normal`](AccessMode::Normal) but also users in allow list.302 AllowList,303}304impl Default for AccessMode {305 fn default() -> Self {306 Self::Normal307 }308}309310// TODO: remove in future.311#[derive(312 Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen, Serialize, Deserialize,313)]314pub enum SchemaVersion {315 ImageURL,316 Unique,317}318impl Default for SchemaVersion {319 fn default() -> Self {320 Self::ImageURL321 }322}323324// TODO: unused type325#[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo, Serialize, Deserialize)]326pub struct Ownership<AccountId> {327 pub owner: AccountId,328 pub fraction: u128,329}330331/// The state of collection sponsorship.332#[derive(333 Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen, Serialize, Deserialize,334)]335pub enum SponsorshipState<AccountId> {336 /// The fees are applied to the transaction sender.337 Disabled,338 /// The sponsor is under consideration. Until the sponsor gives his consent,339 /// the fee will still be charged to sender.340 Unconfirmed(AccountId),341 /// Transactions are sponsored by specified account.342 Confirmed(AccountId),343}344345impl<AccountId> SponsorshipState<AccountId> {346 /// Get a sponsor of the collection who has confirmed his status.347 pub fn sponsor(&self) -> Option<&AccountId> {348 match self {349 Self::Confirmed(sponsor) => Some(sponsor),350 _ => None,351 }352 }353354 /// Get a sponsor of the collection who has pending or confirmed status.355 pub fn pending_sponsor(&self) -> Option<&AccountId> {356 match self {357 Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),358 _ => None,359 }360 }361362 /// Whether the sponsorship is confirmed.363 pub fn confirmed(&self) -> bool {364 matches!(self, Self::Confirmed(_))365 }366}367368impl<T> Default for SponsorshipState<T> {369 fn default() -> Self {370 Self::Disabled371 }372}373374pub type CollectionName = BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>;375pub type CollectionDescription = BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>;376pub type CollectionTokenPrefix = BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>;377378#[derive(AbiCoderFlags, Bitfields, Clone, Copy, PartialEq, Eq, Debug, Default)]379#[bondrewd(enforce_bytes = 1)]380pub struct CollectionFlags {381 /// Tokens in foreign collections can be transferred, but not burnt382 #[bondrewd(bits = "0..1")]383 pub foreign: bool,384 /// Supports ERC721Metadata385 #[bondrewd(bits = "1..2")]386 pub erc721metadata: bool,387 /// External collections can't be managed using `unique` api388 #[bondrewd(bits = "7..8")]389 pub external: bool,390 /// Reserved flags391 #[bondrewd(bits = "2..7")]392 pub reserved: u8,393}394bondrewd_codec!(CollectionFlags);395396impl CollectionFlags {397 pub fn is_allowed_for_user(self) -> bool {398 !self.foreign && !self.external && self.reserved == 0399 }400}401402/// Base structure for represent collection.403///404/// Used to provide basic functionality for all types of collections.405///406/// #### Note407/// Collection parameters, used in storage (see [`RpcCollection`] for the RPC version).408#[struct_versioning::versioned(version = 2, upper)]409#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen)]410pub struct Collection<AccountId> {411 /// Collection owner account.412 pub owner: AccountId,413414 /// Collection mode.415 pub mode: CollectionMode,416417 /// Access mode.418 #[version(..2)]419 pub access: AccessMode,420421 /// Collection name.422 pub name: CollectionName,423424 /// Collection description.425 pub description: CollectionDescription,426427 /// Token prefix.428 pub token_prefix: CollectionTokenPrefix,429430 #[version(..2)]431 pub mint_mode: bool,432433 #[version(..2)]434 pub offchain_schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,435436 #[version(..2)]437 pub schema_version: SchemaVersion,438439 /// The state of sponsorship of the collection.440 pub sponsorship: SponsorshipState<AccountId>,441442 /// Collection limits.443 pub limits: CollectionLimits,444445 /// Collection permissions.446 #[version(2.., upper(Default::default()))]447 pub permissions: CollectionPermissions,448449 #[version(2.., upper(Default::default()))]450 pub flags: CollectionFlags,451452 #[version(..2)]453 pub variable_on_chain_schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>,454455 #[version(..2)]456 pub const_on_chain_schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>,457458 #[version(..2)]459 pub meta_update_permission: MetaUpdatePermission,460}461462#[derive(Debug, Encode, Decode, Clone, PartialEq, TypeInfo, Serialize, Deserialize)]463pub struct RpcCollectionFlags {464 /// Is collection is foreign.465 pub foreign: bool,466 /// Collection supports ERC721Metadata.467 pub erc721metadata: bool,468}469470/// Collection parameters, used in RPC calls (see [`Collection`] for the storage version).471#[struct_versioning::versioned(version = 2, upper)]472#[derive(Debug, Encode, Decode, Clone, PartialEq, TypeInfo, Serialize, Deserialize)]473pub struct RpcCollection<AccountId> {474 /// Collection owner account.475 pub owner: AccountId,476477 /// Collection mode.478 pub mode: CollectionMode,479480 /// Collection name.481 pub name: Vec<u16>,482483 /// Collection description.484 pub description: Vec<u16>,485486 /// Token prefix.487 pub token_prefix: Vec<u8>,488489 /// The state of sponsorship of the collection.490 pub sponsorship: SponsorshipState<AccountId>,491492 /// Collection limits.493 pub limits: CollectionLimits,494495 /// Collection permissions.496 pub permissions: CollectionPermissions,497498 /// Token property permissions.499 pub token_property_permissions: Vec<PropertyKeyPermission>,500501 /// Collection properties.502 pub properties: Vec<Property>,503504 /// Is collection read only.505 pub read_only: bool,506507 /// Extra collection flags508 #[version(2.., upper(RpcCollectionFlags {foreign: false, erc721metadata: false}))]509 pub flags: RpcCollectionFlags,510}511512impl<AccountId> From<CollectionVersion1<AccountId>> for RpcCollection<AccountId> {513 fn from(value: CollectionVersion1<AccountId>) -> Self {514 let CollectionVersion1 {515 name,516 description,517 owner,518 mode,519 access,520 token_prefix,521 mint_mode,522 sponsorship,523 limits,524 ..525 } = value;526527 RpcCollection {528 name: name.into_inner(),529 description: description.into_inner(),530 owner,531 mode,532 token_prefix: token_prefix.into_inner(),533 sponsorship,534 limits,535 permissions: CollectionPermissions {536 access: Some(access),537 mint_mode: Some(mint_mode),538 nesting: None,539 },540 token_property_permissions: Vec::default(),541 properties: Vec::default(),542 read_only: true,543544 flags: RpcCollectionFlags {545 foreign: false,546 erc721metadata: false,547 },548 }549 }550}551552pub struct RawEncoded(Vec<u8>);553554impl parity_scale_codec::Decode for RawEncoded {555 fn decode<I: parity_scale_codec::Input>(556 input: &mut I,557 ) -> Result<Self, parity_scale_codec::Error> {558 let mut out = Vec::new();559 while let Ok(v) = input.read_byte() {560 out.push(v);561 }562 Ok(Self(out))563 }564}565566impl Deref for RawEncoded {567 type Target = Vec<u8>;568569 fn deref(&self) -> &Self::Target {570 &self.0571 }572}573574/// Data used for create collection.575///576/// All fields are wrapped in [`Option`], where `None` means chain default.577#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, Derivative, MaxEncodedLen)]578#[derivative(Debug, Default(bound = ""))]579pub struct CreateCollectionData<CrossAccountId> {580 /// Collection mode.581 #[derivative(Default(value = "CollectionMode::NFT"))]582 pub mode: CollectionMode,583584 /// Access mode.585 pub access: Option<AccessMode>,586587 /// Collection name.588 pub name: CollectionName,589590 /// Collection description.591 pub description: CollectionDescription,592593 /// Token prefix.594 pub token_prefix: CollectionTokenPrefix,595596 /// Collection limits.597 pub limits: Option<CollectionLimits>,598599 /// Collection permissions.600 pub permissions: Option<CollectionPermissions>,601602 /// Token property permissions.603 pub token_property_permissions: CollectionPropertiesPermissionsVec,604605 /// Collection properties.606 pub properties: CollectionPropertiesVec,607608 pub admin_list: Vec<CrossAccountId>,609610 /// Pending collection sponsor.611 pub pending_sponsor: Option<CrossAccountId>,612613 pub flags: CollectionFlags,614}615616/// Bounded vector of properties permissions. Max length is [`MAX_PROPERTIES_PER_ITEM`].617// TODO: maybe rename to PropertiesPermissionsVec618pub type CollectionPropertiesPermissionsVec =619 BoundedVec<PropertyKeyPermission, ConstU32<MAX_PROPERTIES_PER_ITEM>>;620621/// Bounded vector of properties. Max length is [`MAX_PROPERTIES_PER_ITEM`].622pub type CollectionPropertiesVec = BoundedVec<Property, ConstU32<MAX_PROPERTIES_PER_ITEM>>;623624/// Limits and restrictions of a collection.625///626/// All fields are wrapped in [`Option`], where `None` means chain default.627///628/// Update with `pallet_common::Pallet::clamp_limits`.629// IMPORTANT: When adding/removing fields from this struct - don't forget to also630#[derive(631 Encode,632 Decode,633 Debug,634 Default,635 Clone,636 PartialEq,637 TypeInfo,638 MaxEncodedLen,639 Serialize,640 Deserialize,641)]642// When adding/removing fields from this struct - don't forget to also update with `pallet_common::Pallet::clamp_limits`.643// TODO: move `pallet_common::Pallet::clamp_limits` into `impl CollectionLimits`.644// TODO: may be remove [`Option`] and **pub** from fields and create struct with default values.645pub struct CollectionLimits {646 /// How many tokens can a user have on one account.647 /// * Default - [`ACCOUNT_TOKEN_OWNERSHIP_LIMIT`].648 /// * Limit - [`MAX_TOKEN_OWNERSHIP`].649 pub account_token_ownership_limit: Option<u32>,650651 /// How many bytes of data are available for sponsorship.652 /// * Default - [`CUSTOM_DATA_LIMIT`].653 /// * Limit - [`CUSTOM_DATA_LIMIT`].654 pub sponsored_data_size: Option<u32>,655656 // FIXME should we delete this or repurpose it?657 /// Times in how many blocks we sponsor data.658 ///659 /// If is `Some(v)` then **setVariableMetadata** is sponsored if there is `v` block between transactions.660 ///661 /// * Default - [`SponsoringDisabled`](SponsoringRateLimit::SponsoringDisabled).662 /// * Limit - [`MAX_SPONSOR_TIMEOUT`].663 ///664 /// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`]665 pub sponsored_data_rate_limit: Option<SponsoringRateLimit>,666 /// Maximum amount of tokens inside the collection. Chain default: [`COLLECTION_TOKEN_LIMIT`]667668 /// How many tokens can be mined into this collection.669 ///670 /// * Default - [`COLLECTION_TOKEN_LIMIT`].671 /// * Limit - [`COLLECTION_TOKEN_LIMIT`].672 pub token_limit: Option<u32>,673674 /// Timeouts for transfer sponsoring.675 ///676 /// * Default677 /// - **Fungible** - [`FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT`]678 /// - **NFT** - [`NFT_SPONSOR_TRANSFER_TIMEOUT`]679 /// - **Refungible** - [`REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT`]680 /// * Limit - [`MAX_SPONSOR_TIMEOUT`].681 pub sponsor_transfer_timeout: Option<u32>,682683 /// Timeout for sponsoring an approval in passed blocks.684 ///685 /// * Default - [`SPONSOR_APPROVE_TIMEOUT`].686 /// * Limit - [`MAX_SPONSOR_TIMEOUT`].687 pub sponsor_approve_timeout: Option<u32>,688689 /// Whether the collection owner of the collection can send tokens (which belong to other users).690 ///691 /// * Default - **false**.692 pub owner_can_transfer: Option<bool>,693694 /// Can the collection owner burn other people's tokens.695 ///696 /// * Default - **true**.697 pub owner_can_destroy: Option<bool>,698699 /// Is it possible to send tokens from this collection between users.700 ///701 /// * Default - **true**.702 pub transfers_enabled: Option<bool>,703}704705impl CollectionLimits {706 pub fn with_default_limits(collection_type: CollectionMode) -> Self {707 CollectionLimits {708 account_token_ownership_limit: Some(ACCOUNT_TOKEN_OWNERSHIP_LIMIT),709 sponsored_data_size: Some(CUSTOM_DATA_LIMIT),710 sponsored_data_rate_limit: Some(SponsoringRateLimit::SponsoringDisabled),711 token_limit: Some(COLLECTION_TOKEN_LIMIT),712 sponsor_transfer_timeout: match collection_type {713 CollectionMode::NFT => Some(NFT_SPONSOR_TRANSFER_TIMEOUT),714 CollectionMode::ReFungible => Some(REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT),715 CollectionMode::Fungible(_) => Some(FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT),716 },717 sponsor_approve_timeout: Some(SPONSOR_APPROVE_TIMEOUT),718 owner_can_transfer: Some(false),719 owner_can_destroy: Some(true),720 transfers_enabled: Some(true),721 }722 }723724 /// Get effective value for [`account_token_ownership_limit`](self.account_token_ownership_limit).725 pub fn account_token_ownership_limit(&self) -> u32 {726 self.account_token_ownership_limit727 .unwrap_or(ACCOUNT_TOKEN_OWNERSHIP_LIMIT)728 .min(MAX_TOKEN_OWNERSHIP)729 }730731 /// Get effective value for [`sponsored_data_size`](self.sponsored_data_size).732 pub fn sponsored_data_size(&self) -> u32 {733 self.sponsored_data_size734 .unwrap_or(CUSTOM_DATA_LIMIT)735 .min(CUSTOM_DATA_LIMIT)736 }737738 /// Get effective value for [`token_limit`](self.token_limit).739 pub fn token_limit(&self) -> u32 {740 self.token_limit741 .unwrap_or(COLLECTION_TOKEN_LIMIT)742 .min(COLLECTION_TOKEN_LIMIT)743 }744745 // TODO: may be replace u32 to mode?746 /// Get effective value for [`sponsor_transfer_timeout`](self.sponsor_transfer_timeout).747 pub fn sponsor_transfer_timeout(&self, default: u32) -> u32 {748 self.sponsor_transfer_timeout749 .unwrap_or(default)750 .min(MAX_SPONSOR_TIMEOUT)751 }752753 /// Get effective value for [`sponsor_approve_timeout`](self.sponsor_approve_timeout).754 pub fn sponsor_approve_timeout(&self) -> u32 {755 self.sponsor_approve_timeout756 .unwrap_or(SPONSOR_APPROVE_TIMEOUT)757 .min(MAX_SPONSOR_TIMEOUT)758 }759760 /// Get effective value for [`owner_can_transfer`](self.owner_can_transfer).761 pub fn owner_can_transfer(&self) -> bool {762 self.owner_can_transfer.unwrap_or(false)763 }764765 /// Get effective value for [`owner_can_transfer_instaled`](self.owner_can_transfer_instaled).766 pub fn owner_can_transfer_instaled(&self) -> bool {767 self.owner_can_transfer.is_some()768 }769770 /// Get effective value for [`owner_can_destroy`](self.owner_can_destroy).771 pub fn owner_can_destroy(&self) -> bool {772 self.owner_can_destroy.unwrap_or(true)773 }774775 /// Get effective value for [`transfers_enabled`](self.transfers_enabled).776 pub fn transfers_enabled(&self) -> bool {777 self.transfers_enabled.unwrap_or(true)778 }779780 /// Get effective value for [`sponsored_data_rate_limit`](self.sponsored_data_rate_limit).781 pub fn sponsored_data_rate_limit(&self) -> Option<u32> {782 match self783 .sponsored_data_rate_limit784 .unwrap_or(SponsoringRateLimit::SponsoringDisabled)785 {786 SponsoringRateLimit::SponsoringDisabled => None,787 SponsoringRateLimit::Blocks(v) => Some(v.min(MAX_SPONSOR_TIMEOUT)),788 }789 }790}791792/// Permissions on certain operations within a collection.793///794/// Some fields are wrapped in [`Option`], where `None` means chain default.795///796/// Update with `pallet_common::Pallet::clamp_permissions`.797#[derive(798 Encode,799 Decode,800 Debug,801 Default,802 Clone,803 PartialEq,804 TypeInfo,805 MaxEncodedLen,806 Serialize,807 Deserialize,808)]809// When adding/removing fields from this struct - don't forget to also update `pallet_common::Pallet::clamp_permissions`.810// TODO: move `pallet_common::Pallet::clamp_permissions` into `impl CollectionPermissions`.811pub struct CollectionPermissions {812 /// Access mode.813 ///814 /// * Default - [`AccessMode::Normal`].815 pub access: Option<AccessMode>,816817 /// Minting allowance.818 ///819 /// * Default - **false**.820 pub mint_mode: Option<bool>,821822 /// Permissions for nesting.823 ///824 /// * Default825 /// - `token_owner` - **false**826 /// - `collection_admin` - **false**827 /// - `restricted` - **None**828 pub nesting: Option<NestingPermissions>,829}830831impl CollectionPermissions {832 /// Get effective value for [`access`](self.access).833 pub fn access(&self) -> AccessMode {834 self.access.unwrap_or(AccessMode::Normal)835 }836837 /// Get effective value for [`mint_mode`](self.mint_mode).838 pub fn mint_mode(&self) -> bool {839 self.mint_mode.unwrap_or(false)840 }841842 /// Get effective value for [`nesting`](self.nesting).843 pub fn nesting(&self) -> &NestingPermissions {844 static DEFAULT: NestingPermissions = NestingPermissions {845 token_owner: false,846 collection_admin: false,847 restricted: None,848 #[cfg(feature = "runtime-benchmarks")]849 permissive: false,850 };851 self.nesting.as_ref().unwrap_or(&DEFAULT)852 }853}854855/// Inner set for collections allowed to nest.856type OwnerRestrictedSetInner = BoundedBTreeSet<CollectionId, ConstU32<16>>;857858/// Wraper for collections set allowing nest.859#[derive(860 Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative, Serialize, Deserialize,861)]862#[derivative(Debug)]863pub struct OwnerRestrictedSet(864 #[serde(with = "bounded::set_serde")]865 #[derivative(Debug(format_with = "bounded::set_debug"))]866 pub OwnerRestrictedSetInner,867);868869impl OwnerRestrictedSet {870 /// Create new set.871 pub fn new() -> Self {872 Self(Default::default())873 }874}875impl Default for OwnerRestrictedSet {876 fn default() -> Self {877 Self::new()878 }879}880impl core::ops::Deref for OwnerRestrictedSet {881 type Target = OwnerRestrictedSetInner;882 fn deref(&self) -> &Self::Target {883 &self.0884 }885}886impl core::ops::DerefMut for OwnerRestrictedSet {887 fn deref_mut(&mut self) -> &mut Self::Target {888 &mut self.0889 }890}891892impl TryFrom<BTreeSet<CollectionId>> for OwnerRestrictedSet {893 type Error = ();894895 fn try_from(value: BTreeSet<CollectionId>) -> Result<Self, Self::Error> {896 Ok(Self(value.try_into()?))897 }898}899900/// Part of collection permissions, if set, defines who is able to nest tokens into other tokens.901#[derive(902 Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative, Serialize, Deserialize,903)]904#[derivative(Debug)]905pub struct NestingPermissions {906 /// Owner of token can nest tokens under it.907 pub token_owner: bool,908 /// Admin of token collection can nest tokens under token.909 pub collection_admin: bool,910 /// If set - only tokens from specified collections can be nested.911 pub restricted: Option<OwnerRestrictedSet>,912913 #[cfg(feature = "runtime-benchmarks")]914 /// Anyone can nest tokens, mutually exclusive with `token_owner`, `admin`.915 pub permissive: bool,916}917918/// Enum denominating how often can sponsoring occur if it is enabled.919///920/// Used for [`collection limits`](CollectionLimits).921#[derive(922 Encode, Decode, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen, Serialize, Deserialize,923)]924pub enum SponsoringRateLimit {925 /// Sponsoring is disabled, and the collection sponsor will not pay for transactions926 SponsoringDisabled,927 /// Once per how many blocks can sponsorship of a transaction type occur928 Blocks(u32),929}930931/// Data used to describe an NFT at creation.932#[derive(933 Encode,934 Decode,935 MaxEncodedLen,936 Default,937 PartialEq,938 Clone,939 Derivative,940 TypeInfo,941 Serialize,942 Deserialize,943)]944#[derivative(Debug)]945pub struct CreateNftData {946 /// Key-value pairs used to describe the token as metadata947 #[serde(with = "bounded::vec_serde")]948 #[derivative(Debug(format_with = "bounded::vec_debug"))]949 /// Properties that wil be assignet to created item.950 pub properties: CollectionPropertiesVec,951}952953/// Data used to describe a Fungible token at creation.954#[derive(955 Encode,956 Decode,957 MaxEncodedLen,958 Default,959 Debug,960 Clone,961 PartialEq,962 TypeInfo,963 Serialize,964 Deserialize,965)]966pub struct CreateFungibleData {967 /// Number of fungible coins minted968 pub value: u128,969}970971/// Data used to describe a Refungible token at creation.972#[derive(973 Encode,974 Decode,975 MaxEncodedLen,976 Default,977 PartialEq,978 Clone,979 Derivative,980 TypeInfo,981 Serialize,982 Deserialize,983)]984#[derivative(Debug)]985pub struct CreateReFungibleData {986 /// Number of pieces the RFT is split into987 pub pieces: u128,988989 /// Key-value pairs used to describe the token as metadata990 #[serde(with = "bounded::vec_serde")]991 #[derivative(Debug(format_with = "bounded::vec_debug"))]992 pub properties: CollectionPropertiesVec,993}994995// TODO: remove this.996#[derive(997 Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen, Serialize, Deserialize,998)]999pub enum MetaUpdatePermission {1000 ItemOwner,1001 Admin,1002 None,1003}10041005/// Enum holding data used for creation of all three item types.1006/// Unified data for create item.1007#[derive(1008 Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo, Serialize, Deserialize,1009)]1010pub enum CreateItemData {1011 /// Data for create NFT.1012 NFT(CreateNftData),1013 /// Data for create Fungible item.1014 Fungible(CreateFungibleData),1015 /// Data for create ReFungible item.1016 ReFungible(CreateReFungibleData),1017}10181019/// Extended data for create NFT.1020#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]1021#[derivative(Debug)]1022pub struct CreateNftExData<CrossAccountId> {1023 /// Properties that wil be assignet to created item.1024 #[derivative(Debug(format_with = "bounded::vec_debug"))]1025 pub properties: CollectionPropertiesVec,10261027 /// Owner of creating item.1028 pub owner: CrossAccountId,1029}10301031/// Extended data for create ReFungible item.1032#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]1033#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]1034pub struct CreateRefungibleExMultipleOwners<CrossAccountId> {1035 #[derivative(Debug(format_with = "bounded::map_debug"))]1036 pub users: BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,1037 #[derivative(Debug(format_with = "bounded::vec_debug"))]1038 pub properties: CollectionPropertiesVec,1039}10401041/// Extended data for create ReFungible item.1042#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]1043#[derivative(Debug(bound = "CrossAccountId: fmt::Debug"))]1044pub struct CreateRefungibleExSingleOwner<CrossAccountId> {1045 pub user: CrossAccountId,1046 pub pieces: u128,1047 #[derivative(Debug(format_with = "bounded::vec_debug"))]1048 pub properties: CollectionPropertiesVec,1049}10501051/// Unified extended data for creating item.1052#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]1053#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]1054pub enum CreateItemExData<CrossAccountId> {1055 /// Extended data for create NFT.1056 NFT(1057 #[derivative(Debug(format_with = "bounded::vec_debug"))]1058 BoundedVec<CreateNftExData<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,1059 ),10601061 /// Extended data for create Fungible item.1062 Fungible(1063 #[derivative(Debug(format_with = "bounded::map_debug"))]1064 BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,1065 ),10661067 /// Extended data for create ReFungible item in case of1068 /// many tokens, each may have only one owner1069 RefungibleMultipleItems(1070 #[derivative(Debug(format_with = "bounded::vec_debug"))]1071 BoundedVec<CreateRefungibleExSingleOwner<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,1072 ),10731074 /// Extended data for create ReFungible item in case of1075 /// single token, which may have many owners1076 RefungibleMultipleOwners(CreateRefungibleExMultipleOwners<CrossAccountId>),1077}10781079impl From<CreateNftData> for CreateItemData {1080 fn from(item: CreateNftData) -> Self {1081 CreateItemData::NFT(item)1082 }1083}10841085impl From<CreateReFungibleData> for CreateItemData {1086 fn from(item: CreateReFungibleData) -> Self {1087 CreateItemData::ReFungible(item)1088 }1089}10901091impl From<CreateFungibleData> for CreateItemData {1092 fn from(item: CreateFungibleData) -> Self {1093 CreateItemData::Fungible(item)1094 }1095}10961097/// Token's address, dictated by its collection and token IDs.1098#[derive(1099 Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo, Serialize, Deserialize,1100)]1101// todo possibly rename to be used generally as an address pair1102pub struct TokenChild {1103 /// Token id.1104 pub token: TokenId,11051106 /// Collection id.1107 pub collection: CollectionId,1108}11091110/// Collection statistics.1111#[derive(1112 Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo, Serialize, Deserialize,1113)]1114pub struct CollectionStats {1115 /// Number of created items.1116 pub created: u32,11171118 /// Number of burned items.1119 pub destroyed: u32,11201121 /// Number of current items.1122 pub alive: u32,1123}11241125/// This type works like [`PhantomData`] but supports generating _scale-info_ descriptions to generate node metadata.1126#[derive(Encode, Decode, Clone, Debug)]1127#[cfg_attr(feature = "std", derive(PartialEq))]1128pub struct PhantomType<T>(core::marker::PhantomData<T>);11291130impl<T: TypeInfo + 'static> TypeInfo for PhantomType<T> {1131 type Identity = PhantomType<T>;11321133 fn type_info() -> scale_info::Type {1134 use scale_info::{1135 build::{FieldsBuilder, UnnamedFields},1136 form::MetaForm,1137 type_params, Path, Type,1138 };1139 Type::builder()1140 .path(Path::new("up_data_structs", "PhantomType"))1141 .type_params(type_params!(T))1142 .composite(1143 <FieldsBuilder<MetaForm, UnnamedFields>>::default().field(|b| b.ty::<[T; 0]>()),1144 )1145 }1146}1147impl<T> MaxEncodedLen for PhantomType<T> {1148 fn max_encoded_len() -> usize {1149 01150 }1151}11521153/// Bounded vector of bytes.1154pub type BoundedBytes<S> = BoundedVec<u8, S>;11551156/// Extra properties for external collections.1157pub type AuxPropertyValue = BoundedBytes<ConstU32<MAX_AUX_PROPERTY_VALUE_LENGTH>>;11581159/// Property key.1160pub type PropertyKey = BoundedBytes<ConstU32<MAX_PROPERTY_KEY_LENGTH>>;11611162/// Property value.1163pub type PropertyValue = BoundedBytes<ConstU32<MAX_PROPERTY_VALUE_LENGTH>>;11641165/// Property permission.1166#[derive(1167 Encode,1168 Decode,1169 TypeInfo,1170 Debug,1171 MaxEncodedLen,1172 PartialEq,1173 Clone,1174 Default,1175 Serialize,1176 Deserialize,1177)]1178pub struct PropertyPermission {1179 /// Permission to change the property and property permission.1180 ///1181 /// If it **false** then you can not change corresponding property even if [`collection_admin`] and [`token_owner`] are **true**.1182 pub mutable: bool,11831184 /// Change permission for the collection administrator.1185 pub collection_admin: bool,11861187 /// Permission to change the property for the owner of the token.1188 pub token_owner: bool,1189}11901191impl PropertyPermission {1192 /// Creates mutable property permission but changes restricted for collection admin and token owner.1193 pub fn none() -> Self {1194 Self {1195 mutable: true,1196 collection_admin: false,1197 token_owner: false,1198 }1199 }1200}12011202/// Property is simpl key-value record.1203#[derive(1204 Encode, Decode, Debug, TypeInfo, Clone, PartialEq, MaxEncodedLen, Serialize, Deserialize,1205)]1206pub struct Property {1207 /// Property key.1208 #[serde(with = "bounded::vec_serde")]1209 pub key: PropertyKey,12101211 /// Property value.1212 #[serde(with = "bounded::vec_serde")]1213 pub value: PropertyValue,1214}12151216impl From<Property> for (PropertyKey, PropertyValue) {1217 fn from(value: Property) -> Self {1218 (value.key, value.value)1219 }1220}12211222/// Record for proprty key permission.1223#[derive(1224 Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone, Serialize, Deserialize,1225)]1226pub struct PropertyKeyPermission {1227 /// Key.1228 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1229 pub key: PropertyKey,12301231 /// Permission.1232 pub permission: PropertyPermission,1233}12341235impl From<PropertyKeyPermission> for (PropertyKey, PropertyPermission) {1236 fn from(value: PropertyKeyPermission) -> Self {1237 (value.key, value.permission)1238 }1239}12401241/// Errors for properties actions.1242#[derive(Debug)]1243pub enum PropertiesError {1244 /// The space allocated for properties has run out.1245 ///1246 /// * Limit for colection - [`MAX_COLLECTION_PROPERTIES_SIZE`].1247 /// * Limit for token - [`MAX_TOKEN_PROPERTIES_SIZE`].1248 NoSpaceForProperty,12491250 /// The property limit has been reached.1251 ///1252 /// * Limit - [`MAX_PROPERTIES_PER_ITEM`].1253 PropertyLimitReached,12541255 /// Property key contains not allowed character.1256 InvalidCharacterInPropertyKey,12571258 /// Property key length is too long.1259 ///1260 /// * Limit - [`MAX_PROPERTY_KEY_LENGTH`].1261 PropertyKeyIsTooLong,12621263 /// Property key is empty.1264 EmptyPropertyKey,1265}12661267/// Token owner error: it could be either `NotFound` ot `MultipleOwners`.1268#[derive(Debug)]1269pub enum TokenOwnerError {1270 NotFound,1271 MultipleOwners,1272}12731274/// Marker for scope of property.1275///1276/// Scoped property can't be changed by user. Used for external collections.1277#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Clone, Copy)]1278pub enum PropertyScope {1279 None,1280 Rmrk,1281}12821283impl PropertyScope {1284 pub fn prefix(&self) -> &'static [u8] {1285 match self {1286 Self::None => b"",1287 Self::Rmrk => b"rmrk:",1288 }1289 }1290 /// Apply scope to property key.1291 pub fn apply(self, key: PropertyKey) -> Result<PropertyKey, PropertiesError> {1292 let prefix = self.prefix();1293 if prefix == b"" {1294 return Ok(key);1295 }1296 [prefix, key.as_slice()]1297 .concat()1298 .try_into()1299 .map_err(|_| PropertiesError::PropertyKeyIsTooLong)1300 }1301}13021303/// Trait for operate with properties.1304pub trait TrySetProperty: Sized {1305 type Value;13061307 /// Try to set property with scope.1308 fn try_scoped_set(1309 &mut self,1310 scope: PropertyScope,1311 key: PropertyKey,1312 value: Self::Value,1313 ) -> Result<Option<Self::Value>, PropertiesError>;13141315 /// Try to set property with scope from iterator.1316 fn try_scoped_set_from_iter<I, KV>(1317 &mut self,1318 scope: PropertyScope,1319 iter: I,1320 ) -> Result<(), PropertiesError>1321 where1322 I: Iterator<Item = KV>,1323 KV: Into<(PropertyKey, Self::Value)>,1324 {1325 for kv in iter {1326 let (key, value) = kv.into();1327 self.try_scoped_set(scope, key, value)?;1328 }13291330 Ok(())1331 }13321333 /// Try to set property.1334 fn try_set(1335 &mut self,1336 key: PropertyKey,1337 value: Self::Value,1338 ) -> Result<Option<Self::Value>, PropertiesError> {1339 self.try_scoped_set(PropertyScope::None, key, value)1340 }13411342 /// Try to set property from iterator.1343 fn try_set_from_iter<I, KV>(&mut self, iter: I) -> Result<(), PropertiesError>1344 where1345 I: Iterator<Item = KV>,1346 KV: Into<(PropertyKey, Self::Value)>,1347 {1348 self.try_scoped_set_from_iter(PropertyScope::None, iter)1349 }1350}13511352/// Wrapped map for storing properties.1353#[derive(Encode, Decode, TypeInfo, Derivative, Clone, PartialEq, MaxEncodedLen)]1354#[derivative(Default(bound = ""))]1355pub struct PropertiesMap<Value>(1356 BoundedBTreeMap<PropertyKey, Value, ConstU32<MAX_PROPERTIES_PER_ITEM>>,1357);13581359impl<Value> PropertiesMap<Value> {1360 /// Create new property map.1361 pub fn new() -> Self {1362 Self(BoundedBTreeMap::new())1363 }13641365 /// Remove property from map.1366 pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<Value>, PropertiesError> {1367 Self::check_property_key(key)?;13681369 Ok(self.0.remove(key))1370 }13711372 /// Get property with appropriate key from map.1373 pub fn get(&self, key: &PropertyKey) -> Option<&Value> {1374 self.0.get(key)1375 }13761377 /// Check if map contains key.1378 pub fn contains_key(&self, key: &PropertyKey) -> bool {1379 self.0.contains_key(key)1380 }13811382 /// Check if map contains key with key validation.1383 fn check_property_key(key: &PropertyKey) -> Result<(), PropertiesError> {1384 if key.is_empty() {1385 return Err(PropertiesError::EmptyPropertyKey);1386 }13871388 for byte in key.as_slice().iter() {1389 let byte = *byte;13901391 if !byte.is_ascii_alphanumeric() && byte != b'_' && byte != b'-' && byte != b'.' {1392 return Err(PropertiesError::InvalidCharacterInPropertyKey);1393 }1394 }13951396 Ok(())1397 }13981399 pub fn values(&self) -> impl Iterator<Item = &Value> {1400 self.0.values()1401 }14021403 pub fn iter(&self) -> impl Iterator<Item = (&PropertyKey, &Value)> {1404 self.0.iter()1405 }1406}14071408impl<Value> IntoIterator for PropertiesMap<Value> {1409 type Item = (PropertyKey, Value);1410 type IntoIter = <1411 BoundedBTreeMap<1412 PropertyKey,1413 Value,1414 ConstU32<MAX_PROPERTIES_PER_ITEM>1415 > as IntoIterator1416 >::IntoIter;14171418 fn into_iter(self) -> Self::IntoIter {1419 self.0.into_iter()1420 }1421}14221423impl<Value> TrySetProperty for PropertiesMap<Value> {1424 type Value = Value;14251426 fn try_scoped_set(1427 &mut self,1428 scope: PropertyScope,1429 key: PropertyKey,1430 value: Self::Value,1431 ) -> Result<Option<Self::Value>, PropertiesError> {1432 Self::check_property_key(&key)?;14331434 let key = scope.apply(key)?;1435 self.01436 .try_insert(key, value)1437 .map_err(|_| PropertiesError::PropertyLimitReached)1438 }1439}14401441/// Alias for property permissions map.1442pub type PropertiesPermissionMap = PropertiesMap<PropertyPermission>;14431444fn slice_size(data: &[u8]) -> u32 {1445 scoped_slice_size(PropertyScope::None, data)1446}1447fn scoped_slice_size(scope: PropertyScope, data: &[u8]) -> u32 {1448 use parity_scale_codec::Compact;1449 let prefix = scope.prefix();1450 <Compact<u32>>::encoded_size(&Compact(data.len() as u32 + prefix.len() as u32)) as u321451 + data.len() as u321452 + prefix.len() as u321453}14541455/// Wrapper for properties map with consumed space control.1456#[derive(Encode, Decode, TypeInfo, Clone, PartialEq)]1457pub struct Properties<const S: u32> {1458 map: PropertiesMap<PropertyValue>,1459 consumed_space: u32,1460 // May be not zero, previously served as a current S generic1461 _reserved: u32,1462}14631464impl<const S: u32> MaxEncodedLen for Properties<S> {1465 fn max_encoded_len() -> usize {1466 // len of map + len of consumed_space + len of space_limit1467 u32::max_encoded_len() * 3 + S as usize1468 }1469}14701471impl<const S: u32> Default for Properties<S> {1472 fn default() -> Self {1473 Self::new()1474 }1475}14761477impl<const S: u32> Properties<S> {1478 /// Create new properies container.1479 pub fn new() -> Self {1480 Self {1481 map: PropertiesMap::new(),1482 consumed_space: 0,1483 _reserved: 0,1484 }1485 }14861487 /// Remove propery with appropiate key.1488 pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<PropertyValue>, PropertiesError> {1489 let value = self.map.remove(key)?;14901491 if let Some(ref value) = value {1492 let kv_len = slice_size(key) + slice_size(value);1493 self.consumed_space = self.consumed_space.saturating_sub(kv_len);1494 }14951496 Ok(value)1497 }14981499 /// Get property with appropriate key.1500 pub fn get(&self, key: &PropertyKey) -> Option<&PropertyValue> {1501 self.map.get(key)1502 }15031504 /// Recomputes the consumed space for the current properties state.1505 /// Needed to repair a token due to a bug fixed in the [PR #733](https://github.com/UniqueNetwork/unique-chain/pull/773).1506 pub fn recompute_consumed_space(&mut self) {1507 self.consumed_space = self1508 .map1509 .iter()1510 .map(|(key, value)| slice_size(key) + slice_size(value))1511 .sum();1512 }1513}15141515impl<const S: u32> IntoIterator for Properties<S> {1516 type Item = (PropertyKey, PropertyValue);1517 type IntoIter = <PropertiesMap<PropertyValue> as IntoIterator>::IntoIter;15181519 fn into_iter(self) -> Self::IntoIter {1520 self.map.into_iter()1521 }1522}15231524impl<const S: u32> TrySetProperty for Properties<S> {1525 type Value = PropertyValue;15261527 fn try_scoped_set(1528 &mut self,1529 scope: PropertyScope,1530 key: PropertyKey,1531 value: Self::Value,1532 ) -> Result<Option<Self::Value>, PropertiesError> {1533 let key_size = scoped_slice_size(scope, &key);1534 let value_size = slice_size(&value);15351536 if self.consumed_space + value_size + key_size > S && !cfg!(feature = "runtime-benchmarks")1537 {1538 return Err(PropertiesError::NoSpaceForProperty);1539 }15401541 let old_value = self.map.try_scoped_set(scope, key, value)?;15421543 if let Some(old_value) = old_value.as_ref() {1544 let old_value_size = slice_size(old_value);1545 self.consumed_space = self.consumed_space.saturating_sub(old_value_size) + value_size;1546 } else {1547 self.consumed_space += key_size + value_size;1548 }15491550 Ok(old_value)1551 }1552}15531554pub type CollectionProperties = Properties<MAX_COLLECTION_PROPERTIES_SIZE>;1555pub type TokenProperties = Properties<MAX_TOKEN_PROPERTIES_SIZE>;runtime/common/config/orml.rsdiffbeforeafterboth--- a/runtime/common/config/orml.rs
+++ b/runtime/common/config/orml.rs
@@ -14,31 +14,21 @@
// You should have received a copy of the GNU General Public License
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-use frame_support::{
- parameter_types,
- traits::{Contains, Everything},
-};
+use frame_support::{parameter_types, traits::Everything};
use frame_system::EnsureSigned;
use orml_traits::{location::AbsoluteReserveProvider, parameter_type_with_key};
-use pallet_foreign_assets::{CurrencyId, NativeCurrency};
+use pallet_foreign_assets::CurrencyIdConvert;
use sp_runtime::traits::Convert;
-use sp_std::{vec, vec::Vec};
use staging_xcm::latest::{Junction::*, Junctions::*, MultiLocation, Weight};
use staging_xcm_executor::XcmExecutor;
use up_common::{
constants::*,
types::{AccountId, Balance},
};
+use up_data_structs::CollectionId;
use crate::{
- runtime_common::config::{
- pallets::TreasuryAccountId,
- substrate::{MaxLocks, MaxReserves},
- xcm::{
- xcm_assets::CurrencyIdConvert, SelfLocation, UniversalLocation, Weigher,
- XcmExecutorConfig,
- },
- },
+ runtime_common::config::xcm::{SelfLocation, UniversalLocation, Weigher, XcmExecutorConfig},
RelayChainBlockNumberProvider, Runtime, RuntimeEvent,
};
@@ -59,29 +49,6 @@
};
}
-parameter_type_with_key! {
- pub ExistentialDeposits: |currency_id: CurrencyId| -> Balance {
- match currency_id {
- CurrencyId::NativeAssetId(symbol) => match symbol {
- NativeCurrency::Here => 0,
- NativeCurrency::Parent=> 0,
- },
- _ => 100_000
- }
- };
-}
-
-pub fn get_all_module_accounts() -> Vec<AccountId> {
- vec![TreasuryAccountId::get()]
-}
-
-pub struct DustRemovalWhitelist;
-impl Contains<AccountId> for DustRemovalWhitelist {
- fn contains(a: &AccountId) -> bool {
- get_all_module_accounts().contains(a)
- }
-}
-
pub struct AccountIdToMultiLocation;
impl Convert<AccountId, MultiLocation> for AccountIdToMultiLocation {
fn convert(account: AccountId) -> MultiLocation {
@@ -91,18 +58,6 @@
})
.into()
}
-}
-
-pub struct CurrencyHooks;
-impl orml_traits::currency::MutationHooks<AccountId, CurrencyId, Balance> for CurrencyHooks {
- type OnDust = orml_tokens::TransferDust<Runtime, TreasuryAccountId>;
- type OnSlash = ();
- type PreTransfer = ();
- type PostTransfer = ();
- type PreDeposit = ();
- type PostDeposit = ();
- type OnNewTokenAccount = ();
- type OnKilledTokenAccount = ();
}
impl orml_vesting::Config for Runtime {
@@ -113,29 +68,13 @@
type WeightInfo = ();
type MaxVestingSchedules = MaxVestingSchedules;
type BlockNumberProvider = RelayChainBlockNumberProvider<Runtime>;
-}
-
-impl orml_tokens::Config for Runtime {
- type RuntimeEvent = RuntimeEvent;
- type Balance = Balance;
- type Amount = Amount;
- type CurrencyId = CurrencyId;
- type WeightInfo = ();
- type ExistentialDeposits = ExistentialDeposits;
- type CurrencyHooks = CurrencyHooks;
- type MaxLocks = MaxLocks;
- type MaxReserves = MaxReserves;
- // TODO: Add all module accounts
- type DustRemovalWhitelist = DustRemovalWhitelist;
- /// The id type for named reserves.
- type ReserveIdentifier = ();
}
impl orml_xtokens::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type Balance = Balance;
- type CurrencyId = CurrencyId;
- type CurrencyIdConvert = CurrencyIdConvert;
+ type CurrencyId = CollectionId;
+ type CurrencyIdConvert = CurrencyIdConvert<Self>;
type AccountIdToMultiLocation = AccountIdToMultiLocation;
type SelfLocation = SelfLocation;
type XcmExecutor = XcmExecutor<XcmExecutorConfig<Self>>;
runtime/common/config/pallets/foreign_asset.rsdiffbeforeafterboth--- a/runtime/common/config/pallets/foreign_asset.rs
+++ b/runtime/common/config/pallets/foreign_asset.rs
@@ -1,10 +1,52 @@
-use up_common::types::AccountId;
+use frame_support::{parameter_types, PalletId};
+#[cfg(not(feature = "governance"))]
+use frame_system::EnsureRoot;
+use pallet_evm::account::CrossAccountId;
+use sp_core::H160;
+use staging_xcm::prelude::*;
+use staging_xcm_builder::AccountKey20Aliases;
+
+#[cfg(feature = "governance")]
+use crate::runtime_common::config::governance;
+use crate::{
+ runtime_common::config::{
+ ethereum::CrossAccountId as ConfigCrossAccountId,
+ xcm::{LocationToAccountId, SelfLocation},
+ },
+ RelayNetwork, Runtime, RuntimeEvent,
+};
+
+parameter_types! {
+ pub ForeignAssetPalletId: PalletId = PalletId(*b"frgnasts");
+}
+
+pub struct LocationToCrossAccountId;
+impl staging_xcm_executor::traits::ConvertLocation<ConfigCrossAccountId>
+ for LocationToCrossAccountId
+{
+ fn convert_location(location: &MultiLocation) -> Option<ConfigCrossAccountId> {
+ LocationToAccountId::convert_location(location)
+ .map(ConfigCrossAccountId::from_sub)
+ .or_else(|| {
+ let eth_address =
+ AccountKey20Aliases::<RelayNetwork, H160>::convert_location(location)?;
-use crate::{Balances, Runtime, RuntimeEvent};
+ Some(ConfigCrossAccountId::from_eth(eth_address))
+ })
+ }
+}
impl pallet_foreign_assets::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
- type Currency = Balances;
- type RegisterOrigin = frame_system::EnsureRoot<AccountId>;
+
+ #[cfg(feature = "governance")]
+ type ForceRegisterOrigin = governance::RootOrTechnicalCommitteeMember;
+
+ #[cfg(not(feature = "governance"))]
+ type ForceRegisterOrigin = EnsureRoot<Self::AccountId>;
+
+ type PalletId = ForeignAssetPalletId;
+ type SelfLocation = SelfLocation;
+ type LocationToAccountId = LocationToCrossAccountId;
type WeightInfo = pallet_foreign_assets::weights::SubstrateWeight<Self>;
}
runtime/common/config/xcm.rsdiffbeforeafterboth--- /dev/null
+++ b/runtime/common/config/xcm.rs
@@ -0,0 +1,254 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+use cumulus_primitives_core::ParaId;
+use frame_support::{
+ parameter_types,
+ traits::{ConstU32, Everything, Get, Nothing, ProcessMessageError},
+};
+use frame_system::EnsureRoot;
+use orml_traits::location::AbsoluteReserveProvider;
+use orml_xcm_support::MultiNativeAsset;
+use pallet_foreign_assets::FreeForAll;
+use pallet_xcm::XcmPassthrough;
+use polkadot_parachain_primitives::primitives::Sibling;
+use polkadot_runtime_common::xcm_sender::NoPriceForMessageDelivery;
+use sp_std::marker::PhantomData;
+use staging_xcm::{
+ latest::{prelude::*, MultiLocation, Weight},
+ v3::Instruction,
+};
+use staging_xcm_builder::{
+ AccountId32Aliases, EnsureXcmOrigin, FixedWeightBounds, ParentIsPreset, RelayChainAsNative,
+ SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative,
+ SignedToAccountId32, SovereignSignedViaLocation,
+};
+use staging_xcm_executor::{
+ traits::{Properties, ShouldExecute},
+ XcmExecutor,
+};
+use up_common::types::AccountId;
+
+#[cfg(feature = "governance")]
+use crate::runtime_common::config::governance;
+use crate::{
+ xcm_barrier::Barrier, AllPalletsWithSystem, Balances, ForeignAssets, ParachainInfo,
+ ParachainSystem, PolkadotXcm, RelayNetwork, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin,
+ XcmpQueue,
+};
+
+parameter_types! {
+ pub const RelayLocation: MultiLocation = MultiLocation::parent();
+ pub RelayOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into();
+ pub UniversalLocation: InteriorMultiLocation = (
+ GlobalConsensus(crate::RelayNetwork::get()),
+ Parachain(ParachainInfo::get().into()),
+ ).into();
+ pub SelfLocation: MultiLocation = MultiLocation::new(1, X1(Parachain(ParachainInfo::get().into())));
+
+ // One XCM operation is 1_000_000 weight - almost certainly a conservative estimate.
+ pub UnitWeightCost: Weight = Weight::from_parts(1_000_000, 1000); // ?
+ pub const MaxInstructions: u32 = 100;
+}
+
+/// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used
+/// when determining ownership of accounts for asset transacting and when attempting to use XCM
+/// `Transact` in order to determine the dispatch Origin.
+pub type LocationToAccountId = (
+ // The parent (Relay-chain) origin converts to the default `AccountId`.
+ ParentIsPreset<AccountId>,
+ // Sibling parachain origins convert to AccountId via the `ParaId::into`.
+ SiblingParachainConvertsVia<Sibling, AccountId>,
+ // Straight up local `AccountId32` origins just alias directly to `AccountId`.
+ AccountId32Aliases<RelayNetwork, AccountId>,
+);
+
+/// No local origins on this chain are allowed to dispatch XCM sends/executions.
+pub type LocalOriginToLocation = (SignedToAccountId32<RuntimeOrigin, AccountId, RelayNetwork>,);
+
+/// The means for routing XCM messages which are not for local execution into the right message
+/// queues.
+pub type XcmRouter = (
+ // Two routers - use UMP to communicate with the relay chain:
+ cumulus_primitives_utility::ParentAsUmp<ParachainSystem, PolkadotXcm, ()>,
+ // ..and XCMP to communicate with the sibling chains.
+ XcmpQueue,
+);
+
+/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance,
+/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can
+/// biases the kind of local `Origin` it will become.
+pub type XcmOriginToTransactDispatchOrigin = (
+ // Sovereign account converter; this attempts to derive an `AccountId` from the origin location
+ // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for
+ // foreign chains who want to have a local sovereign account on this chain which they control.
+ SovereignSignedViaLocation<LocationToAccountId, RuntimeOrigin>,
+ // Native converter for Relay-chain (Parent) location; will converts to a `Relay` origin when
+ // recognised.
+ RelayChainAsNative<RelayOrigin, RuntimeOrigin>,
+ // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when
+ // recognised.
+ SiblingParachainAsNative<cumulus_pallet_xcm::Origin, RuntimeOrigin>,
+ // Native signed account converter; this just converts an `AccountId32` origin into a normal
+ // `Origin::Signed` origin of the same 32-byte value.
+ SignedAccountId32AsNative<RelayNetwork, RuntimeOrigin>,
+ // Xcm origins can be represented natively under the Xcm pallet's Xcm origin.
+ XcmPassthrough<RuntimeOrigin>,
+);
+
+pub trait TryPass {
+ fn try_pass<Call>(
+ origin: &MultiLocation,
+ message: &mut [Instruction<Call>],
+ ) -> Result<(), ProcessMessageError>;
+}
+
+#[impl_trait_for_tuples::impl_for_tuples(30)]
+impl TryPass for Tuple {
+ fn try_pass<Call>(
+ origin: &MultiLocation,
+ message: &mut [Instruction<Call>],
+ ) -> Result<(), ProcessMessageError> {
+ for_tuples!( #(
+ Tuple::try_pass(origin, message)?;
+ )* );
+
+ Ok(())
+ }
+}
+
+/// Deny executing the XCM if it matches any of the Deny filter regardless of anything else.
+/// If it passes the Deny, and matches one of the Allow cases then it is let through.
+pub struct DenyThenTry<Deny, Allow>(PhantomData<Deny>, PhantomData<Allow>)
+where
+ Deny: TryPass,
+ Allow: ShouldExecute;
+
+impl<Deny, Allow> ShouldExecute for DenyThenTry<Deny, Allow>
+where
+ Deny: TryPass,
+ Allow: ShouldExecute,
+{
+ fn should_execute<Call>(
+ origin: &MultiLocation,
+ message: &mut [Instruction<Call>],
+ max_weight: Weight,
+ properties: &mut Properties,
+ ) -> Result<(), ProcessMessageError> {
+ Deny::try_pass(origin, message)?;
+ Allow::should_execute(origin, message, max_weight, properties)
+ }
+}
+
+pub type Weigher = FixedWeightBounds<UnitWeightCost, RuntimeCall, MaxInstructions>;
+
+pub type IsReserve = MultiNativeAsset<AbsoluteReserveProvider>;
+
+pub type Trader = FreeForAll;
+
+pub struct XcmExecutorConfig<T>(PhantomData<T>);
+impl<T> staging_xcm_executor::Config for XcmExecutorConfig<T>
+where
+ T: pallet_configuration::Config,
+{
+ type RuntimeCall = RuntimeCall;
+ type XcmSender = XcmRouter;
+ // How to withdraw and deposit an asset.
+ type AssetTransactor = ForeignAssets;
+ type OriginConverter = XcmOriginToTransactDispatchOrigin;
+ type IsReserve = IsReserve;
+ type IsTeleporter = (); // Teleportation is disabled
+ type UniversalLocation = UniversalLocation;
+ type Barrier = Barrier;
+ type Weigher = Weigher;
+ type Trader = Trader;
+ type ResponseHandler = PolkadotXcm;
+ type SubscriptionService = PolkadotXcm;
+ type PalletInstancesInfo = AllPalletsWithSystem;
+ type MaxAssetsIntoHolding = ConstU32<8>;
+
+ type AssetTrap = PolkadotXcm;
+ type AssetClaims = PolkadotXcm;
+ type AssetLocker = ();
+ type AssetExchanger = ();
+ type FeeManager = ();
+ type MessageExporter = ();
+ type UniversalAliases = Nothing;
+ type CallDispatcher = RuntimeCall;
+ type SafeCallFilter = Nothing;
+ type Aliasers = Nothing;
+}
+
+#[cfg(feature = "runtime-benchmarks")]
+parameter_types! {
+ pub ReachableDest: Option<MultiLocation> = Some(Parent.into());
+}
+
+impl pallet_xcm::Config for Runtime {
+ type RuntimeEvent = RuntimeEvent;
+ type SendXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, ()>;
+ type XcmRouter = XcmRouter;
+ type ExecuteXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
+ type XcmExecuteFilter = Everything;
+ type XcmExecutor = XcmExecutor<XcmExecutorConfig<Self>>;
+ type XcmTeleportFilter = Everything;
+ type XcmReserveTransferFilter = Everything;
+ type Weigher = FixedWeightBounds<UnitWeightCost, RuntimeCall, MaxInstructions>;
+ type RuntimeOrigin = RuntimeOrigin;
+ type RuntimeCall = RuntimeCall;
+ const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
+ type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion;
+ type UniversalLocation = UniversalLocation;
+ type Currency = Balances;
+ type CurrencyMatcher = ();
+ type TrustedLockers = ();
+ type SovereignAccountOf = LocationToAccountId;
+ type MaxLockers = ConstU32<8>;
+ type WeightInfo = crate::weights::xcm::SubstrateWeight<Runtime>;
+ type AdminOrigin = EnsureRoot<AccountId>;
+ type MaxRemoteLockConsumers = ConstU32<0>;
+ type RemoteLockConsumerIdentifier = ();
+ #[cfg(feature = "runtime-benchmarks")]
+ type ReachableDest = ReachableDest;
+}
+
+impl cumulus_pallet_xcm::Config for Runtime {
+ type RuntimeEvent = RuntimeEvent;
+ type XcmExecutor = XcmExecutor<XcmExecutorConfig<Self>>;
+}
+impl cumulus_pallet_xcmp_queue::Config for Runtime {
+ type WeightInfo = ();
+ type RuntimeEvent = RuntimeEvent;
+ type XcmExecutor = XcmExecutor<XcmExecutorConfig<Self>>;
+ type ChannelInfo = ParachainSystem;
+ type VersionWrapper = PolkadotXcm;
+ type ExecuteOverweightOrigin = frame_system::EnsureRoot<AccountId>;
+
+ #[cfg(feature = "governance")]
+ type ControllerOrigin = governance::RootOrTechnicalCommitteeMember;
+
+ #[cfg(not(feature = "governance"))]
+ type ControllerOrigin = frame_system::EnsureRoot<AccountId>;
+
+ type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin;
+ type PriceForSiblingDelivery = NoPriceForMessageDelivery<ParaId>;
+}
+
+impl cumulus_pallet_dmp_queue::Config for Runtime {
+ type RuntimeEvent = RuntimeEvent;
+ type XcmExecutor = XcmExecutor<XcmExecutorConfig<Self>>;
+ type ExecuteOverweightOrigin = frame_system::EnsureRoot<AccountId>;
+}
runtime/common/config/xcm/foreignassets.rsdiffbeforeafterboth--- a/runtime/common/config/xcm/foreignassets.rs
+++ /dev/null
@@ -1,205 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-use frame_support::{parameter_types, traits::Get};
-use orml_traits::location::AbsoluteReserveProvider;
-use orml_xcm_support::MultiNativeAsset;
-use pallet_foreign_assets::{
- AssetId, AssetIdMapping, CurrencyId, ForeignAssetId, FreeForAll, NativeCurrency, TryAsForeign,
- XcmForeignAssetIdMapping,
-};
-use sp_runtime::traits::{Convert, MaybeEquivalence};
-use sp_std::marker::PhantomData;
-use staging_xcm::latest::{prelude::*, MultiAsset, MultiLocation};
-use staging_xcm_builder::{ConvertedConcreteId, FungiblesAdapter, NoChecking};
-use staging_xcm_executor::traits::{JustTry, TransactAsset};
-use up_common::types::{AccountId, Balance};
-
-use super::{LocationToAccountId, RelayLocation};
-use crate::{Balances, ForeignAssets, ParachainInfo, PolkadotXcm, Runtime};
-
-parameter_types! {
- pub CheckingAccount: AccountId = PolkadotXcm::check_account();
-}
-
-pub struct AsInnerId<ConvertAssetId>(PhantomData<(AssetId, ConvertAssetId)>);
-impl<ConvertAssetId: MaybeEquivalence<AssetId, AssetId>> MaybeEquivalence<MultiLocation, AssetId>
- for AsInnerId<ConvertAssetId>
-{
- fn convert(id: &MultiLocation) -> Option<AssetId> {
- log::trace!(
- target: "xcm::AsInnerId::Convert",
- "AsInnerId {:?}",
- id
- );
-
- let parent = MultiLocation::parent();
- let here = MultiLocation::here();
- let self_location = MultiLocation::new(1, X1(Parachain(ParachainInfo::get().into())));
-
- if *id == parent {
- return ConvertAssetId::convert(&AssetId::NativeAssetId(NativeCurrency::Parent));
- }
-
- if *id == here || *id == self_location {
- return ConvertAssetId::convert(&AssetId::NativeAssetId(NativeCurrency::Here));
- }
-
- match XcmForeignAssetIdMapping::<Runtime>::get_currency_id(*id) {
- Some(AssetId::ForeignAssetId(foreign_asset_id)) => {
- ConvertAssetId::convert(&AssetId::ForeignAssetId(foreign_asset_id))
- }
- _ => None,
- }
- }
-
- fn convert_back(asset_id: &AssetId) -> Option<MultiLocation> {
- log::trace!(
- target: "xcm::AsInnerId::Reverse",
- "AsInnerId",
- );
-
- let parent_id =
- ConvertAssetId::convert(&AssetId::NativeAssetId(NativeCurrency::Parent)).unwrap();
- let here_id =
- ConvertAssetId::convert(&AssetId::NativeAssetId(NativeCurrency::Here)).unwrap();
-
- if *asset_id == parent_id {
- return Some(MultiLocation::parent());
- }
-
- if *asset_id == here_id {
- return Some(MultiLocation::new(
- 1,
- X1(Parachain(ParachainInfo::get().into())),
- ));
- }
-
- let fid = <AssetId as TryAsForeign<AssetId, ForeignAssetId>>::try_as_foreign(*asset_id)?;
- XcmForeignAssetIdMapping::<Runtime>::get_multi_location(fid)
- }
-}
-
-/// Means for transacting assets besides the native currency on this chain.
-pub type FungiblesTransactor = FungiblesAdapter<
- // Use this fungibles implementation:
- ForeignAssets,
- // Use this currency when it is a fungible asset matching the given location or name:
- ConvertedConcreteId<AssetId, Balance, AsInnerId<JustTry>, JustTry>,
- // Convert an XCM MultiLocation into a local account id:
- LocationToAccountId,
- // Our chain's account ID type (we can't get away without mentioning it explicitly):
- AccountId,
- // No Checking for teleported assets since we disallow teleports at all.
- NoChecking,
- // The account to use for tracking teleports.
- CheckingAccount,
->;
-
-/// Means for transacting assets on this chain.
-pub struct AssetTransactor;
-impl TransactAsset for AssetTransactor {
- fn can_check_in(
- _origin: &MultiLocation,
- _what: &MultiAsset,
- _context: &XcmContext,
- ) -> XcmResult {
- Err(XcmError::Unimplemented)
- }
-
- fn check_in(_origin: &MultiLocation, _what: &MultiAsset, _context: &XcmContext) {}
-
- fn can_check_out(
- _dest: &MultiLocation,
- _what: &MultiAsset,
- _context: &XcmContext,
- ) -> XcmResult {
- Err(XcmError::Unimplemented)
- }
-
- fn check_out(_dest: &MultiLocation, _what: &MultiAsset, _context: &XcmContext) {}
-
- fn deposit_asset(
- what: &MultiAsset,
- who: &MultiLocation,
- context: Option<&XcmContext>,
- ) -> XcmResult {
- FungiblesTransactor::deposit_asset(what, who, context)
- }
-
- fn withdraw_asset(
- what: &MultiAsset,
- who: &MultiLocation,
- maybe_context: Option<&XcmContext>,
- ) -> Result<staging_xcm_executor::Assets, XcmError> {
- FungiblesTransactor::withdraw_asset(what, who, maybe_context)
- }
-
- fn internal_transfer_asset(
- what: &MultiAsset,
- from: &MultiLocation,
- to: &MultiLocation,
- context: &XcmContext,
- ) -> Result<staging_xcm_executor::Assets, XcmError> {
- FungiblesTransactor::internal_transfer_asset(what, from, to, context)
- }
-}
-
-pub type IsReserve = MultiNativeAsset<AbsoluteReserveProvider>;
-
-pub type Trader<T> = FreeForAll<
- pallet_configuration::WeightToFee<T, Balance>,
- RelayLocation,
- AccountId,
- Balances,
- (),
->;
-
-pub struct CurrencyIdConvert;
-impl Convert<AssetId, Option<MultiLocation>> for CurrencyIdConvert {
- fn convert(id: AssetId) -> Option<MultiLocation> {
- match id {
- AssetId::NativeAssetId(NativeCurrency::Here) => Some(MultiLocation::new(
- 1,
- X1(Parachain(ParachainInfo::get().into())),
- )),
- AssetId::NativeAssetId(NativeCurrency::Parent) => Some(MultiLocation::parent()),
- AssetId::ForeignAssetId(foreign_asset_id) => {
- XcmForeignAssetIdMapping::<Runtime>::get_multi_location(foreign_asset_id)
- }
- }
- }
-}
-
-impl Convert<MultiLocation, Option<CurrencyId>> for CurrencyIdConvert {
- fn convert(location: MultiLocation) -> Option<CurrencyId> {
- if location == MultiLocation::here()
- || location == MultiLocation::new(1, X1(Parachain(ParachainInfo::get().into())))
- {
- return Some(AssetId::NativeAssetId(NativeCurrency::Here));
- }
-
- if location == MultiLocation::parent() {
- return Some(AssetId::NativeAssetId(NativeCurrency::Parent));
- }
-
- if let Some(currency_id) = XcmForeignAssetIdMapping::<Runtime>::get_currency_id(location) {
- return Some(currency_id);
- }
-
- None
- }
-}
runtime/common/config/xcm/mod.rsdiffbeforeafterboth--- a/runtime/common/config/xcm/mod.rs
+++ /dev/null
@@ -1,259 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-use cumulus_primitives_core::ParaId;
-use frame_support::{
- parameter_types,
- traits::{ConstU32, Everything, Get, Nothing, ProcessMessageError},
-};
-use frame_system::EnsureRoot;
-use pallet_xcm::XcmPassthrough;
-use polkadot_parachain_primitives::primitives::Sibling;
-use polkadot_runtime_common::xcm_sender::NoPriceForMessageDelivery;
-use sp_std::marker::PhantomData;
-use staging_xcm::{
- latest::{prelude::*, MultiLocation, Weight},
- v3::Instruction,
-};
-use staging_xcm_builder::{
- AccountId32Aliases, EnsureXcmOrigin, FixedWeightBounds, ParentIsPreset, RelayChainAsNative,
- SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative,
- SignedToAccountId32, SovereignSignedViaLocation,
-};
-use staging_xcm_executor::{
- traits::{Properties, ShouldExecute},
- XcmExecutor,
-};
-use up_common::types::AccountId;
-
-use crate::{
- xcm_barrier::Barrier, AllPalletsWithSystem, Balances, ParachainInfo, ParachainSystem,
- PolkadotXcm, RelayNetwork, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, XcmpQueue,
-};
-
-#[cfg(feature = "foreign-assets")]
-pub mod foreignassets;
-
-#[cfg(not(feature = "foreign-assets"))]
-pub mod nativeassets;
-
-#[cfg(feature = "foreign-assets")]
-pub use foreignassets as xcm_assets;
-#[cfg(not(feature = "foreign-assets"))]
-pub use nativeassets as xcm_assets;
-use xcm_assets::{AssetTransactor, IsReserve, Trader};
-
-#[cfg(feature = "governance")]
-use crate::runtime_common::config::governance;
-
-parameter_types! {
- pub const RelayLocation: MultiLocation = MultiLocation::parent();
- pub RelayOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into();
- pub UniversalLocation: InteriorMultiLocation = (
- GlobalConsensus(crate::RelayNetwork::get()),
- Parachain(ParachainInfo::get().into()),
- ).into();
- pub SelfLocation: MultiLocation = MultiLocation::new(1, X1(Parachain(ParachainInfo::get().into())));
-
- // One XCM operation is 1_000_000 weight - almost certainly a conservative estimate.
- pub UnitWeightCost: Weight = Weight::from_parts(1_000_000, 1000); // ?
- pub const MaxInstructions: u32 = 100;
-}
-
-/// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used
-/// when determining ownership of accounts for asset transacting and when attempting to use XCM
-/// `Transact` in order to determine the dispatch Origin.
-pub type LocationToAccountId = (
- // The parent (Relay-chain) origin converts to the default `AccountId`.
- ParentIsPreset<AccountId>,
- // Sibling parachain origins convert to AccountId via the `ParaId::into`.
- SiblingParachainConvertsVia<Sibling, AccountId>,
- // Straight up local `AccountId32` origins just alias directly to `AccountId`.
- AccountId32Aliases<RelayNetwork, AccountId>,
-);
-
-/// No local origins on this chain are allowed to dispatch XCM sends/executions.
-pub type LocalOriginToLocation = (SignedToAccountId32<RuntimeOrigin, AccountId, RelayNetwork>,);
-
-/// The means for routing XCM messages which are not for local execution into the right message
-/// queues.
-pub type XcmRouter = (
- // Two routers - use UMP to communicate with the relay chain:
- cumulus_primitives_utility::ParentAsUmp<ParachainSystem, PolkadotXcm, ()>,
- // ..and XCMP to communicate with the sibling chains.
- XcmpQueue,
-);
-
-/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance,
-/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can
-/// biases the kind of local `Origin` it will become.
-pub type XcmOriginToTransactDispatchOrigin = (
- // Sovereign account converter; this attempts to derive an `AccountId` from the origin location
- // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for
- // foreign chains who want to have a local sovereign account on this chain which they control.
- SovereignSignedViaLocation<LocationToAccountId, RuntimeOrigin>,
- // Native converter for Relay-chain (Parent) location; will converts to a `Relay` origin when
- // recognised.
- RelayChainAsNative<RelayOrigin, RuntimeOrigin>,
- // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when
- // recognised.
- SiblingParachainAsNative<cumulus_pallet_xcm::Origin, RuntimeOrigin>,
- // Native signed account converter; this just converts an `AccountId32` origin into a normal
- // `Origin::Signed` origin of the same 32-byte value.
- SignedAccountId32AsNative<RelayNetwork, RuntimeOrigin>,
- // Xcm origins can be represented natively under the Xcm pallet's Xcm origin.
- XcmPassthrough<RuntimeOrigin>,
-);
-
-pub trait TryPass {
- fn try_pass<Call>(
- origin: &MultiLocation,
- message: &mut [Instruction<Call>],
- ) -> Result<(), ProcessMessageError>;
-}
-
-#[impl_trait_for_tuples::impl_for_tuples(30)]
-impl TryPass for Tuple {
- fn try_pass<Call>(
- origin: &MultiLocation,
- message: &mut [Instruction<Call>],
- ) -> Result<(), ProcessMessageError> {
- for_tuples!( #(
- Tuple::try_pass(origin, message)?;
- )* );
-
- Ok(())
- }
-}
-
-/// Deny executing the XCM if it matches any of the Deny filter regardless of anything else.
-/// If it passes the Deny, and matches one of the Allow cases then it is let through.
-pub struct DenyThenTry<Deny, Allow>(PhantomData<Deny>, PhantomData<Allow>)
-where
- Deny: TryPass,
- Allow: ShouldExecute;
-
-impl<Deny, Allow> ShouldExecute for DenyThenTry<Deny, Allow>
-where
- Deny: TryPass,
- Allow: ShouldExecute,
-{
- fn should_execute<Call>(
- origin: &MultiLocation,
- message: &mut [Instruction<Call>],
- max_weight: Weight,
- properties: &mut Properties,
- ) -> Result<(), ProcessMessageError> {
- Deny::try_pass(origin, message)?;
- Allow::should_execute(origin, message, max_weight, properties)
- }
-}
-
-pub type Weigher = FixedWeightBounds<UnitWeightCost, RuntimeCall, MaxInstructions>;
-
-pub struct XcmExecutorConfig<T>(PhantomData<T>);
-impl<T> staging_xcm_executor::Config for XcmExecutorConfig<T>
-where
- T: pallet_configuration::Config,
-{
- type RuntimeCall = RuntimeCall;
- type XcmSender = XcmRouter;
- // How to withdraw and deposit an asset.
- type AssetTransactor = AssetTransactor;
- type OriginConverter = XcmOriginToTransactDispatchOrigin;
- type IsReserve = IsReserve;
- type IsTeleporter = (); // Teleportation is disabled
- type UniversalLocation = UniversalLocation;
- type Barrier = Barrier;
- type Weigher = Weigher;
- type Trader = Trader<T>;
- type ResponseHandler = PolkadotXcm;
- type SubscriptionService = PolkadotXcm;
- type PalletInstancesInfo = AllPalletsWithSystem;
- type MaxAssetsIntoHolding = ConstU32<8>;
-
- type AssetTrap = PolkadotXcm;
- type AssetClaims = PolkadotXcm;
- type AssetLocker = ();
- type AssetExchanger = ();
- type FeeManager = ();
- type MessageExporter = ();
- type UniversalAliases = Nothing;
- type CallDispatcher = RuntimeCall;
- type SafeCallFilter = Nothing;
- type Aliasers = Nothing;
-}
-
-#[cfg(feature = "runtime-benchmarks")]
-parameter_types! {
- pub ReachableDest: Option<MultiLocation> = Some(Parent.into());
-}
-
-impl pallet_xcm::Config for Runtime {
- type RuntimeEvent = RuntimeEvent;
- type SendXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, ()>;
- type XcmRouter = XcmRouter;
- type ExecuteXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
- type XcmExecuteFilter = Everything;
- type XcmExecutor = XcmExecutor<XcmExecutorConfig<Self>>;
- type XcmTeleportFilter = Everything;
- type XcmReserveTransferFilter = Everything;
- type Weigher = FixedWeightBounds<UnitWeightCost, RuntimeCall, MaxInstructions>;
- type RuntimeOrigin = RuntimeOrigin;
- type RuntimeCall = RuntimeCall;
- const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
- type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion;
- type UniversalLocation = UniversalLocation;
- type Currency = Balances;
- type CurrencyMatcher = ();
- type TrustedLockers = ();
- type SovereignAccountOf = LocationToAccountId;
- type MaxLockers = ConstU32<8>;
- type WeightInfo = crate::weights::xcm::SubstrateWeight<Runtime>;
- type AdminOrigin = EnsureRoot<AccountId>;
- type MaxRemoteLockConsumers = ConstU32<0>;
- type RemoteLockConsumerIdentifier = ();
- #[cfg(feature = "runtime-benchmarks")]
- type ReachableDest = ReachableDest;
-}
-
-impl cumulus_pallet_xcm::Config for Runtime {
- type RuntimeEvent = RuntimeEvent;
- type XcmExecutor = XcmExecutor<XcmExecutorConfig<Self>>;
-}
-impl cumulus_pallet_xcmp_queue::Config for Runtime {
- type WeightInfo = ();
- type RuntimeEvent = RuntimeEvent;
- type XcmExecutor = XcmExecutor<XcmExecutorConfig<Self>>;
- type ChannelInfo = ParachainSystem;
- type VersionWrapper = PolkadotXcm;
- type ExecuteOverweightOrigin = frame_system::EnsureRoot<AccountId>;
-
- #[cfg(feature = "governance")]
- type ControllerOrigin = governance::RootOrTechnicalCommitteeMember;
-
- #[cfg(not(feature = "governance"))]
- type ControllerOrigin = frame_system::EnsureRoot<AccountId>;
-
- type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin;
- type PriceForSiblingDelivery = NoPriceForMessageDelivery<ParaId>;
-}
-
-impl cumulus_pallet_dmp_queue::Config for Runtime {
- type RuntimeEvent = RuntimeEvent;
- type XcmExecutor = XcmExecutor<XcmExecutorConfig<Self>>;
- type ExecuteOverweightOrigin = frame_system::EnsureRoot<AccountId>;
-}
runtime/common/config/xcm/nativeassets.rsdiffbeforeafterboth--- a/runtime/common/config/xcm/nativeassets.rs
+++ /dev/null
@@ -1,148 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-use cumulus_primitives_core::XcmContext;
-use frame_support::{
- traits::{tokens::currency::Currency as CurrencyT, Get, OnUnbalanced as OnUnbalancedT},
- weights::WeightToFeePolynomial,
-};
-use pallet_foreign_assets::{AssetIds, NativeCurrency};
-use sp_runtime::traits::{CheckedConversion, Convert, Zero};
-use sp_std::marker::PhantomData;
-use staging_xcm::latest::{
- AssetId::Concrete, Error as XcmError, Fungibility::Fungible as XcmFungible, Junction::*,
- Junctions::*, MultiAsset, MultiLocation, Weight,
-};
-use staging_xcm_builder::{CurrencyAdapter, NativeAsset};
-use staging_xcm_executor::{
- traits::{MatchesFungible, WeightTrader},
- Assets,
-};
-use up_common::types::{AccountId, Balance};
-
-use super::{LocationToAccountId, RelayLocation};
-use crate::{Balances, ParachainInfo};
-
-pub struct OnlySelfCurrency;
-impl<B: TryFrom<u128>> MatchesFungible<B> for OnlySelfCurrency {
- fn matches_fungible(a: &MultiAsset) -> Option<B> {
- let paraid = Parachain(ParachainInfo::parachain_id().into());
- match (&a.id, &a.fun) {
- (
- Concrete(MultiLocation {
- parents: 1,
- interior: X1(loc),
- }),
- XcmFungible(ref amount),
- ) if paraid == *loc => CheckedConversion::checked_from(*amount),
- (
- Concrete(MultiLocation {
- parents: 0,
- interior: Here,
- }),
- XcmFungible(ref amount),
- ) => CheckedConversion::checked_from(*amount),
- _ => None,
- }
- }
-}
-
-/// Means for transacting assets on this chain.
-pub type LocalAssetTransactor = CurrencyAdapter<
- // Use this currency:
- Balances,
- // Use this currency when it is a fungible asset matching the given location or name:
- OnlySelfCurrency,
- // Do a simple punn to convert an AccountId32 MultiLocation into a native chain account ID:
- LocationToAccountId,
- // Our chain's account ID type (we can't get away without mentioning it explicitly):
- AccountId,
- // We don't track any teleports.
- (),
->;
-
-pub type AssetTransactor = LocalAssetTransactor;
-
-pub type IsReserve = NativeAsset;
-
-pub struct UsingOnlySelfCurrencyComponents<
- WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,
- AssetId: Get<MultiLocation>,
- AccountId,
- Currency: CurrencyT<AccountId>,
- OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,
->(
- Weight,
- Currency::Balance,
- PhantomData<(WeightToFee, AssetId, AccountId, Currency, OnUnbalanced)>,
-);
-impl<
- WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,
- AssetId: Get<MultiLocation>,
- AccountId,
- Currency: CurrencyT<AccountId>,
- OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,
- > WeightTrader
- for UsingOnlySelfCurrencyComponents<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>
-{
- fn new() -> Self {
- // FIXME: benchmark
- Self(Weight::from_parts(0, 0), Zero::zero(), PhantomData)
- }
-
- fn buy_weight(
- &mut self,
- _weight: Weight,
- payment: Assets,
- _xcm: &XcmContext,
- ) -> Result<Assets, XcmError> {
- Ok(payment)
- }
-}
-impl<
- WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,
- AssetId: Get<MultiLocation>,
- AccountId,
- Currency: CurrencyT<AccountId>,
- OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,
- > Drop
- for UsingOnlySelfCurrencyComponents<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>
-{
- fn drop(&mut self) {
- OnUnbalanced::on_unbalanced(Currency::issue(self.1));
- }
-}
-
-pub type Trader<T> = UsingOnlySelfCurrencyComponents<
- pallet_configuration::WeightToFee<T, Balance>,
- RelayLocation,
- AccountId,
- Balances,
- (),
->;
-
-pub struct CurrencyIdConvert;
-impl Convert<AssetIds, Option<MultiLocation>> for CurrencyIdConvert {
- fn convert(id: AssetIds) -> Option<MultiLocation> {
- match id {
- AssetIds::NativeAssetId(NativeCurrency::Here) => Some(MultiLocation::new(
- 1,
- X1(Parachain(ParachainInfo::get().into())),
- )),
- _ => None,
- }
- }
-}
runtime/common/construct_runtime.rsdiffbeforeafterboth--- a/runtime/common/construct_runtime.rs
+++ b/runtime/common/construct_runtime.rs
@@ -47,7 +47,7 @@
Vesting: orml_vesting = 37,
XTokens: orml_xtokens = 38,
- Tokens: orml_tokens = 39,
+ // [REMOVED] Tokens: orml_tokens = 39,
// Contracts: pallet_contracts::{Pallet, Call, Storage, Event<T>} = 38,
#[cfg(feature = "governance")]
runtime/common/dispatch.rsdiffbeforeafterboth--- a/runtime/common/dispatch.rs
+++ b/runtime/common/dispatch.rs
@@ -17,11 +17,9 @@
use frame_support::{dispatch::DispatchResult, ensure, fail};
use pallet_balances_adapter::NativeFungibleHandle;
pub use pallet_common::dispatch::CollectionDispatch;
-#[cfg(not(feature = "refungible"))]
-use pallet_common::unsupported;
use pallet_common::{
- erc::CommonEvmHandler, eth::map_eth_to_id, CollectionById, CollectionHandle,
- CommonCollectionOperations,
+ erc::CommonEvmHandler, eth::map_eth_to_id, CollectionById, CollectionHandle, CollectionIssuer,
+ CommonCollectionOperations, Pallet as PalletCommon,
};
use pallet_evm::{PrecompileHandle, PrecompileResult};
use pallet_fungible::{FungibleHandle, Pallet as PalletFungible};
@@ -70,27 +68,25 @@
fn create(
sender: T::CrossAccountId,
- payer: T::CrossAccountId,
+ issuer: CollectionIssuer<T::CrossAccountId>,
data: CreateCollectionData<T::CrossAccountId>,
) -> Result<CollectionId, DispatchError> {
- let id = match data.mode {
- CollectionMode::NFT => <PalletNonfungible<T>>::init_collection(sender, payer, data)?,
+ match data.mode {
CollectionMode::Fungible(decimal_points) => {
// check params
ensure!(
decimal_points <= MAX_DECIMAL_POINTS,
pallet_unique::Error::<T>::CollectionDecimalPointLimitExceeded
);
- <PalletFungible<T>>::init_collection(sender, payer, data)?
}
-
- #[cfg(feature = "refungible")]
- CollectionMode::ReFungible => <PalletRefungible<T>>::init_collection(sender, payer, data)?,
#[cfg(not(feature = "refungible"))]
CollectionMode::ReFungible => return unsupported!(T),
+
+ _ => {}
};
- Ok(id)
+
+ <PalletCommon<T>>::init_collection(sender, issuer, data)
}
fn destroy(sender: T::CrossAccountId, collection_id: CollectionId) -> DispatchResult {
runtime/opal/Cargo.tomldiffbeforeafterboth--- a/runtime/opal/Cargo.toml
+++ b/runtime/opal/Cargo.toml
@@ -151,7 +151,6 @@
'up-rpc/std',
'up-sponsorship/std',
- "orml-tokens/std",
"orml-traits/std",
"orml-vesting/std",
"orml-xcm-support/std",
@@ -179,7 +178,6 @@
'frame-system/try-runtime',
'frame-try-runtime',
'frame-try-runtime?/try-runtime',
- 'orml-tokens/try-runtime',
'orml-vesting/try-runtime',
'orml-xtokens/try-runtime',
'pallet-app-promotion/try-runtime',
@@ -254,7 +252,6 @@
frame-support = { workspace = true }
frame-system = { workspace = true }
frame-system-rpc-runtime-api = { workspace = true }
-orml-tokens = { workspace = true }
orml-traits = { workspace = true }
orml-vesting = { workspace = true }
orml-xcm-support = { workspace = true }
runtime/quartz/Cargo.tomldiffbeforeafterboth--- a/runtime/quartz/Cargo.toml
+++ b/runtime/quartz/Cargo.toml
@@ -150,7 +150,6 @@
'up-rpc/std',
'up-sponsorship/std',
- "orml-tokens/std",
"orml-traits/std",
"orml-vesting/std",
"orml-xcm-support/std",
@@ -175,7 +174,6 @@
'frame-support/try-runtime',
'frame-system/try-runtime',
'frame-try-runtime',
- 'orml-tokens/try-runtime',
'orml-vesting/try-runtime',
'orml-xtokens/try-runtime',
'pallet-app-promotion/try-runtime',
@@ -242,7 +240,6 @@
frame-support = { workspace = true }
frame-system = { workspace = true }
frame-system-rpc-runtime-api = { workspace = true }
-orml-tokens = { workspace = true }
orml-traits = { workspace = true }
orml-vesting = { workspace = true }
orml-xcm-support = { workspace = true }
runtime/tests/src/tests.rsdiffbeforeafterboth--- a/runtime/tests/src/tests.rs
+++ b/runtime/tests/src/tests.rs
@@ -2357,7 +2357,8 @@
#[test]
fn total_number_collections_bound_neg() {
new_test_ext().execute_with(|| {
- let origin1 = RuntimeOrigin::signed(1);
+ let user = 1;
+ let origin1 = RuntimeOrigin::signed(user);
for i in 1..=COLLECTION_NUMBER_LIMIT {
create_test_collection(&CollectionMode::NFT, CollectionId(i));
@@ -2376,6 +2377,7 @@
};
// 11-th collection in chain. Expects error
+ add_balance(user, CollectionCreationPrice::get() as u64 + 1);
assert_noop!(
Unique::create_collection_ex(origin1, data),
CommonError::<Test>::TotalCollectionsLimitExceeded
runtime/unique/Cargo.tomldiffbeforeafterboth--- a/runtime/unique/Cargo.toml
+++ b/runtime/unique/Cargo.toml
@@ -147,7 +147,6 @@
'up-rpc/std',
'up-sponsorship/std',
- "orml-tokens/std",
"orml-traits/std",
"orml-vesting/std",
"orml-xcm-support/std",
@@ -173,7 +172,6 @@
'frame-support/try-runtime',
'frame-system/try-runtime',
'frame-try-runtime',
- 'orml-tokens/try-runtime',
'orml-vesting/try-runtime',
'orml-xtokens/try-runtime',
'pallet-app-promotion/try-runtime',
@@ -245,7 +243,6 @@
frame-support = { workspace = true }
frame-system = { workspace = true }
frame-system-rpc-runtime-api = { workspace = true }
-orml-tokens = { workspace = true }
orml-traits = { workspace = true }
orml-vesting = { workspace = true }
orml-xcm-support = { workspace = true }