diff --git a/Cargo.lock b/Cargo.lock
index 110525a18..c0cccc11e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -68,7 +68,7 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
dependencies = [
- "getrandom 0.2.4",
+ "getrandom 0.2.5",
"once_cell",
"version_check",
]
@@ -99,9 +99,9 @@ dependencies = [
[[package]]
name = "anyhow"
-version = "1.0.53"
+version = "1.0.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94a45b455c14666b85fc40a019e8ab9eb75e3a124e05494f5397122bc9eb06e0"
+checksum = "159bb86af3a200e19a068f4224eae4c8bb2d0fa054c7e5d1cacd5cef95e684cd"
[[package]]
name = "approx"
@@ -223,9 +223,9 @@ dependencies = [
[[package]]
name = "async-lock"
-version = "2.4.0"
+version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6a8ea61bf9947a1007c5cada31e647dbc77b103c679858150003ba697ea798b"
+checksum = "e97a171d191782fba31bb902b14ad94e24a68145032b7eedf871ab0bc0d077b6"
dependencies = [
"event-listener",
]
@@ -849,9 +849,9 @@ checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899"
[[package]]
name = "byte-slice-cast"
-version = "1.2.0"
+version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d30c751592b77c499e7bce34d99d67c2c11bdc0574e9a488ddade14150a4698"
+checksum = "87c5fdd0166095e1d463fc6cc01aa8ce547ad77a4e84d42eb6762b084e28067e"
[[package]]
name = "byte-tools"
@@ -1002,7 +1002,7 @@ checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa"
dependencies = [
"camino",
"cargo-platform",
- "semver 1.0.5",
+ "semver 1.0.6",
"serde",
"serde_json",
]
@@ -1683,7 +1683,7 @@ name = "cumulus-pallet-parachain-system-proc-macro"
version = "0.1.0"
source = "git+https://github.com/paritytech/cumulus.git?branch=polkadot-v0.9.16#86f76c5619c64d1300315612695ad4b4fcd0f562"
dependencies = [
- "proc-macro-crate 1.1.2",
+ "proc-macro-crate 1.1.3",
"proc-macro2",
"quote",
"syn",
@@ -2026,7 +2026,6 @@ dependencies = [
name = "dolphin-runtime"
version = "3.1.4"
dependencies = [
- "calamari-vesting",
"cumulus-pallet-aura-ext",
"cumulus-pallet-dmp-queue",
"cumulus-pallet-parachain-system",
@@ -2047,6 +2046,9 @@ dependencies = [
"log",
"manta-collator-selection",
"manta-primitives",
+ "orml-xtokens",
+ "pallet-asset-manager",
+ "pallet-assets",
"pallet-aura",
"pallet-authorship",
"pallet-balances",
@@ -2067,9 +2069,11 @@ dependencies = [
"pallet-xcm",
"parachain-info",
"parity-scale-codec",
+ "polkadot-core-primitives",
"polkadot-parachain",
"polkadot-primitives",
"polkadot-runtime-common",
+ "polkadot-runtime-parachains",
"scale-info",
"serde",
"smallvec",
@@ -2089,6 +2093,7 @@ dependencies = [
"xcm",
"xcm-builder",
"xcm-executor",
+ "xcm-simulator",
]
[[package]]
@@ -2132,9 +2137,9 @@ checksum = "ee2626afccd7561a06cf1367e2950c4718ea04565e20fb5029b6c7d8ad09abcf"
[[package]]
name = "ed25519"
-version = "1.3.0"
+version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74e1069e39f1454367eb2de793ed062fac4c35c2934b76a81d90dd9abcd28816"
+checksum = "eed12bbf7b5312f8da1c2722bc06d8c6b12c2d86a7fb35a194c7f3e6fc2bbe39"
dependencies = [
"signature",
]
@@ -2170,11 +2175,11 @@ dependencies = [
[[package]]
name = "enum-as-inner"
-version = "0.3.3"
+version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c5f0096a91d210159eceb2ff5e1c4da18388a170e1e3ce948aac9c8fdbbf595"
+checksum = "570d109b813e904becc80d8d5da38376818a143348413f7149f1340fe04754d4"
dependencies = [
- "heck",
+ "heck 0.4.0",
"proc-macro2",
"quote",
"syn",
@@ -2555,7 +2560,7 @@ version = "4.0.0-dev"
source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.16#19162e43be45817b44c7d48e50d03f074f60fbf4"
dependencies = [
"frame-support-procedural-tools-derive",
- "proc-macro-crate 1.1.2",
+ "proc-macro-crate 1.1.3",
"proc-macro2",
"quote",
"syn",
@@ -2625,9 +2630,9 @@ dependencies = [
[[package]]
name = "fs-err"
-version = "2.6.0"
+version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ebd3504ad6116843b8375ad70df74e7bfe83cac77a1f3fe73200c844d43bfe0"
+checksum = "5bd79fa345a495d3ae89fb7165fec01c0e72f41821d642dda363a1e97975652e"
[[package]]
name = "fs-swap"
@@ -2836,9 +2841,9 @@ dependencies = [
[[package]]
name = "getrandom"
-version = "0.2.4"
+version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c"
+checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77"
dependencies = [
"cfg-if 1.0.0",
"libc",
@@ -2972,6 +2977,12 @@ dependencies = [
"unicode-segmentation",
]
+[[package]]
+name = "heck"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
+
[[package]]
name = "hermit-abi"
version = "0.1.19"
@@ -3254,9 +3265,9 @@ dependencies = [
[[package]]
name = "integer-encoding"
-version = "3.0.2"
+version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90c11140ffea82edce8dcd74137ce9324ec24b3cf0175fc9d7e29164da9915b8"
+checksum = "0e85a1509a128c855368e135cffcde7eac17d8e1083f41e2b98c58bc1a5074be"
[[package]]
name = "integer-sqrt"
@@ -3499,7 +3510,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d802063f7a3c867456955f9d2f15eb3ee0edb5ec9ec2b5526324756759221c0f"
dependencies = [
"log",
- "proc-macro-crate 1.1.2",
+ "proc-macro-crate 1.1.3",
"proc-macro2",
"quote",
"syn",
@@ -3737,9 +3748,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "libc"
-version = "0.2.118"
+version = "0.2.119"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06e509672465a0504304aa87f9f176f2b2b716ed8fb105ebe5c02dc6dce96a94"
+checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4"
[[package]]
name = "libloading"
@@ -4526,13 +4537,19 @@ dependencies = [
name = "manta-primitives"
version = "3.1.4"
dependencies = [
+ "frame-support",
+ "log",
"parity-scale-codec",
+ "scale-info",
"smallvec",
"sp-consensus-aura",
"sp-core",
"sp-io",
"sp-runtime",
"sp-std",
+ "xcm",
+ "xcm-builder",
+ "xcm-executor",
]
[[package]]
@@ -4886,7 +4903,7 @@ version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "424f6e86263cd5294cbd7f1e95746b95aca0e0d66bff31e5a40d6baa87b4aa99"
dependencies = [
- "proc-macro-crate 1.1.2",
+ "proc-macro-crate 1.1.3",
"proc-macro-error",
"proc-macro2",
"quote",
@@ -5180,6 +5197,72 @@ dependencies = [
"num-traits",
]
+[[package]]
+name = "orml-traits"
+version = "0.4.1-dev"
+source = "git+https://github.com/manta-network/open-runtime-module-library.git?rev=4a66b29#4a66b299037cc3997689538f82847785f9afa65d"
+dependencies = [
+ "frame-support",
+ "impl-trait-for-tuples",
+ "num-traits",
+ "orml-utilities",
+ "parity-scale-codec",
+ "scale-info",
+ "serde",
+ "sp-io",
+ "sp-runtime",
+ "sp-std",
+ "xcm",
+]
+
+[[package]]
+name = "orml-utilities"
+version = "0.4.1-dev"
+source = "git+https://github.com/manta-network/open-runtime-module-library.git?rev=4a66b29#4a66b299037cc3997689538f82847785f9afa65d"
+dependencies = [
+ "frame-support",
+ "parity-scale-codec",
+ "scale-info",
+ "serde",
+ "sp-io",
+ "sp-runtime",
+ "sp-std",
+]
+
+[[package]]
+name = "orml-xcm-support"
+version = "0.4.1-dev"
+source = "git+https://github.com/manta-network/open-runtime-module-library.git?rev=4a66b29#4a66b299037cc3997689538f82847785f9afa65d"
+dependencies = [
+ "frame-support",
+ "orml-traits",
+ "parity-scale-codec",
+ "sp-runtime",
+ "sp-std",
+ "xcm",
+ "xcm-executor",
+]
+
+[[package]]
+name = "orml-xtokens"
+version = "0.4.1-dev"
+source = "git+https://github.com/manta-network/open-runtime-module-library.git?rev=4a66b29#4a66b299037cc3997689538f82847785f9afa65d"
+dependencies = [
+ "cumulus-primitives-core",
+ "frame-support",
+ "frame-system",
+ "orml-traits",
+ "orml-xcm-support",
+ "parity-scale-codec",
+ "scale-info",
+ "serde",
+ "sp-io",
+ "sp-runtime",
+ "sp-std",
+ "xcm",
+ "xcm-executor",
+]
+
[[package]]
name = "owning_ref"
version = "0.4.1"
@@ -5189,6 +5272,39 @@ dependencies = [
"stable_deref_trait",
]
+[[package]]
+name = "pallet-asset-manager"
+version = "3.1.4"
+dependencies = [
+ "frame-benchmarking",
+ "frame-support",
+ "frame-system",
+ "manta-primitives",
+ "pallet-assets",
+ "pallet-balances",
+ "parity-scale-codec",
+ "scale-info",
+ "sp-core",
+ "sp-io",
+ "sp-runtime",
+ "sp-std",
+ "xcm",
+]
+
+[[package]]
+name = "pallet-assets"
+version = "4.0.0-dev"
+source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.16#19162e43be45817b44c7d48e50d03f074f60fbf4"
+dependencies = [
+ "frame-benchmarking",
+ "frame-support",
+ "frame-system",
+ "parity-scale-codec",
+ "scale-info",
+ "sp-runtime",
+ "sp-std",
+]
+
[[package]]
name = "pallet-aura"
version = "4.0.0-dev"
@@ -5877,7 +5993,7 @@ name = "pallet-staking-reward-curve"
version = "4.0.0-dev"
source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.16#19162e43be45817b44c7d48e50d03f074f60fbf4"
dependencies = [
- "proc-macro-crate 1.1.2",
+ "proc-macro-crate 1.1.3",
"proc-macro2",
"quote",
"syn",
@@ -6140,7 +6256,7 @@ version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1557010476e0595c9b568d16dcfb81b93cdeb157612726f5170d31aa707bed27"
dependencies = [
- "proc-macro-crate 1.1.2",
+ "proc-macro-crate 1.1.3",
"proc-macro2",
"quote",
"syn",
@@ -7121,7 +7237,7 @@ name = "polkadot-overseer-gen-proc-macro"
version = "0.9.16"
source = "git+https://github.com/paritytech/polkadot.git?branch=release-v0.9.16#41ab002d7451766324a9f314fee11c9c53314350"
dependencies = [
- "proc-macro-crate 1.1.2",
+ "proc-macro-crate 1.1.3",
"proc-macro2",
"quote",
"syn",
@@ -7614,9 +7730,9 @@ dependencies = [
[[package]]
name = "proc-macro-crate"
-version = "1.1.2"
+version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9dada8c9981fcf32929c3c0f0cd796a9284aca335565227ed88c83babb1d43dc"
+checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a"
dependencies = [
"thiserror",
"toml",
@@ -7686,7 +7802,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62941722fb675d463659e49c4f3fe1fe792ff24fe5bbaa9c08cd3b98a1c354f5"
dependencies = [
"bytes 1.1.0",
- "heck",
+ "heck 0.3.3",
"itertools",
"lazy_static",
"log",
@@ -7829,7 +7945,7 @@ version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
dependencies = [
- "getrandom 0.2.4",
+ "getrandom 0.2.5",
]
[[package]]
@@ -7912,7 +8028,7 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
dependencies = [
- "getrandom 0.2.4",
+ "getrandom 0.2.5",
"redox_syscall 0.2.10",
]
@@ -8072,9 +8188,9 @@ dependencies = [
[[package]]
name = "retain_mut"
-version = "0.1.6"
+version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51dd4445360338dab5116712bee1388dc727991d51969558a8882ab552e6db30"
+checksum = "8c31b5c4033f8fdde8700e4657be2c497e7288f01515be52168c631e2e4d4086"
[[package]]
name = "ring"
@@ -8241,7 +8357,7 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
dependencies = [
- "semver 1.0.5",
+ "semver 1.0.6",
]
[[package]]
@@ -8423,7 +8539,7 @@ name = "sc-chain-spec-derive"
version = "4.0.0-dev"
source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.16#19162e43be45817b44c7d48e50d03f074f60fbf4"
dependencies = [
- "proc-macro-crate 1.1.2",
+ "proc-macro-crate 1.1.3",
"proc-macro2",
"quote",
"syn",
@@ -9207,7 +9323,7 @@ name = "sc-tracing-proc-macro"
version = "4.0.0-dev"
source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.16#19162e43be45817b44c7d48e50d03f074f60fbf4"
dependencies = [
- "proc-macro-crate 1.1.2",
+ "proc-macro-crate 1.1.3",
"proc-macro2",
"quote",
"syn",
@@ -9286,7 +9402,7 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baeb2780690380592f86205aa4ee49815feb2acad8c2f59e6dd207148c3f1fcd"
dependencies = [
- "proc-macro-crate 1.1.2",
+ "proc-macro-crate 1.1.3",
"proc-macro2",
"quote",
"syn",
@@ -9394,9 +9510,9 @@ dependencies = [
[[package]]
name = "semver"
-version = "1.0.5"
+version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0486718e92ec9a68fbed73bb5ef687d71103b142595b406835649bebd33f72c7"
+checksum = "a4a3381e03edd24287172047536f20cabde766e2cd3e65e6b00fb3af51c4f38d"
dependencies = [
"serde",
]
@@ -9701,7 +9817,7 @@ version = "4.0.0-dev"
source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.16#19162e43be45817b44c7d48e50d03f074f60fbf4"
dependencies = [
"blake2-rfc",
- "proc-macro-crate 1.1.2",
+ "proc-macro-crate 1.1.3",
"proc-macro2",
"quote",
"syn",
@@ -10088,7 +10204,7 @@ name = "sp-npos-elections-solution-type"
version = "4.0.0-dev"
source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.16#19162e43be45817b44c7d48e50d03f074f60fbf4"
dependencies = [
- "proc-macro-crate 1.1.2",
+ "proc-macro-crate 1.1.3",
"proc-macro2",
"quote",
"syn",
@@ -10169,7 +10285,7 @@ version = "4.0.0"
source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.16#19162e43be45817b44c7d48e50d03f074f60fbf4"
dependencies = [
"Inflector",
- "proc-macro-crate 1.1.2",
+ "proc-macro-crate 1.1.3",
"proc-macro2",
"quote",
"syn",
@@ -10380,9 +10496,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "ss58-registry"
-version = "1.14.0"
+version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8cb4b9ce18beb6cb16ecad62d936245cef5212ddc8e094d7417a75e8d0e85f5"
+checksum = "2f9799e6d412271cb2414597581128b03f3285f260ea49f5363d07df6a332b3e"
dependencies = [
"Inflector",
"proc-macro2",
@@ -10465,7 +10581,7 @@ version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0"
dependencies = [
- "heck",
+ "heck 0.3.3",
"proc-macro-error",
"proc-macro2",
"quote",
@@ -10496,7 +10612,7 @@ version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "339f799d8b549e3744c7ac7feb216383e4005d94bdb22561b3ab8f3b808ae9fb"
dependencies = [
- "heck",
+ "heck 0.3.3",
"proc-macro2",
"quote",
"syn",
@@ -10508,7 +10624,7 @@ version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bb0dc7ee9c15cea6199cde9a127fa16a4c5819af85395457ad72d68edc85a38"
dependencies = [
- "heck",
+ "heck 0.3.3",
"proc-macro2",
"quote",
"rustversion",
@@ -11858,6 +11974,23 @@ dependencies = [
"syn",
]
+[[package]]
+name = "xcm-simulator"
+version = "0.9.16"
+source = "git+https://github.com/paritytech/polkadot.git?branch=release-v0.9.16#41ab002d7451766324a9f314fee11c9c53314350"
+dependencies = [
+ "frame-support",
+ "parity-scale-codec",
+ "paste",
+ "polkadot-core-primitives",
+ "polkadot-parachain",
+ "polkadot-runtime-parachains",
+ "sp-io",
+ "sp-std",
+ "xcm",
+ "xcm-executor",
+]
+
[[package]]
name = "yamux"
version = "0.9.0"
@@ -11874,18 +12007,18 @@ dependencies = [
[[package]]
name = "zeroize"
-version = "1.5.2"
+version = "1.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c88870063c39ee00ec285a2f8d6a966e5b6fb2becc4e8dac77ed0d370ed6006"
+checksum = "50344758e2f40e3a1fcfc8f6f91aa57b5f8ebd8d27919fe6451f15aaaf9ee608"
dependencies = [
"zeroize_derive",
]
[[package]]
name = "zeroize_derive"
-version = "1.3.1"
+version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81e8f13fef10b63c06356d65d416b070798ddabcadc10d3ece0c5be9b3c7eddb"
+checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17"
dependencies = [
"proc-macro2",
"quote",
diff --git a/pallets/asset-manager/Cargo.toml b/pallets/asset-manager/Cargo.toml
new file mode 100644
index 000000000..0cd04dee4
--- /dev/null
+++ b/pallets/asset-manager/Cargo.toml
@@ -0,0 +1,50 @@
+[package]
+authors = ['Manta Network']
+name = "pallet-asset-manager"
+version = "3.1.4"
+edition = "2021"
+homepage = 'https://manta.network'
+license = 'GPL-3.0'
+repository = 'https://github.com/Manta-Network/Manta/'
+
+
+[dependencies]
+codec = { package = "parity-scale-codec", version = "2.3.1", default-features = false }
+# scale-info has to be 1.0 for now
+scale-info = { version = "1.0", default-features = false, features = ["derive"] }
+sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.16", default-features = false }
+frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.16", default-features = false }
+frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.16", default-features = false }
+sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.16", default-features = false }
+frame-benchmarking = { git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.16", default-features = false, optional = true }
+manta-primitives = { path = '../../runtime/primitives', default-features = false}
+
+[dev-dependencies]
+sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.16" }
+pallet-balances = { git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.16" }
+pallet-assets = { git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.16" }
+sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.16" }
+xcm = { git = 'https://github.com/paritytech/polkadot.git', branch = "release-v0.9.16" }
+
+[features]
+default = ["std"]
+std = [
+ "codec/std",
+ "scale-info/std",
+ "sp-runtime/std",
+ "frame-support/std",
+ "frame-system/std",
+ "sp-std/std",
+ "manta-primitives/std",
+ "frame-benchmarking/std",
+]
+try-runtime = [
+ "frame-support/try-runtime",
+]
+
+runtime-benchmarks = [
+ "frame-benchmarking",
+ 'frame-support/runtime-benchmarks',
+ 'frame-system/runtime-benchmarks',
+]
+
diff --git a/pallets/asset-manager/src/lib.rs b/pallets/asset-manager/src/lib.rs
new file mode 100644
index 000000000..5f5f99dfd
--- /dev/null
+++ b/pallets/asset-manager/src/lib.rs
@@ -0,0 +1,374 @@
+// Copyright 2020-2022 Manta Network.
+// This file is part of Manta.
+//
+// Manta 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.
+//
+// Manta 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 Manta. If not, see .
+
+//! # Asset Manager Pallet
+//!
+//! A simple asset manager for native and cross-chain tokens
+//!
+//! ## Overview
+//!
+//! The Asset manager module provides functionality for registering cross chain assets
+//!
+//! TODO: detailed doc-string comment
+
+#![cfg_attr(not(feature = "std"), no_std)]
+
+pub use pallet::*;
+
+#[cfg(test)]
+mod mock;
+
+#[cfg(test)]
+mod tests;
+
+#[frame_support::pallet]
+pub mod pallet {
+
+ use codec::Codec;
+ use frame_support::{pallet_prelude::*, transactional, PalletId};
+ use frame_system::pallet_prelude::*;
+ use manta_primitives::{AssetIdLocationGetter, UnitsToWeightRatio};
+ use scale_info::TypeInfo;
+ use sp_runtime::{
+ traits::{AccountIdConversion, AtLeast32BitUnsigned, CheckedAdd, One},
+ ArithmeticError,
+ };
+
+ #[pallet::pallet]
+ #[pallet::generate_store(pub(super) trait Store)]
+ #[pallet::without_storage_info]
+ pub struct Pallet(_);
+
+ /// The AssetManagers's pallet id
+ pub const PALLET_ID: PalletId = PalletId(*b"asstmngr");
+
+ /// The registrar trait: defines the interface of creating an asset in the asset implementation layer.
+ /// We may revisit this interface design (e.g. add change asset interface). However, change StorageMetadata
+ /// should be rare.
+ pub trait AssetRegistrar {
+ /// Create an new asset.
+ ///
+ /// * `asset_id`: the asset id to be created
+ /// * `min_balance`: the minimum balance to hold this asset
+ /// * `metadata`: the metadata that the implementation layer stores
+ /// * `is_sufficient`: whether this asset can be used as reserve asset,
+ /// to the first approximation. More specifically, Whether a non-zero balance of this asset is deposit of sufficient
+ /// value to account for the state bloat associated with its balance storage. If set to
+ /// `true`, then non-zero balances may be stored without a `consumer` reference (and thus
+ /// an ED in the Balances pallet or whatever else is used to control user-account state
+ /// growth).
+ fn create_asset(
+ asset_id: T::AssetId,
+ min_balance: T::Balance,
+ metadata: T::StorageMetadata,
+ is_sufficient: bool,
+ ) -> DispatchResult;
+
+ /// Update asset metadata by `AssetId`.
+ ///
+ /// * `asset_id`: the asset id to be created.
+ /// * `metadata`: the metadata that the implementation layer stores.
+ fn update_asset_metadata(
+ asset_id: T::AssetId,
+ metadata: T::StorageMetadata,
+ ) -> DispatchResult;
+ }
+
+ /// The AssetMetadata trait:
+ pub trait AssetMetadata {
+ /// Returns the minimum balance to hold this asset
+ fn min_balance(&self) -> T::Balance;
+
+ /// Returns a boolean value indicating whether this asset needs an existential deposit
+ fn is_sufficient(&self) -> bool;
+ }
+
+ /// Convert AssetId and AssetLocation
+ impl AssetIdLocationGetter for Pallet {
+ fn get_asset_id(loc: &T::AssetLocation) -> Option {
+ LocationAssetId::::get(loc)
+ }
+
+ fn get_asset_location(id: T::AssetId) -> Option {
+ AssetIdLocation::::get(id)
+ }
+ }
+
+ /// Get unit per second from `AssetId`
+ impl UnitsToWeightRatio for Pallet {
+ fn get_units_per_second(id: T::AssetId) -> Option {
+ UnitsPerSecond::::get(id)
+ }
+ }
+
+ #[pallet::config]
+ pub trait Config: frame_system::Config {
+ /// The overarching event type.
+ type Event: From> + IsType<::Event>;
+
+ /// The asset id type, this have to be consistent with pallet-manta-pay
+ type AssetId: Member
+ + Parameter
+ + Default
+ + Copy
+ + AtLeast32BitUnsigned
+ + MaybeSerializeDeserialize
+ + MaxEncodedLen
+ + TypeInfo;
+
+ /// The trait we use to register Assets
+ type AssetRegistrar: AssetRegistrar;
+
+ /// The units in which we record balances.
+ type Balance: Member + Parameter + AtLeast32BitUnsigned + Default + Copy + MaxEncodedLen;
+
+ /// Metadata type that required in token storage: e.g. AssetMetadata in Pallet-Assets.
+ type StorageMetadata: Member + Parameter + Default;
+
+ /// The Asset Metadata type stored in this pallet.
+ type AssetRegistrarMetadata: Member
+ + Parameter
+ + Codec
+ + Default
+ + Into
+ + AssetMetadata;
+
+ /// The AssetLocation type: could be just a thin wrapper of MultiLocation
+ type AssetLocation: Member + Parameter + Default + TypeInfo;
+
+ /// The origin which may forcibly create or destroy an asset or otherwise alter privileged
+ /// attributes.
+ type ModifierOrigin: EnsureOrigin;
+ }
+
+ #[pallet::event]
+ #[pallet::generate_deposit(pub(super) fn deposit_event)]
+ pub enum Event {
+ /// A new asset registered.
+ AssetRegistered {
+ asset_id: T::AssetId,
+ asset_address: T::AssetLocation,
+ metadata: T::AssetRegistrarMetadata,
+ },
+ /// An asset's location has been updated.
+ AssetLocationUpdated {
+ asset_id: T::AssetId,
+ location: T::AssetLocation,
+ },
+ /// An asset;s metadata has been updated.
+ AssetMetadataUpdated {
+ asset_id: T::AssetId,
+ metadata: T::AssetRegistrarMetadata,
+ },
+ /// Update units per second of an asset
+ UnitsPerSecondUpdated {
+ asset_id: T::AssetId,
+ units_per_second: u128,
+ },
+ }
+
+ /// Error.
+ #[pallet::error]
+ pub enum Error {
+ /// Location already exists.
+ LocationAlreadyExists,
+ /// Error creating asset, e.g. error returned from the implementation layer.
+ ErrorCreatingAsset,
+ /// Update a non-exist asset
+ UpdateNonExistAsset,
+ /// Asset already registered.
+ AssetAlreadyRegistered,
+ }
+
+ /// AssetId to MultiLocation Map.
+ /// This is mostly useful when sending an asset to a foreign location.
+ #[pallet::storage]
+ #[pallet::getter(fn asset_id_location)]
+ pub(super) type AssetIdLocation =
+ StorageMap<_, Blake2_128Concat, T::AssetId, T::AssetLocation>;
+
+ /// MultiLocation to AssetId Map.
+ /// This is mostly useful when receiving an asset from a foreign location.
+ #[pallet::storage]
+ #[pallet::getter(fn location_asset_id)]
+ pub(super) type LocationAssetId =
+ StorageMap<_, Blake2_128Concat, T::AssetLocation, T::AssetId>;
+
+ /// AssetId to AssetRegistrar Map.
+ #[pallet::storage]
+ #[pallet::getter(fn asset_id_metadata)]
+ pub(super) type AssetIdMetadata =
+ StorageMap<_, Blake2_128Concat, T::AssetId, T::AssetRegistrarMetadata>;
+
+ /// Get the next available AssetId.
+ #[pallet::storage]
+ #[pallet::getter(fn next_asset_id)]
+ pub type NextAssetId = StorageValue<_, T::AssetId, ValueQuery>;
+
+ /// XCM transfer cost for different asset.
+ #[pallet::storage]
+ pub type UnitsPerSecond = StorageMap<_, Blake2_128Concat, T::AssetId, u128>;
+
+ #[pallet::call]
+ impl Pallet {
+ /// Register a new asset in the asset manager.
+ ///
+ /// * `origin`: Caller of this extrinsic, the acess control is specfied by `ForceOrigin`.
+ /// * `location`: Location of the asset.
+ /// * `metadata`: Asset metadata.
+ /// * `min_balance`: Minimum balance to keep an account alive, used in conjunction with `is_sufficient`.
+ /// * `is_sufficient`: Whether this asset needs users to have an existential deposit to hold
+ /// this asset.
+ ///
+ /// #
+ /// TODO: get actual weight
+ /// #
+ #[pallet::weight(50_000_000)]
+ #[transactional]
+ pub fn register_asset(
+ origin: OriginFor,
+ location: T::AssetLocation,
+ metadata: T::AssetRegistrarMetadata,
+ ) -> DispatchResult {
+ T::ModifierOrigin::ensure_origin(origin)?;
+ ensure!(
+ !LocationAssetId::::contains_key(&location),
+ Error::::LocationAlreadyExists
+ );
+ let asset_id = Self::get_next_asset_id()?;
+ T::AssetRegistrar::create_asset(
+ asset_id,
+ metadata.min_balance(),
+ metadata.clone().into(),
+ metadata.is_sufficient(),
+ )
+ .map_err(|_| Error::::ErrorCreatingAsset)?;
+ AssetIdLocation::::insert(&asset_id, &location);
+ AssetIdMetadata::::insert(&asset_id, &metadata);
+ LocationAssetId::::insert(&location, &asset_id);
+ Self::deposit_event(Event::::AssetRegistered {
+ asset_id,
+ asset_address: location,
+ metadata,
+ });
+ Ok(())
+ }
+
+ /// Update an asset by its asset id in the asset manager.
+ ///
+ /// * `origin`: Caller of this extrinsic, the acess control is specfied by `ForceOrigin`.
+ /// * `asset_id`: AssetId to be updated.
+ /// * `location`: `location` to update the asset location.
+ /// #
+ /// TODO: get actual weight
+ /// #
+ #[pallet::weight(50_000_000)]
+ #[transactional]
+ pub fn update_asset_location(
+ origin: OriginFor,
+ #[pallet::compact] asset_id: T::AssetId,
+ location: T::AssetLocation,
+ ) -> DispatchResult {
+ // checks validity
+ T::ModifierOrigin::ensure_origin(origin)?;
+ ensure!(
+ AssetIdLocation::::contains_key(&asset_id),
+ Error::::UpdateNonExistAsset
+ );
+ ensure!(
+ !LocationAssetId::::contains_key(&location),
+ Error::::LocationAlreadyExists
+ );
+ // change the ledger state.
+ let old_location =
+ AssetIdLocation::::get(&asset_id).ok_or(Error::::UpdateNonExistAsset)?;
+ LocationAssetId::::remove(&old_location);
+ LocationAssetId::::insert(&location, &asset_id);
+ AssetIdLocation::::insert(&asset_id, &location);
+ // deposit event.
+ Self::deposit_event(Event::::AssetLocationUpdated { asset_id, location });
+ Ok(())
+ }
+
+ /// Update an asset's metadata by its `asset_id`
+ ///
+ /// * `origin`: Caller of this extrinsic, the acess control is specfied by `ForceOrigin`.
+ /// * `asset_id`: AssetId to be updated.
+ /// * `metadata`: new `metadata` to be associated with `asset_id`.
+ #[pallet::weight(50_000_000)]
+ #[transactional]
+ pub fn update_asset_metadata(
+ origin: OriginFor,
+ #[pallet::compact] asset_id: T::AssetId,
+ metadata: T::AssetRegistrarMetadata,
+ ) -> DispatchResult {
+ T::ModifierOrigin::ensure_origin(origin)?;
+ ensure!(
+ AssetIdLocation::::contains_key(&asset_id),
+ Error::::UpdateNonExistAsset
+ );
+ AssetIdMetadata::::insert(&asset_id, &metadata);
+ Self::deposit_event(Event::::AssetMetadataUpdated { asset_id, metadata });
+ Ok(())
+ }
+
+ /// Update an asset by its asset id in the asset manager.
+ ///
+ /// * `origin`: Caller of this extrinsic, the acess control is specfied by `ForceOrigin`.
+ /// * `asset_id`: AssetId to be updated.
+ /// * `units_per_second`: units per second for `asset_id`
+ /// #
+ /// TODO: get actual weight
+ /// #
+ #[pallet::weight(50_000_000)]
+ #[transactional]
+ pub fn set_units_per_second(
+ origin: OriginFor,
+ #[pallet::compact] asset_id: T::AssetId,
+ #[pallet::compact] units_per_second: u128,
+ ) -> DispatchResult {
+ T::ModifierOrigin::ensure_origin(origin)?;
+ ensure!(
+ AssetIdLocation::::contains_key(&asset_id),
+ Error::::UpdateNonExistAsset
+ );
+ UnitsPerSecond::::insert(&asset_id, &units_per_second);
+ Self::deposit_event(Event::::UnitsPerSecondUpdated {
+ asset_id,
+ units_per_second,
+ });
+ Ok(())
+ }
+ }
+
+ impl Pallet {
+ /// Get and increment the `NextAssetID` by one.
+ fn get_next_asset_id() -> Result {
+ NextAssetId::::try_mutate(|current| -> Result {
+ let id = *current;
+ *current = current
+ .checked_add(&One::one())
+ .ok_or(ArithmeticError::Overflow)?;
+ Ok(id)
+ })
+ }
+
+ /// The account ID of AssetManager
+ pub fn account_id() -> T::AccountId {
+ PALLET_ID.into_account()
+ }
+ }
+}
diff --git a/pallets/asset-manager/src/mock.rs b/pallets/asset-manager/src/mock.rs
new file mode 100644
index 000000000..09acc3b91
--- /dev/null
+++ b/pallets/asset-manager/src/mock.rs
@@ -0,0 +1,196 @@
+// Copyright 2020-2022 Manta Network.
+// This file is part of Manta.
+//
+// Manta 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.
+//
+// Manta 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 Manta. If not, see .
+//
+// The pallet-tx-pause pallet is forked from Acala's transaction-pause module https://github.com/AcalaNetwork/Acala/tree/master/modules/transaction-pause
+// The original license is the following - SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+
+//! Mock runtime for asset-manager
+
+use super::*;
+use crate as pallet_asset_manager;
+use frame_support::{construct_runtime, parameter_types, traits::ConstU32};
+use frame_system as system;
+use frame_system::EnsureRoot;
+use manta_primitives::{
+ AccountId, AssetId, AssetLocation, AssetRegistarMetadata, AssetStorageMetadata, Balance,
+ ASSET_STRING_LIMIT,
+};
+use sp_core::H256;
+use sp_runtime::{
+ testing::Header,
+ traits::{BlakeTwo256, IdentityLookup},
+};
+
+parameter_types! {
+ pub const BlockHashCount: u64 = 250;
+ pub const SS58Prefix: u8 = 78;
+}
+
+impl system::Config for Runtime {
+ type BaseCallFilter = frame_support::traits::Everything;
+ type BlockWeights = ();
+ type BlockLength = ();
+ type DbWeight = ();
+ type Origin = Origin;
+ type Call = Call;
+ type Index = u64;
+ type BlockNumber = u64;
+ type Hash = H256;
+ type Hashing = BlakeTwo256;
+ type AccountId = AccountId;
+ type Lookup = IdentityLookup;
+ type Header = Header;
+ type Event = Event;
+ type BlockHashCount = BlockHashCount;
+ type Version = ();
+ type PalletInfo = PalletInfo;
+ type AccountData = pallet_balances::AccountData;
+ type OnNewAccount = ();
+ type OnKilledAccount = ();
+ type SystemWeightInfo = ();
+ type SS58Prefix = SS58Prefix;
+ type OnSetCode = ();
+ type MaxConsumers = ConstU32<16>;
+}
+
+parameter_types! {
+ pub const AssetDeposit: Balance = 0; // Does not really matter as this will be only called by root
+ pub const AssetAccountDeposit: Balance = 0;
+ pub const ApprovalDeposit: Balance = 0;
+ pub const AssetsStringLimit: u32 = ASSET_STRING_LIMIT;
+ pub const MetadataDepositBase: Balance = 0;
+ pub const MetadataDepositPerByte: Balance = 0;
+}
+
+impl pallet_assets::Config for Runtime {
+ type Event = Event;
+ type Balance = Balance;
+ type AssetId = AssetId;
+ type Currency = Balances;
+ type ForceOrigin = EnsureRoot;
+ type AssetDeposit = AssetDeposit;
+ type AssetAccountDeposit = AssetAccountDeposit;
+ type MetadataDepositBase = MetadataDepositBase;
+ type MetadataDepositPerByte = MetadataDepositPerByte;
+ type ApprovalDeposit = ApprovalDeposit;
+ type StringLimit = AssetsStringLimit;
+ type Freezer = ();
+ type Extra = ();
+ type WeightInfo = ();
+}
+
+parameter_types! {
+ pub ExistentialDeposit: Balance = 1;
+ pub const MaxLocks: u32 = 50;
+ pub const MaxReserves: u32 = 50;
+}
+
+impl pallet_balances::Config for Runtime {
+ type MaxLocks = MaxLocks;
+ type Balance = Balance;
+ type Event = Event;
+ type DustRemoval = ();
+ type ExistentialDeposit = ExistentialDeposit;
+ type AccountStore = System;
+ type WeightInfo = ();
+ type MaxReserves = MaxReserves;
+ type ReserveIdentifier = [u8; 8];
+}
+
+pub struct AssetRegistrar;
+use frame_support::pallet_prelude::DispatchResult;
+impl pallet_asset_manager::AssetRegistrar for AssetRegistrar {
+ fn create_asset(
+ asset_id: AssetId,
+ min_balance: Balance,
+ metadata: AssetStorageMetadata,
+ is_sufficient: bool,
+ ) -> DispatchResult {
+ Assets::force_create(
+ Origin::root(),
+ asset_id,
+ AssetManager::account_id(),
+ is_sufficient,
+ min_balance,
+ )?;
+
+ Assets::force_set_metadata(
+ Origin::root(),
+ asset_id,
+ metadata.name,
+ metadata.symbol,
+ metadata.decimals,
+ metadata.is_frozen,
+ )
+ }
+
+ fn update_asset_metadata(asset_id: AssetId, metadata: AssetStorageMetadata) -> DispatchResult {
+ Assets::force_set_metadata(
+ Origin::root(),
+ asset_id,
+ metadata.name,
+ metadata.symbol,
+ metadata.decimals,
+ metadata.is_frozen,
+ )
+ }
+}
+
+impl AssetMetadata for AssetRegistarMetadata {
+ fn min_balance(&self) -> Balance {
+ self.min_balance
+ }
+
+ fn is_sufficient(&self) -> bool {
+ self.is_sufficient
+ }
+}
+
+impl pallet_asset_manager::Config for Runtime {
+ type Event = Event;
+ type Balance = Balance;
+ type AssetId = AssetId;
+ type AssetRegistrarMetadata = AssetRegistarMetadata;
+ type StorageMetadata = AssetStorageMetadata;
+ type AssetLocation = AssetLocation;
+ type AssetRegistrar = AssetRegistrar;
+ type ModifierOrigin = EnsureRoot;
+}
+
+type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic;
+type Block = frame_system::mocking::MockBlock;
+
+construct_runtime!(
+ pub enum Runtime where
+ Block = Block,
+ NodeBlock = Block,
+ UncheckedExtrinsic = UncheckedExtrinsic,
+ {
+ System: frame_system::{Pallet, Call, Storage, Config, Event} = 0,
+ Assets: pallet_assets::{Pallet, Storage, Event} = 1,
+ AssetManager: pallet_asset_manager::{Pallet, Call, Storage, Event} = 2,
+ Balances: pallet_balances::{Pallet, Call, Storage, Config, Event} = 3,
+ }
+);
+
+pub const PALLET_BALANCES_INDEX: u8 = 3;
+
+pub fn new_test_ext() -> sp_io::TestExternalities {
+ let t = frame_system::GenesisConfig::default()
+ .build_storage::()
+ .unwrap();
+ sp_io::TestExternalities::new(t)
+}
diff --git a/pallets/asset-manager/src/tests.rs b/pallets/asset-manager/src/tests.rs
new file mode 100644
index 000000000..6b055ae71
--- /dev/null
+++ b/pallets/asset-manager/src/tests.rs
@@ -0,0 +1,191 @@
+// Copyright 2020-2022 Manta Network.
+// This file is part of Manta.
+//
+// Manta 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.
+//
+// Manta 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 Manta. If not, see .
+//
+// The pallet-tx-pause pallet is forked from Acala's transaction-pause module https://github.com/AcalaNetwork/Acala/tree/master/modules/transaction-pause
+// The original license is the following - SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+
+//! unit tests for asset-manager
+
+use crate::{self as asset_manager, AssetIdLocation, UnitsPerSecond};
+use asset_manager::mock::*;
+use frame_support::{assert_noop, assert_ok};
+use manta_primitives::{AssetLocation, AssetRegistarMetadata};
+use sp_runtime::traits::BadOrigin;
+use xcm::{latest::prelude::*, VersionedMultiLocation};
+
+#[test]
+fn basic_setup_should_work() {
+ new_test_ext()
+ .execute_with(|| assert!(AssetIdLocation::::iter_values().next().is_none()));
+}
+
+#[test]
+fn wrong_modifer_origin_should_not_work() {
+ new_test_ext().execute_with(|| {
+ let asset_metadata = AssetRegistarMetadata {
+ name: b"Kusama".to_vec(),
+ symbol: b"KSM".to_vec(),
+ decimals: 12,
+ min_balance: 1u128,
+ evm_address: None,
+ is_frozen: false,
+ is_sufficient: true,
+ };
+ let source_location = AssetLocation(VersionedMultiLocation::V1(MultiLocation::parent()));
+ assert_noop!(
+ AssetManager::register_asset(
+ Origin::signed([0u8; 32].into()),
+ source_location.clone(),
+ asset_metadata.clone()
+ ),
+ BadOrigin
+ );
+ assert_noop!(
+ AssetManager::update_asset_location(
+ Origin::signed([2u8; 32].into()),
+ 0,
+ source_location
+ ),
+ BadOrigin
+ );
+ assert_noop!(
+ AssetManager::update_asset_metadata(
+ Origin::signed([3u8; 32].into()),
+ 0,
+ asset_metadata
+ ),
+ BadOrigin
+ );
+ assert_noop!(
+ AssetManager::set_units_per_second(Origin::signed([4u8; 32].into()), 0, 0),
+ BadOrigin
+ );
+ })
+}
+
+#[test]
+fn register_asset_should_work() {
+ let asset_metadata = AssetRegistarMetadata {
+ name: b"Kusama".to_vec(),
+ symbol: b"KSM".to_vec(),
+ decimals: 12,
+ min_balance: 1u128,
+ evm_address: None,
+ is_frozen: false,
+ is_sufficient: true,
+ };
+ let source_location = AssetLocation(VersionedMultiLocation::V1(MultiLocation::parent()));
+ let new_location = AssetLocation(VersionedMultiLocation::V1(MultiLocation::new(
+ 1,
+ X2(Parachain(1), PalletInstance(PALLET_BALANCES_INDEX)),
+ )));
+ new_test_ext().execute_with(|| {
+ let mut counter: u32 = 0;
+ // Register relay chain native token
+ assert_ok!(AssetManager::register_asset(
+ Origin::root(),
+ source_location.clone(),
+ asset_metadata.clone()
+ ));
+ assert_eq!(
+ AssetIdLocation::::get(counter),
+ Some(source_location.clone())
+ );
+ counter += 1;
+ // Register twice will fail
+ assert_noop!(
+ AssetManager::register_asset(Origin::root(), source_location, asset_metadata.clone()),
+ crate::Error::::LocationAlreadyExists
+ );
+ // Register a new asset
+ assert_ok!(AssetManager::register_asset(
+ Origin::root(),
+ new_location.clone(),
+ asset_metadata.clone()
+ ));
+ assert_eq!(AssetIdLocation::::get(counter), Some(new_location));
+ })
+}
+
+#[test]
+fn update_asset() {
+ let asset_metadata = AssetRegistarMetadata {
+ name: b"Kusama".to_vec(),
+ symbol: b"KSM".to_vec(),
+ decimals: 12,
+ min_balance: 1u128,
+ evm_address: None,
+ is_frozen: false,
+ is_sufficient: true,
+ };
+ let mut new_metadata = asset_metadata.clone();
+ new_metadata.is_frozen = true;
+ let source_location = AssetLocation(VersionedMultiLocation::V1(MultiLocation::parent()));
+ let new_location = AssetLocation(VersionedMultiLocation::V1(MultiLocation::new(
+ 1,
+ X2(Parachain(1), PalletInstance(PALLET_BALANCES_INDEX)),
+ )));
+ new_test_ext().execute_with(|| {
+ // Register relay chain native token
+ assert_ok!(AssetManager::register_asset(
+ Origin::root(),
+ source_location.clone(),
+ asset_metadata.clone()
+ ));
+ assert_eq!(
+ AssetIdLocation::::get(0),
+ Some(source_location.clone())
+ );
+ // Update the asset metadata
+ assert_ok!(AssetManager::update_asset_metadata(
+ Origin::root(),
+ 0,
+ new_metadata.clone()
+ ));
+ // Update the asset location
+ assert_ok!(AssetManager::update_asset_location(
+ Origin::root(),
+ 0,
+ new_location.clone()
+ ));
+ // Update asset units per seconds
+ assert_ok!(AssetManager::set_units_per_second(
+ Origin::root(),
+ 0,
+ 125u128
+ ));
+ assert_eq!(UnitsPerSecond::::get(0), Some(125));
+ // Update a non-exist asset should fail
+ assert_noop!(
+ AssetManager::update_asset_location(Origin::root(), 1, new_location.clone()),
+ crate::Error::::UpdateNonExistAsset
+ );
+ assert_noop!(
+ AssetManager::update_asset_metadata(Origin::root(), 1, new_metadata.clone()),
+ crate::Error::::UpdateNonExistAsset
+ );
+ // Update an asset to an existing location will fail
+ assert_ok!(AssetManager::register_asset(
+ Origin::root(),
+ source_location.clone(),
+ asset_metadata.clone()
+ ));
+ assert_noop!(
+ AssetManager::update_asset_location(Origin::root(), 1, new_location),
+ crate::Error::::LocationAlreadyExists
+ );
+ })
+}
diff --git a/pallets/pallet-tx-pause/Cargo.toml b/pallets/tx-pause/Cargo.toml
similarity index 100%
rename from pallets/pallet-tx-pause/Cargo.toml
rename to pallets/tx-pause/Cargo.toml
diff --git a/pallets/pallet-tx-pause/README.md b/pallets/tx-pause/README.md
similarity index 100%
rename from pallets/pallet-tx-pause/README.md
rename to pallets/tx-pause/README.md
diff --git a/pallets/pallet-tx-pause/src/benchmarking.rs b/pallets/tx-pause/src/benchmarking.rs
similarity index 100%
rename from pallets/pallet-tx-pause/src/benchmarking.rs
rename to pallets/tx-pause/src/benchmarking.rs
diff --git a/pallets/pallet-tx-pause/src/lib.rs b/pallets/tx-pause/src/lib.rs
similarity index 100%
rename from pallets/pallet-tx-pause/src/lib.rs
rename to pallets/tx-pause/src/lib.rs
diff --git a/pallets/pallet-tx-pause/src/mock.rs b/pallets/tx-pause/src/mock.rs
similarity index 100%
rename from pallets/pallet-tx-pause/src/mock.rs
rename to pallets/tx-pause/src/mock.rs
diff --git a/pallets/pallet-tx-pause/src/tests.rs b/pallets/tx-pause/src/tests.rs
similarity index 100%
rename from pallets/pallet-tx-pause/src/tests.rs
rename to pallets/tx-pause/src/tests.rs
diff --git a/pallets/pallet-tx-pause/src/weights.rs b/pallets/tx-pause/src/weights.rs
similarity index 100%
rename from pallets/pallet-tx-pause/src/weights.rs
rename to pallets/tx-pause/src/weights.rs
diff --git a/runtime/calamari/Cargo.toml b/runtime/calamari/Cargo.toml
index 87bead203..8edf60f46 100644
--- a/runtime/calamari/Cargo.toml
+++ b/runtime/calamari/Cargo.toml
@@ -81,7 +81,11 @@ pallet-xcm = { git = 'https://github.com/paritytech/polkadot.git', default-featu
manta-primitives = { path = '../primitives', default-features = false }
calamari-vesting = { path = '../../pallets/vesting', default-features = false }
manta-collator-selection = { path = '../../pallets/collator-selection', default-features = false }
-pallet-tx-pause = { path = '../../pallets/pallet-tx-pause', default-features = false }
+pallet-tx-pause = { path = '../../pallets/tx-pause', default-features = false }
+
+[dev-dependencies]
+serde_json = "1.0"
+reqwest = { version = "0.11", features = ["blocking"] }
[package.metadata.docs.rs]
targets = ['x86_64-unknown-linux-gnu']
@@ -89,9 +93,6 @@ targets = ['x86_64-unknown-linux-gnu']
[build-dependencies]
substrate-wasm-builder = { git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.16" }
-[dev-dependencies]
-serde_json = "1.0"
-reqwest = { version = "0.11", features = ["blocking"] }
[features]
default = ['std']
@@ -156,6 +157,7 @@ std = [
'pallet-sudo/std',
'pallet-xcm/std',
'pallet-transaction-payment/std',
+ 'pallet-treasury/std',
'pallet-collective/std',
'pallet-democracy/std',
'pallet-scheduler/std',
diff --git a/runtime/dolphin/Cargo.toml b/runtime/dolphin/Cargo.toml
index cf86a3935..442efe7a8 100644
--- a/runtime/dolphin/Cargo.toml
+++ b/runtime/dolphin/Cargo.toml
@@ -39,6 +39,7 @@ frame-system-rpc-runtime-api = { git = 'https://github.com/paritytech/substrate.
frame-try-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, optional = true, branch = "polkadot-v0.9.16" }
# Substrate pallets
+pallet-assets = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.16" }
pallet-aura = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.16" }
pallet-authorship = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.16" }
pallet-balances = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.16" }
@@ -70,6 +71,7 @@ parachain-info = { git = 'https://github.com/paritytech/cumulus.git', default-fe
# Polkadot dependencies
polkadot-primitives = { git = 'https://github.com/paritytech/polkadot.git', default-features = false, branch = "release-v0.9.16" }
+polkadot-core-primitives = { git = 'https://github.com/paritytech/polkadot.git', default-features = false, branch = "release-v0.9.16" }
polkadot-runtime-common = { git = 'https://github.com/paritytech/polkadot.git', default-features = false, branch = "release-v0.9.16" }
polkadot-parachain = { git = 'https://github.com/paritytech/polkadot.git', default-features = false, branch = "release-v0.9.16" }
xcm = { git = 'https://github.com/paritytech/polkadot.git', default-features = false, branch = "release-v0.9.16" }
@@ -79,9 +81,16 @@ pallet-xcm = { git = 'https://github.com/paritytech/polkadot.git', default-featu
# Self dependencies
manta-primitives = { path = '../primitives', default-features = false }
-calamari-vesting = { path = '../../pallets/vesting', default-features = false }
manta-collator-selection = { path = '../../pallets/collator-selection', default-features = false }
-pallet-tx-pause = { path = '../../pallets/pallet-tx-pause', default-features = false }
+pallet-tx-pause = { path = '../../pallets/tx-pause', default-features = false }
+pallet-asset-manager = { path = '../../pallets/asset-manager', default-features = false }
+
+# Third party (vendored) dependencies
+orml-xtokens = { git = "https://github.com/manta-network/open-runtime-module-library.git", default-features = false, rev="4a66b29"}
+
+[dev-dependencies]
+xcm-simulator = { git = 'https://github.com/paritytech/polkadot.git', default-features = false, branch = "release-v0.9.16"}
+polkadot-runtime-parachains = { git = 'https://github.com/paritytech/polkadot.git', branch = "release-v0.9.16" }
[package.metadata.docs.rs]
targets = ['x86_64-unknown-linux-gnu']
@@ -115,7 +124,6 @@ runtime-benchmarks = [
'pallet-preimage/runtime-benchmarks',
'pallet-scheduler/runtime-benchmarks',
'pallet-membership/runtime-benchmarks',
- 'calamari-vesting/runtime-benchmarks',
'pallet-tx-pause/runtime-benchmarks',
'pallet-treasury/runtime-benchmarks',
]
@@ -170,8 +178,9 @@ std = [
'xcm-executor/std',
'polkadot-runtime-common/std',
'polkadot-primitives/std',
+ 'orml-xtokens/std',
+ 'pallet-asset-manager/std',
'manta-collator-selection/std',
- 'calamari-vesting/std',
'pallet-tx-pause/std',
'pallet-treasury/std',
]
\ No newline at end of file
diff --git a/runtime/dolphin/tests/xcm_mock/mod.rs b/runtime/dolphin/tests/xcm_mock/mod.rs
new file mode 100644
index 000000000..a83bca6b1
--- /dev/null
+++ b/runtime/dolphin/tests/xcm_mock/mod.rs
@@ -0,0 +1,130 @@
+// Copyright 2020-2022 Manta Network.
+// This file is part of Manta.
+//
+// Manta 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.
+//
+// Manta 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 Manta. If not, see .
+
+pub mod parachain;
+pub mod relay_chain;
+
+use frame_support::traits::GenesisBuild;
+use polkadot_parachain::primitives::Id as ParaId;
+use sp_runtime::traits::AccountIdConversion;
+use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain};
+pub const ALICE: sp_runtime::AccountId32 = sp_runtime::AccountId32::new([0u8; 32]);
+pub const INITIAL_BALANCE: u128 = 10_000_000_000_000_000;
+
+decl_test_parachain! {
+ pub struct ParaA {
+ Runtime = parachain::Runtime,
+ XcmpMessageHandler = parachain::MsgQueue,
+ DmpMessageHandler = parachain::MsgQueue,
+ new_ext = para_ext(1),
+ }
+}
+
+decl_test_parachain! {
+ pub struct ParaB {
+ Runtime = parachain::Runtime,
+ XcmpMessageHandler = parachain::MsgQueue,
+ DmpMessageHandler = parachain::MsgQueue,
+ new_ext = para_ext(2),
+ }
+}
+
+decl_test_parachain! {
+ pub struct ParaC {
+ Runtime = parachain::Runtime,
+ XcmpMessageHandler = parachain::MsgQueue,
+ DmpMessageHandler = parachain::MsgQueue,
+ new_ext = para_ext(3),
+ }
+}
+
+decl_test_relay_chain! {
+ pub struct Relay {
+ Runtime = relay_chain::Runtime,
+ XcmConfig = relay_chain::XcmExecutorConfig,
+ new_ext = relay_ext(),
+ }
+}
+
+decl_test_network! {
+ pub struct MockNet {
+ relay_chain = Relay,
+ parachains = vec![
+ (1, ParaA),
+ (2, ParaB),
+ (3, ParaC),
+ ],
+ }
+}
+
+pub fn para_account_id(id: u32) -> relay_chain::AccountId {
+ ParaId::from(id).into_account()
+}
+
+pub fn para_ext(para_id: u32) -> sp_io::TestExternalities {
+ use parachain::{MsgQueue, Runtime, System};
+
+ let mut t = frame_system::GenesisConfig::default()
+ .build_storage::()
+ .unwrap();
+
+ pallet_balances::GenesisConfig:: {
+ balances: vec![(ALICE, INITIAL_BALANCE)],
+ }
+ .assimilate_storage(&mut t)
+ .unwrap();
+
+ let parachain_info_config = parachain_info::GenesisConfig {
+ parachain_id: para_id.into(),
+ };
+ >::assimilate_storage(
+ ¶chain_info_config,
+ &mut t,
+ )
+ .unwrap();
+
+ let mut ext = sp_io::TestExternalities::new(t);
+ ext.execute_with(|| {
+ System::set_block_number(1);
+ MsgQueue::set_para_id(para_id.into());
+ });
+ ext
+}
+
+pub fn relay_ext() -> sp_io::TestExternalities {
+ use relay_chain::{Runtime, System};
+
+ let mut t = frame_system::GenesisConfig::default()
+ .build_storage::()
+ .unwrap();
+
+ pallet_balances::GenesisConfig:: {
+ balances: vec![
+ (ALICE, INITIAL_BALANCE),
+ (para_account_id(1), INITIAL_BALANCE),
+ ],
+ }
+ .assimilate_storage(&mut t)
+ .unwrap();
+
+ let mut ext = sp_io::TestExternalities::new(t);
+ ext.execute_with(|| System::set_block_number(1));
+ ext
+}
+
+pub type RelayChainPalletXcm = pallet_xcm::Pallet;
+pub type RelayBalances = pallet_balances::Pallet;
+pub type ParachainPalletXcm = pallet_xcm::Pallet;
diff --git a/runtime/dolphin/tests/xcm_mock/parachain.rs b/runtime/dolphin/tests/xcm_mock/parachain.rs
new file mode 100644
index 000000000..e19bf8562
--- /dev/null
+++ b/runtime/dolphin/tests/xcm_mock/parachain.rs
@@ -0,0 +1,637 @@
+// Copyright 2020-2022 Manta Network.
+// This file is part of Manta.
+//
+// Manta 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.
+//
+// Manta 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 Manta. If not, see .
+
+//! Parachain runtime mock.
+
+use codec::{Decode, Encode};
+use frame_support::{
+ construct_runtime, parameter_types,
+ traits::{ConstU32, Everything, Nothing},
+ weights::{constants::WEIGHT_PER_SECOND, Weight},
+};
+use frame_system::EnsureRoot;
+use pallet_asset_manager::AssetMetadata;
+use scale_info::TypeInfo;
+use sp_core::H256;
+use sp_runtime::{
+ testing::Header,
+ traits::{Hash, IdentityLookup},
+ AccountId32,
+};
+use sp_std::{convert::TryFrom, prelude::*};
+
+use manta_primitives::{
+ AssetIdLocationConvert, AssetLocation, FirstAssetTrader, IsNativeConcrete, MultiNativeAsset,
+};
+use pallet_xcm::XcmPassthrough;
+use polkadot_core_primitives::BlockNumber as RelayBlockNumber;
+use polkadot_parachain::primitives::{
+ DmpMessageHandler, Id as ParaId, Sibling, XcmpMessageFormat, XcmpMessageHandler,
+};
+use xcm::{latest::prelude::*, Version as XcmVersion, VersionedXcm};
+use xcm_builder::{
+ AccountId32Aliases, AllowUnpaidExecutionFrom, ConvertedConcreteAssetId,
+ CurrencyAdapter as XcmCurrencyAdapter, EnsureXcmOrigin, FixedRateOfFungible, FixedWeightBounds,
+ FungiblesAdapter, LocationInverter, ParentIsDefault, SiblingParachainAsNative,
+ SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32,
+ SovereignSignedViaLocation,
+};
+use xcm_executor::{traits::JustTry, Config, XcmExecutor};
+use xcm_simulator::Get;
+
+pub use manta_primitives::{AssetId, AssetRegistarMetadata, AssetStorageMetadata};
+pub type AccountId = AccountId32;
+pub type Balance = u128;
+
+parameter_types! {
+ pub const BlockHashCount: u64 = 250;
+}
+
+impl frame_system::Config for Runtime {
+ type Origin = Origin;
+ type Call = Call;
+ type Index = u64;
+ type BlockNumber = u64;
+ type Hash = H256;
+ type Hashing = ::sp_runtime::traits::BlakeTwo256;
+ type AccountId = AccountId;
+ type Lookup = IdentityLookup;
+ type Header = Header;
+ type Event = Event;
+ type BlockHashCount = BlockHashCount;
+ type BlockWeights = ();
+ type BlockLength = ();
+ type Version = ();
+ type PalletInfo = PalletInfo;
+ type AccountData = pallet_balances::AccountData;
+ type OnNewAccount = ();
+ type OnKilledAccount = ();
+ type DbWeight = ();
+ type BaseCallFilter = Everything;
+ type SystemWeightInfo = ();
+ type SS58Prefix = ();
+ type OnSetCode = ();
+ type MaxConsumers = ConstU32<16>;
+}
+
+parameter_types! {
+ pub ExistentialDeposit: Balance = 1;
+ pub const MaxLocks: u32 = 50;
+ pub const MaxReserves: u32 = 50;
+}
+
+impl pallet_balances::Config for Runtime {
+ type MaxLocks = MaxLocks;
+ type Balance = Balance;
+ type Event = Event;
+ type DustRemoval = ();
+ type ExistentialDeposit = ExistentialDeposit;
+ type AccountStore = System;
+ type WeightInfo = ();
+ type MaxReserves = MaxReserves;
+ type ReserveIdentifier = [u8; 8];
+}
+
+parameter_types! {
+ pub const ReservedXcmpWeight: Weight = WEIGHT_PER_SECOND / 4;
+ pub const ReservedDmpWeight: Weight = WEIGHT_PER_SECOND / 4;
+}
+
+parameter_types! {
+ pub const KsmLocation: MultiLocation = MultiLocation::parent();
+ pub const RelayNetwork: NetworkId = NetworkId::Kusama;
+ pub Ancestry: MultiLocation = Parachain(MsgQueue::parachain_id().into()).into();
+ pub SelfReserve: MultiLocation = MultiLocation::new(1, X1(Parachain(ParachainInfo::get().into())));
+}
+
+parameter_types! {
+ pub const AssetDeposit: Balance = 0; // Does not really matter as this will be only called by root
+ pub const AssetAccountDeposit: Balance = 0;
+ pub const ApprovalDeposit: Balance = 0;
+ pub const AssetsStringLimit: u32 = 50;
+ pub const MetadataDepositBase: Balance = 0;
+ pub const MetadataDepositPerByte: Balance = 0;
+}
+
+impl pallet_assets::Config for Runtime {
+ type Event = Event;
+ type Balance = Balance;
+ type AssetId = AssetId;
+ type Currency = Balances;
+ type ForceOrigin = EnsureRoot;
+ type AssetDeposit = AssetDeposit;
+ type AssetAccountDeposit = AssetAccountDeposit;
+ type MetadataDepositBase = MetadataDepositBase;
+ type MetadataDepositPerByte = MetadataDepositPerByte;
+ type ApprovalDeposit = ApprovalDeposit;
+ type StringLimit = AssetsStringLimit;
+ type Freezer = ();
+ type Extra = ();
+ type WeightInfo = pallet_assets::weights::SubstrateWeight;
+}
+
+/// 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`.
+ ParentIsDefault,
+ // Sibling parachain origins convert to AccountId via the `ParaId::into`.
+ SiblingParachainConvertsVia,
+ AccountId32Aliases,
+);
+
+/// This is the type to convert an (incoming) XCM origin into a local `Origin` instance,
+/// ready for dispatching a transaction with Xcm's `Transact`.
+/// It uses some Rust magic macro to do the pattern matching sequentially.
+/// There is an `OriginKind` which can biases the kind of local `Origin` it will become.
+pub type XcmOriginToCallOrigin = (
+ // 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,
+ // If the incoming XCM origin is of type `AccountId32` and the Network is Network::Any
+ // or `RelayNetwork`, convert it to a Native 32 byte account.
+ SignedAccountId32AsNative,
+ // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when
+ // recognised.
+ SiblingParachainAsNative,
+ // Xcm origins can be represented natively under the Xcm pallet's Xcm origin.
+ XcmPassthrough,
+);
+
+parameter_types! {
+ pub const UnitWeightCost: Weight = 1;
+ // Used in native traders
+ // This might be able to skipped.
+ // We have to use `here()` because of reanchoring logic
+ pub ParaTokenPerSecond: (xcm::v1::AssetId, u128) = (Concrete(MultiLocation::here()), 1_000_000_000);
+ pub const MaxInstructions: u32 = 100;
+}
+
+/// Transactor for native currency, i.e. implements `fungible` trait
+pub type LocalAssetTransactor = XcmCurrencyAdapter<
+ // Transacting native currency, i.e. MANTA, KMA, DOL
+ Balances,
+ IsNativeConcrete,
+ LocationToAccountId,
+ AccountId,
+ (),
+>;
+
+/// Transactor for currency in pallet-assets, i.e. implements `fungibles` trait
+pub type FungiblesTransactor = FungiblesAdapter<
+ Assets,
+ ConvertedConcreteAssetId<
+ AssetId,
+ Balance,
+ AssetIdLocationConvert,
+ JustTry,
+ >,
+ // "default" implementation of converting a `MultiLocation` to an `AccountId`
+ LocationToAccountId,
+ AccountId,
+ // No teleport support.
+ Nothing,
+ // No teleport tracking.
+ (),
+>;
+
+pub type XcmRouter = super::ParachainXcmRouter;
+pub type Barrier = AllowUnpaidExecutionFrom;
+
+parameter_types! {
+ /// Xcm fees will go to the asset manager (we don't implement treasury yet)
+ pub XcmFeesAccount: AccountId = AssetManager::account_id();
+}
+
+pub type XcmFeesToAccount = manta_primitives::XcmFeesToAccount<
+ Assets,
+ ConvertedConcreteAssetId<
+ AssetId,
+ Balance,
+ AssetIdLocationConvert,
+ JustTry,
+ >,
+ AccountId,
+ XcmFeesAccount,
+>;
+
+pub struct XcmExecutorConfig;
+impl Config for XcmExecutorConfig {
+ type Call = Call;
+ type XcmSender = XcmRouter;
+ // Defines how to Withdraw and Deposit instruction work
+ // Under the hood, substrate framework will do pattern matching in macro,
+ // as a result, the order of the following tuple matters.
+ type AssetTransactor = (LocalAssetTransactor, FungiblesTransactor);
+ type OriginConverter = XcmOriginToCallOrigin;
+ // Combinations of (Location, Asset) pairs which we trust as reserves.
+ type IsReserve = MultiNativeAsset;
+ type IsTeleporter = ();
+ type LocationInverter = LocationInverter;
+ type Barrier = Barrier;
+ type Weigher = FixedWeightBounds;
+ // Trader is the means to purchasing weight credit for XCM execution.
+ // We define two traders:
+ // The first one will charge parachain's native currency, who's `MultiLocation`
+ // is defined in `SelfReserve`.
+ // The second one will charge the first asset in the MultiAssets with pre-defined rate
+ // i.e. units_per_second in `AssetManager`
+ type Trader = (
+ FixedRateOfFungible,
+ FirstAssetTrader,
+ );
+ type ResponseHandler = PolkadotXcm;
+ type AssetTrap = PolkadotXcm;
+ type AssetClaims = PolkadotXcm;
+ // This is needed for the version change notifier work
+ type SubscriptionService = PolkadotXcm;
+}
+
+// Pallet to provide the version, used to test runtime upgrade version changes
+#[frame_support::pallet]
+pub mod mock_version_changer {
+ use super::*;
+ use frame_support::pallet_prelude::*;
+
+ #[pallet::config]
+ pub trait Config: frame_system::Config {
+ type Event: From> + IsType<::Event>;
+ }
+
+ #[pallet::call]
+ impl Pallet {}
+
+ #[pallet::pallet]
+ #[pallet::generate_store(pub(super) trait Store)]
+ pub struct Pallet(_);
+
+ #[pallet::storage]
+ #[pallet::getter(fn current_version)]
+ pub(super) type CurrentVersion = StorageValue<_, XcmVersion, ValueQuery>;
+
+ impl Get for Pallet {
+ fn get() -> XcmVersion {
+ Self::current_version()
+ }
+ }
+
+ #[pallet::event]
+ #[pallet::generate_deposit(pub(super) fn deposit_event)]
+ pub enum Event {
+ // XCMP
+ /// Some XCM was executed OK.
+ VersionChanged(XcmVersion),
+ }
+
+ impl Pallet {
+ pub fn set_version(version: XcmVersion) {
+ CurrentVersion::::put(version);
+ Self::deposit_event(Event::VersionChanged(version));
+ }
+ }
+}
+
+#[frame_support::pallet]
+pub mod mock_msg_queue {
+ use super::*;
+ use frame_support::pallet_prelude::*;
+
+ #[pallet::config]
+ pub trait Config: frame_system::Config {
+ type Event: From> + IsType<::Event>;
+ type XcmExecutor: ExecuteXcm;
+ }
+
+ #[pallet::call]
+ impl Pallet {}
+
+ // without storage info is a work around
+ #[pallet::pallet]
+ #[pallet::generate_store(pub(super) trait Store)]
+ #[pallet::without_storage_info]
+ pub struct Pallet(_);
+
+ #[pallet::storage]
+ #[pallet::getter(fn parachain_id)]
+ pub(super) type ParachainId = StorageValue<_, ParaId, ValueQuery>;
+
+ #[pallet::storage]
+ #[pallet::getter(fn received_dmp)]
+ /// A queue of received DMP messages
+ pub(super) type ReceivedDmp = StorageValue<_, Vec>, ValueQuery>;
+
+ impl Get for Pallet {
+ fn get() -> ParaId {
+ Self::parachain_id()
+ }
+ }
+
+ pub type MessageId = [u8; 32];
+
+ #[pallet::event]
+ #[pallet::generate_deposit(pub(super) fn deposit_event)]
+ pub enum Event {
+ // XCMP
+ /// Some XCM was executed OK.
+ Success(Option),
+ /// Some XCM failed.
+ Fail(Option, XcmError),
+ /// Bad XCM version used.
+ BadVersion(Option),
+ /// Bad XCM format used.
+ BadFormat(Option),
+
+ // DMP
+ /// Downward message is invalid XCM.
+ InvalidFormat(MessageId),
+ /// Downward message is unsupported version of XCM.
+ UnsupportedVersion(MessageId),
+ /// Downward message executed with the given outcome.
+ ExecutedDownward(MessageId, Outcome),
+ }
+
+ impl Pallet {
+ pub fn set_para_id(para_id: ParaId) {
+ ParachainId::::put(para_id);
+ }
+
+ fn handle_xcmp_message(
+ sender: ParaId,
+ _sent_at: RelayBlockNumber,
+ xcm: VersionedXcm,
+ max_weight: Weight,
+ ) -> Result {
+ let hash = Encode::using_encoded(&xcm, T::Hashing::hash);
+ let (result, event) = match Xcm::::try_from(xcm) {
+ Ok(xcm) => {
+ let location = (1, Parachain(sender.into()));
+ match T::XcmExecutor::execute_xcm(location, xcm, max_weight) {
+ Outcome::Error(e) => (Err(e.clone()), Event::Fail(Some(hash), e)),
+ Outcome::Complete(w) => (Ok(w), Event::Success(Some(hash))),
+ // As far as the caller is concerned, this was dispatched without error, so
+ // we just report the weight used.
+ Outcome::Incomplete(w, e) => (Ok(w), Event::Fail(Some(hash), e)),
+ }
+ }
+ Err(()) => (
+ Err(XcmError::UnhandledXcmVersion),
+ Event::BadVersion(Some(hash)),
+ ),
+ };
+ Self::deposit_event(event);
+ result
+ }
+ }
+
+ impl XcmpMessageHandler for Pallet {
+ fn handle_xcmp_messages<'a, I: Iterator- >(
+ iter: I,
+ max_weight: Weight,
+ ) -> Weight {
+ for (sender, sent_at, data) in iter {
+ let mut data_ref = data;
+ let _ = XcmpMessageFormat::decode(&mut data_ref)
+ .expect("Simulator encodes with versioned xcm format; qed");
+
+ let mut remaining_fragments = &data_ref[..];
+ while !remaining_fragments.is_empty() {
+ if let Ok(xcm) = VersionedXcm::::decode(&mut remaining_fragments) {
+ let _ = Self::handle_xcmp_message(sender, sent_at, xcm, max_weight);
+ } else {
+ debug_assert!(false, "Invalid incoming XCMP message data");
+ }
+ }
+ }
+ max_weight
+ }
+ }
+
+ impl DmpMessageHandler for Pallet {
+ fn handle_dmp_messages(
+ iter: impl Iterator
- )>,
+ limit: Weight,
+ ) -> Weight {
+ for (_i, (_sent_at, data)) in iter.enumerate() {
+ let id = sp_io::hashing::blake2_256(&data[..]);
+ let maybe_msg =
+ VersionedXcm::::decode(&mut &data[..]).map(Xcm::::try_from);
+ match maybe_msg {
+ Err(_) => {
+ Self::deposit_event(Event::InvalidFormat(id));
+ }
+ Ok(Err(())) => {
+ Self::deposit_event(Event::UnsupportedVersion(id));
+ }
+ Ok(Ok(x)) => {
+ let outcome = T::XcmExecutor::execute_xcm(Parent, x.clone(), limit);
+ >::append(x);
+ Self::deposit_event(Event::ExecutedDownward(id, outcome));
+ }
+ }
+ }
+ limit
+ }
+ }
+}
+
+impl mock_msg_queue::Config for Runtime {
+ type Event = Event;
+ type XcmExecutor = XcmExecutor;
+}
+
+impl mock_version_changer::Config for Runtime {
+ type Event = Event;
+}
+
+pub type LocalOriginToLocation = SignedToAccountId32;
+
+impl pallet_xcm::Config for Runtime {
+ type Event = Event;
+ type SendXcmOrigin = EnsureXcmOrigin;
+ type XcmRouter = XcmRouter;
+ type ExecuteXcmOrigin = EnsureXcmOrigin;
+ type XcmExecuteFilter = Everything;
+ type XcmExecutor = XcmExecutor;
+ // Do not allow teleports
+ type XcmTeleportFilter = Nothing;
+ type XcmReserveTransferFilter = Everything;
+ type Weigher = FixedWeightBounds;
+ type LocationInverter = LocationInverter;
+ type Origin = Origin;
+ type Call = Call;
+ const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
+ type AdvertisedXcmVersion = XcmVersioner;
+}
+
+impl AssetMetadata for AssetRegistarMetadata {
+ fn min_balance(&self) -> Balance {
+ self.min_balance
+ }
+
+ fn is_sufficient(&self) -> bool {
+ self.is_sufficient
+ }
+}
+
+pub struct AssetRegistrar;
+use frame_support::pallet_prelude::DispatchResult;
+impl pallet_asset_manager::AssetRegistrar for AssetRegistrar {
+ fn create_asset(
+ asset_id: AssetId,
+ min_balance: Balance,
+ metadata: AssetStorageMetadata,
+ is_sufficient: bool,
+ ) -> DispatchResult {
+ Assets::force_create(
+ Origin::root(),
+ asset_id,
+ AssetManager::account_id(),
+ is_sufficient,
+ min_balance,
+ )?;
+
+ Assets::force_set_metadata(
+ Origin::root(),
+ asset_id,
+ metadata.name,
+ metadata.symbol,
+ metadata.decimals,
+ metadata.is_frozen,
+ )
+ }
+
+ fn update_asset_metadata(asset_id: AssetId, metadata: AssetStorageMetadata) -> DispatchResult {
+ Assets::force_set_metadata(
+ Origin::root(),
+ asset_id,
+ metadata.name,
+ metadata.symbol,
+ metadata.decimals,
+ metadata.is_frozen,
+ )
+ }
+}
+impl pallet_asset_manager::Config for Runtime {
+ type Event = Event;
+ type Balance = Balance;
+ type AssetId = AssetId;
+ type AssetRegistrarMetadata = AssetRegistarMetadata;
+ type StorageMetadata = AssetStorageMetadata;
+ type AssetLocation = AssetLocation;
+ type AssetRegistrar = AssetRegistrar;
+ type ModifierOrigin = EnsureRoot;
+}
+
+impl cumulus_pallet_xcm::Config for Runtime {
+ type Event = Event;
+ type XcmExecutor = XcmExecutor;
+}
+
+// We wrap AssetId for XToken
+#[derive(Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
+pub enum CurrencyId {
+ MantaCurrency(AssetId),
+}
+
+pub struct CurrencyIdtoMultiLocation(sp_std::marker::PhantomData);
+impl sp_runtime::traits::Convert>
+ for CurrencyIdtoMultiLocation
+where
+ AssetXConverter: xcm_executor::traits::Convert,
+{
+ fn convert(currency: CurrencyId) -> Option {
+ match currency {
+ CurrencyId::MantaCurrency(asset_id) => match AssetXConverter::reverse_ref(&asset_id) {
+ Ok(location) => Some(location),
+ Err(_) => None,
+ },
+ }
+ }
+}
+
+parameter_types! {
+ pub const BaseXcmWeight: Weight = 100;
+ pub const MaxAssetsForTransfer: usize = 2;
+}
+
+// The XCM message wrapper wrapper
+impl orml_xtokens::Config for Runtime {
+ type Event = Event;
+ type Balance = Balance;
+ type CurrencyId = CurrencyId;
+ type AccountIdToMultiLocation = manta_primitives::AccountIdToMultiLocation;
+ type CurrencyIdConvert =
+ CurrencyIdtoMultiLocation>;
+ type XcmExecutor = XcmExecutor;
+ type SelfLocation = SelfReserve;
+ type Weigher = xcm_builder::FixedWeightBounds;
+ type BaseXcmWeight = BaseXcmWeight;
+ type LocationInverter = LocationInverter;
+ type MaxAssetsForTransfer = MaxAssetsForTransfer;
+}
+
+impl parachain_info::Config for Runtime {}
+
+type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic;
+type Block = frame_system::mocking::MockBlock;
+
+pub const PALLET_ASSET_INDEX: u8 = 1;
+
+construct_runtime!(
+ pub enum Runtime where
+ Block = Block,
+ NodeBlock = Block,
+ UncheckedExtrinsic = UncheckedExtrinsic,
+ {
+ System: frame_system::{Pallet, Call, Storage, Config, Event} = 0,
+ Assets: pallet_assets::{Pallet, Storage, Event} = 1,
+ AssetManager: pallet_asset_manager::{Pallet, Call, Storage, Event} = 2,
+ Balances: pallet_balances::{Pallet, Call, Storage, Config, Event} = 3,
+ MsgQueue: mock_msg_queue::{Pallet, Storage, Event} = 4,
+ PolkadotXcm: pallet_xcm::{Pallet, Call, Event, Origin} = 5,
+ XTokens: orml_xtokens::{Pallet, Call, Event, Storage} = 6,
+ CumulusXcm: cumulus_pallet_xcm::{Pallet, Event, Origin} = 7,
+ XcmVersioner: mock_version_changer::{Pallet, Storage, Event} = 8,
+ ParachainInfo: parachain_info::{Pallet, Storage, Config} = 9,
+ }
+);
+
+pub(crate) fn para_events() -> Vec {
+ System::events()
+ .into_iter()
+ .map(|r| r.event)
+ .filter_map(|e| Some(e))
+ .collect::>()
+}
+
+use frame_support::traits::{OnFinalize, OnInitialize, OnRuntimeUpgrade};
+pub(crate) fn on_runtime_upgrade() {
+ PolkadotXcm::on_runtime_upgrade();
+}
+
+pub(crate) fn para_roll_to(n: u64) {
+ while System::block_number() < n {
+ PolkadotXcm::on_finalize(System::block_number());
+ Balances::on_finalize(System::block_number());
+ System::on_finalize(System::block_number());
+ System::set_block_number(System::block_number() + 1);
+ System::on_initialize(System::block_number());
+ Balances::on_initialize(System::block_number());
+ PolkadotXcm::on_initialize(System::block_number());
+ }
+}
diff --git a/runtime/dolphin/tests/xcm_mock/relay_chain.rs b/runtime/dolphin/tests/xcm_mock/relay_chain.rs
new file mode 100644
index 000000000..c990e9938
--- /dev/null
+++ b/runtime/dolphin/tests/xcm_mock/relay_chain.rs
@@ -0,0 +1,233 @@
+// Copyright 2020-2022 Manta Network.
+// This file is part of Manta.
+//
+// Manta 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.
+//
+// Manta 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 Manta. If not, see .
+
+//! Relay chain runtime mock.
+
+use frame_support::{
+ construct_runtime, parameter_types,
+ traits::{ConstU32, Everything, Nothing},
+ weights::Weight,
+};
+use sp_core::H256;
+use sp_runtime::{testing::Header, traits::IdentityLookup, AccountId32};
+
+use polkadot_parachain::primitives::Id as ParaId;
+use polkadot_runtime_parachains::{configuration, origin, shared, ump};
+use xcm::latest::prelude::*;
+use xcm_builder::{
+ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom,
+ AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, ChildParachainAsNative,
+ ChildParachainConvertsVia, ChildSystemParachainAsSuperuser,
+ CurrencyAdapter as XcmCurrencyAdapter, FixedRateOfFungible, FixedWeightBounds, IsConcrete,
+ LocationInverter, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation,
+ TakeWeightCredit,
+};
+use xcm_executor::{Config, XcmExecutor};
+pub type AccountId = AccountId32;
+pub type Balance = u128;
+
+parameter_types! {
+ pub const BlockHashCount: u64 = 250;
+}
+
+impl frame_system::Config for Runtime {
+ type Origin = Origin;
+ type Call = Call;
+ type Index = u64;
+ type BlockNumber = u64;
+ type Hash = H256;
+ type Hashing = ::sp_runtime::traits::BlakeTwo256;
+ type AccountId = AccountId;
+ type Lookup = IdentityLookup;
+ type Header = Header;
+ type Event = Event;
+ type BlockHashCount = BlockHashCount;
+ type BlockWeights = ();
+ type BlockLength = ();
+ type Version = ();
+ type PalletInfo = PalletInfo;
+ type AccountData = pallet_balances::AccountData;
+ type OnNewAccount = ();
+ type OnKilledAccount = ();
+ type DbWeight = ();
+ type BaseCallFilter = Everything;
+ type SystemWeightInfo = ();
+ type SS58Prefix = ();
+ type OnSetCode = ();
+ type MaxConsumers = ConstU32<16>;
+}
+
+parameter_types! {
+ pub ExistentialDeposit: Balance = 1;
+ pub const MaxLocks: u32 = 50;
+ pub const MaxReserves: u32 = 50;
+}
+
+impl pallet_balances::Config for Runtime {
+ type MaxLocks = MaxLocks;
+ type Balance = Balance;
+ type Event = Event;
+ type DustRemoval = ();
+ type ExistentialDeposit = ExistentialDeposit;
+ type AccountStore = System;
+ type WeightInfo = ();
+ type MaxReserves = MaxReserves;
+ type ReserveIdentifier = [u8; 8];
+}
+
+impl pallet_utility::Config for Runtime {
+ type Event = Event;
+ type Call = Call;
+ type WeightInfo = ();
+ type PalletsOrigin = OriginCaller;
+}
+
+impl shared::Config for Runtime {}
+
+impl configuration::Config for Runtime {
+ type WeightInfo = configuration::TestWeightInfo;
+}
+
+parameter_types! {
+ pub const KsmLocation: MultiLocation = Here.into();
+ pub const KusamaNetwork: NetworkId = NetworkId::Kusama;
+ pub const AnyNetwork: NetworkId = NetworkId::Any;
+ pub Ancestry: MultiLocation = Here.into();
+ pub UnitWeightCost: Weight = 1_000;
+}
+
+pub type SovereignAccountOf = (
+ ChildParachainConvertsVia,
+ AccountId32Aliases,
+);
+
+pub type LocalAssetTransactor =
+ XcmCurrencyAdapter, SovereignAccountOf, AccountId, ()>;
+
+type LocalOriginConverter = (
+ SovereignSignedViaLocation,
+ ChildParachainAsNative,
+ SignedAccountId32AsNative,
+ ChildSystemParachainAsSuperuser,
+);
+
+parameter_types! {
+ pub const BaseXcmWeight: Weight = 1_000;
+ pub KsmPerSecond: (AssetId, u128) = (Concrete(KsmLocation::get()), 1);
+ pub const MaxInstructions: u32 = 100;
+}
+
+pub type XcmRouter = super::RelayChainXcmRouter;
+pub type Barrier = (
+ TakeWeightCredit,
+ AllowTopLevelPaidExecutionFrom,
+ // Expected responses are OK.
+ AllowKnownQueryResponses,
+ // Subscriptions for version tracking are OK.
+ AllowSubscriptionsFrom,
+ // The following is purely for testing ump
+ AllowUnpaidExecutionFrom,
+);
+
+pub struct XcmExecutorConfig;
+impl Config for XcmExecutorConfig {
+ type Call = Call;
+ type XcmSender = XcmRouter;
+ type AssetTransactor = LocalAssetTransactor;
+ type OriginConverter = LocalOriginConverter;
+ type IsReserve = ();
+ type IsTeleporter = ();
+ type LocationInverter = LocationInverter;
+ type Barrier = Barrier;
+ type Weigher = FixedWeightBounds;
+ type Trader = FixedRateOfFungible;
+ type ResponseHandler = XcmPallet;
+ type AssetTrap = XcmPallet;
+ type AssetClaims = XcmPallet;
+ type SubscriptionService = XcmPallet;
+}
+
+pub type LocalOriginToLocation = SignedToAccountId32;
+
+impl pallet_xcm::Config for Runtime {
+ type Event = Event;
+ type SendXcmOrigin = xcm_builder::EnsureXcmOrigin;
+ type XcmRouter = XcmRouter;
+ // Anyone can execute XCM messages locally...
+ type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin;
+ type XcmExecuteFilter = Nothing;
+ type XcmExecutor = XcmExecutor;
+ type XcmTeleportFilter = Everything;
+ type XcmReserveTransferFilter = Everything;
+ type Weigher = FixedWeightBounds;
+ type LocationInverter = LocationInverter;
+ type Origin = Origin;
+ type Call = Call;
+ const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
+ type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion;
+}
+
+parameter_types! {
+ pub const FirstMessageFactorPercent: u64 = 100;
+}
+
+impl ump::Config for Runtime {
+ type Event = Event;
+ type UmpSink = ump::XcmSink, Runtime>;
+ type FirstMessageFactorPercent = FirstMessageFactorPercent;
+ type ExecuteOverweightOrigin = frame_system::EnsureRoot;
+}
+
+impl origin::Config for Runtime {}
+
+type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic;
+type Block = frame_system::mocking::MockBlock;
+
+construct_runtime!(
+ pub enum Runtime where
+ Block = Block,
+ NodeBlock = Block,
+ UncheckedExtrinsic = UncheckedExtrinsic,
+ {
+ System: frame_system::{Pallet, Call, Storage, Config, Event},
+ Balances: pallet_balances::{Pallet, Call, Storage, Config, Event},
+ ParasOrigin: origin::{Pallet, Origin},
+ ParasUmp: ump::{Pallet, Call, Storage, Event},
+ XcmPallet: pallet_xcm::{Pallet, Call, Storage, Event, Origin},
+ Utility: pallet_utility::{Pallet, Call, Event},
+ }
+);
+
+pub(crate) fn relay_events() -> Vec {
+ System::events()
+ .into_iter()
+ .map(|r| r.event)
+ .filter_map(|e| Some(e))
+ .collect::>()
+}
+
+use frame_support::traits::{OnFinalize, OnInitialize};
+pub(crate) fn relay_roll_to(n: u64) {
+ while System::block_number() < n {
+ XcmPallet::on_finalize(System::block_number());
+ Balances::on_finalize(System::block_number());
+ System::on_finalize(System::block_number());
+ System::set_block_number(System::block_number() + 1);
+ System::on_initialize(System::block_number());
+ Balances::on_initialize(System::block_number());
+ XcmPallet::on_initialize(System::block_number());
+ }
+}
diff --git a/runtime/dolphin/tests/xcm_tests.rs b/runtime/dolphin/tests/xcm_tests.rs
new file mode 100644
index 000000000..3ed0a5d0f
--- /dev/null
+++ b/runtime/dolphin/tests/xcm_tests.rs
@@ -0,0 +1,1961 @@
+// Copyright 2020-2022 Manta Network.
+// This file is part of Manta.
+//
+// Manta 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.
+//
+// Manta 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 Manta. If not, see .
+
+//! Simulation Tests for XCM
+
+mod xcm_mock;
+
+use codec::Encode;
+use frame_support::{assert_ok, weights::constants::WEIGHT_PER_SECOND};
+use manta_primitives::AssetLocation;
+use xcm::{latest::prelude::*, v2::Response, VersionedMultiLocation, WrapVersion};
+use xcm_mock::{parachain::PALLET_ASSET_INDEX, *};
+use xcm_simulator::TestExt;
+
+use crate::xcm_mock::parachain::{AssetManager, ParaTokenPerSecond};
+
+// `reserved_transfer_asset` contains the following 4 instructions
+// 1. ReserveAssetDeposited(assets.clone()),
+// 2. ClearOrigin,
+// 3. BuyExecution { fees, weight_limit: Limited(0) },
+// 4. DepositAsset { assets: Wild(All), max_assets, beneficiary },
+// each instruction's weight is 1000, thus, the total weight is 4000
+const RESERVE_TRANSFER_WEIGHT: u64 = 4000;
+
+fn calculate_fee(units_per_seconds: u128, weight: u64) -> u128 {
+ units_per_seconds * (weight as u128) / (WEIGHT_PER_SECOND as u128)
+}
+
+// Helper function for forming buy execution message
+fn buy_execution(fees: impl Into) -> Instruction {
+ BuyExecution {
+ fees: fees.into(),
+ weight_limit: Unlimited,
+ }
+}
+
+#[test]
+fn dmp() {
+ MockNet::reset();
+
+ let remark = parachain::Call::System(
+ frame_system::Call::::remark_with_event {
+ remark: vec![1, 2, 3],
+ },
+ );
+ Relay::execute_with(|| {
+ assert_ok!(RelayChainPalletXcm::send_xcm(
+ Here,
+ Parachain(1),
+ Xcm(vec![Transact {
+ origin_type: OriginKind::SovereignAccount,
+ require_weight_at_most: INITIAL_BALANCE as u64,
+ call: remark.encode().into(),
+ }]),
+ ));
+ });
+
+ ParaA::execute_with(|| {
+ use parachain::{Event, System};
+ assert!(System::events()
+ .iter()
+ .any(|r| matches!(r.event, Event::System(frame_system::Event::Remarked { .. }))));
+ });
+}
+
+#[test]
+fn ump() {
+ MockNet::reset();
+
+ let remark = relay_chain::Call::System(
+ frame_system::Call::::remark_with_event {
+ remark: vec![1, 2, 3],
+ },
+ );
+ ParaA::execute_with(|| {
+ assert_ok!(ParachainPalletXcm::send_xcm(
+ Here,
+ Parent,
+ Xcm(vec![Transact {
+ origin_type: OriginKind::SovereignAccount,
+ require_weight_at_most: INITIAL_BALANCE as u64,
+ call: remark.encode().into(),
+ }]),
+ ));
+ });
+
+ Relay::execute_with(|| {
+ use relay_chain::{Event, System};
+ assert!(System::events()
+ .iter()
+ .any(|r| matches!(r.event, Event::System(frame_system::Event::Remarked { .. }))));
+ });
+}
+
+#[test]
+fn xcmp() {
+ MockNet::reset();
+
+ let remark = parachain::Call::System(
+ frame_system::Call::::remark_with_event {
+ remark: vec![1, 2, 3],
+ },
+ );
+ ParaA::execute_with(|| {
+ assert_ok!(ParachainPalletXcm::send_xcm(
+ Here,
+ (Parent, Parachain(2)),
+ Xcm(vec![Transact {
+ origin_type: OriginKind::SovereignAccount,
+ require_weight_at_most: INITIAL_BALANCE as u64,
+ call: remark.encode().into(),
+ }]),
+ ));
+ });
+
+ ParaB::execute_with(|| {
+ use parachain::{Event, System};
+ assert!(System::events()
+ .iter()
+ .any(|r| matches!(r.event, Event::System(frame_system::Event::Remarked { .. }))));
+ });
+}
+
+#[test]
+fn reserve_transfer_relaychain_to_parachain_a() {
+ MockNet::reset();
+
+ let relay_asset_id: parachain::AssetId = 0;
+ let source_location = AssetLocation(VersionedMultiLocation::V1(MultiLocation::parent()));
+
+ let asset_metadata = parachain::AssetRegistarMetadata {
+ name: b"Kusama".to_vec(),
+ symbol: b"KSM".to_vec(),
+ decimals: 12,
+ min_balance: 1u128,
+ evm_address: None,
+ is_frozen: false,
+ is_sufficient: true,
+ };
+
+ // Register relay chain asset in parachain A
+ ParaA::execute_with(|| {
+ assert_ok!(parachain::AssetManager::register_asset(
+ parachain::Origin::root(),
+ source_location,
+ asset_metadata
+ ));
+ // we don't charge anything during test
+ assert_ok!(parachain::AssetManager::set_units_per_second(
+ parachain::Origin::root(),
+ relay_asset_id,
+ 0u128
+ ));
+ });
+
+ let withdraw_amount = 123;
+
+ Relay::execute_with(|| {
+ assert_ok!(RelayChainPalletXcm::reserve_transfer_assets(
+ relay_chain::Origin::signed(ALICE),
+ Box::new(X1(Parachain(1)).into().into()),
+ Box::new(
+ X1(AccountId32 {
+ network: Any,
+ id: ALICE.into()
+ })
+ .into()
+ .into()
+ ),
+ Box::new((Here, withdraw_amount).into()),
+ 0,
+ ));
+ assert_eq!(
+ relay_chain::Balances::free_balance(¶_account_id(1)),
+ INITIAL_BALANCE + withdraw_amount
+ );
+ });
+
+ ParaA::execute_with(|| {
+ // free execution, full amount received
+ assert_eq!(
+ pallet_assets::Pallet::::balance(relay_asset_id, &ALICE.into()),
+ withdraw_amount
+ );
+ });
+}
+
+#[test]
+fn reserve_transfer_relaychain_to_parachain_a_then_back() {
+ MockNet::reset();
+
+ let relay_asset_id: parachain::AssetId = 0;
+ let source_location = AssetLocation(VersionedMultiLocation::V1(MultiLocation::parent()));
+
+ let asset_metadata = parachain::AssetRegistarMetadata {
+ name: b"Kusama".to_vec(),
+ symbol: b"KSM".to_vec(),
+ decimals: 12,
+ min_balance: 1u128,
+ evm_address: None,
+ is_frozen: false,
+ is_sufficient: true,
+ };
+
+ // Register relay chain asset in parachain A
+ ParaA::execute_with(|| {
+ assert_ok!(parachain::AssetManager::register_asset(
+ parachain::Origin::root(),
+ source_location,
+ asset_metadata
+ ));
+ // we don't charge anything
+ assert_ok!(parachain::AssetManager::set_units_per_second(
+ parachain::Origin::root(),
+ relay_asset_id,
+ 0u128
+ ));
+ });
+
+ let amount = 123;
+
+ Relay::execute_with(|| {
+ assert_ok!(RelayChainPalletXcm::reserve_transfer_assets(
+ relay_chain::Origin::signed(ALICE),
+ Box::new(X1(Parachain(1)).into().into()),
+ Box::new(
+ X1(AccountId32 {
+ network: Any,
+ id: ALICE.into()
+ })
+ .into()
+ .into()
+ ),
+ Box::new((Here, amount).into()),
+ 0,
+ ));
+ assert_eq!(
+ parachain::Balances::free_balance(¶_account_id(1)),
+ INITIAL_BALANCE + amount
+ );
+ });
+
+ ParaA::execute_with(|| {
+ // free execution, full amount received
+ assert_eq!(
+ pallet_assets::Pallet::::balance(relay_asset_id, &ALICE.into()),
+ amount
+ );
+ });
+
+ // Checking the balance of relay chain before sending token back
+ let mut balance_before_sending = 0;
+ Relay::execute_with(|| {
+ balance_before_sending = RelayBalances::free_balance(&ALICE);
+ });
+
+ let dest = MultiLocation {
+ parents: 1,
+ interior: X1(AccountId32 {
+ network: NetworkId::Any,
+ id: ALICE.into(),
+ }),
+ };
+
+ ParaA::execute_with(|| {
+ // free execution, full amount received
+ assert_ok!(parachain::XTokens::transfer(
+ parachain::Origin::signed(ALICE.into()),
+ parachain::CurrencyId::MantaCurrency(relay_asset_id),
+ amount,
+ Box::new(VersionedMultiLocation::V1(dest)),
+ 40000
+ ));
+ });
+
+ ParaA::execute_with(|| {
+ // free execution, this will drain the parachain asset account
+ assert_eq!(parachain::Assets::balance(relay_asset_id, &ALICE.into()), 0);
+ });
+
+ Relay::execute_with(|| {
+ // free execution, full amount received
+ assert_eq!(
+ RelayBalances::free_balance(&ALICE),
+ balance_before_sending + amount
+ );
+ });
+}
+
+#[test]
+fn send_para_a_native_asset_to_para_b() {
+ MockNet::reset();
+
+ // We use an opinioned source location here:
+ // Ideally, we could use `here()`, however, we always prefer to use the location from
+ // `root` when possible.
+ let source_location = AssetLocation(VersionedMultiLocation::V1(MultiLocation::new(
+ 1,
+ X1(Parachain(1)),
+ )));
+ let a_currency_id = 0u32;
+ let amount = 100u128;
+
+ let asset_metadata = parachain::AssetRegistarMetadata {
+ name: b"ParaAToken".to_vec(),
+ symbol: b"ParaA".to_vec(),
+ decimals: 18,
+ evm_address: None,
+ min_balance: 1,
+ is_frozen: false,
+ is_sufficient: false,
+ };
+
+ // Register ParaA native asset in ParaB
+ ParaB::execute_with(|| {
+ assert_ok!(AssetManager::register_asset(
+ parachain::Origin::root(),
+ source_location.clone(),
+ asset_metadata.clone()
+ ));
+ assert_ok!(AssetManager::set_units_per_second(
+ parachain::Origin::root(),
+ a_currency_id,
+ 0u128
+ ));
+ assert_eq!(
+ Some(a_currency_id),
+ AssetManager::location_asset_id(source_location.clone())
+ );
+ });
+
+ // Register ParaA native asset in ParaA
+ ParaA::execute_with(|| {
+ assert_ok!(AssetManager::register_asset(
+ parachain::Origin::root(),
+ source_location.clone(),
+ asset_metadata
+ ));
+ assert_ok!(AssetManager::set_units_per_second(
+ parachain::Origin::root(),
+ a_currency_id,
+ 0u128
+ ));
+ assert_eq!(
+ Some(a_currency_id),
+ AssetManager::location_asset_id(source_location)
+ );
+ });
+
+ let dest = MultiLocation {
+ parents: 1,
+ interior: X2(
+ Parachain(2),
+ AccountId32 {
+ network: NetworkId::Any,
+ id: ALICE.into(),
+ },
+ ),
+ };
+
+ // Transfer ParaA balance to B
+ ParaA::execute_with(|| {
+ assert_ok!(parachain::XTokens::transfer(
+ parachain::Origin::signed(ALICE.into()),
+ parachain::CurrencyId::MantaCurrency(a_currency_id),
+ amount,
+ Box::new(VersionedMultiLocation::V1(dest)),
+ 800000
+ ));
+ assert_eq!(
+ parachain::Balances::free_balance(&ALICE.into()),
+ INITIAL_BALANCE - amount
+ )
+ });
+
+ // Make sure B received the token
+ ParaB::execute_with(|| {
+ // free execution, full amount received
+ assert_eq!(
+ parachain::Assets::balance(a_currency_id, &ALICE.into()),
+ amount
+ );
+ });
+}
+
+#[test]
+fn send_para_a_custom_asset_to_para_b() {
+ let a_currency_id: u32 = 0;
+ let amount = 321;
+ let asset_metadata = parachain::AssetRegistarMetadata {
+ name: b"ParaADoge".to_vec(),
+ symbol: b"Doge".to_vec(),
+ decimals: 18,
+ evm_address: None,
+ min_balance: 1,
+ is_frozen: false,
+ is_sufficient: true,
+ };
+
+ let source_location = AssetLocation(VersionedMultiLocation::V1(MultiLocation::new(
+ 1,
+ X3(
+ Parachain(1),
+ PalletInstance(PALLET_ASSET_INDEX),
+ GeneralIndex(0),
+ ),
+ )));
+
+ // register a_currency in ParaA, ParaB
+ ParaA::execute_with(|| {
+ assert_ok!(parachain::AssetManager::register_asset(
+ parachain::Origin::root(),
+ source_location.clone(),
+ asset_metadata.clone()
+ ));
+ // we have to do this in order to mint asset to alice on A
+ assert_ok!(parachain::Assets::force_asset_status(
+ parachain::Origin::root(),
+ 0,
+ ALICE.into(),
+ ALICE.into(),
+ ALICE.into(),
+ ALICE.into(),
+ 1,
+ true,
+ false,
+ ));
+ assert_ok!(AssetManager::set_units_per_second(
+ parachain::Origin::root(),
+ a_currency_id,
+ 0u128
+ ));
+ assert_eq!(
+ Some(a_currency_id),
+ parachain::AssetManager::location_asset_id(source_location.clone())
+ );
+ });
+
+ ParaB::execute_with(|| {
+ assert_ok!(parachain::AssetManager::register_asset(
+ parachain::Origin::root(),
+ source_location.clone(),
+ asset_metadata.clone()
+ ));
+ assert_ok!(AssetManager::set_units_per_second(
+ parachain::Origin::root(),
+ a_currency_id,
+ 0u128
+ ));
+ assert_eq!(
+ Some(a_currency_id),
+ parachain::AssetManager::location_asset_id(source_location)
+ );
+ });
+
+ let alice_on_b = MultiLocation {
+ parents: 1,
+ interior: X2(
+ Parachain(2),
+ AccountId32 {
+ network: NetworkId::Any,
+ id: ALICE.into(),
+ },
+ ),
+ };
+
+ ParaA::execute_with(|| {
+ // Force customized asset balance for Alice
+ assert_ok!(parachain::Assets::mint(
+ parachain::Origin::signed(ALICE.into()),
+ 0,
+ ALICE.into(),
+ INITIAL_BALANCE
+ ));
+ assert_ok!(parachain::XTokens::transfer(
+ parachain::Origin::signed(ALICE.into()),
+ parachain::CurrencyId::MantaCurrency(a_currency_id),
+ amount,
+ Box::new(VersionedMultiLocation::V1(alice_on_b)),
+ 800000
+ ));
+ assert_eq!(
+ parachain::Assets::balance(a_currency_id, &ALICE.into()),
+ INITIAL_BALANCE - amount
+ )
+ });
+
+ // Make sure B received the token
+ ParaB::execute_with(|| {
+ // free execution, full amount received
+ assert_eq!(
+ parachain::Assets::balance(a_currency_id, &ALICE.into()),
+ amount
+ );
+ });
+}
+
+#[test]
+fn send_para_a_native_asset_para_b_and_then_send_back() {
+ MockNet::reset();
+
+ // para a native asset location
+ let source_location = AssetLocation(VersionedMultiLocation::V1(MultiLocation::new(
+ 1,
+ X1(Parachain(1)),
+ )));
+ // a's currency id in para a, para b, and para c
+ let a_currency_id = 0u32;
+ let amount = 5000u128;
+ let weight = 800000u64;
+ let fee_on_b_when_send_back = calculate_fee(ParaTokenPerSecond::get().1, weight);
+ assert!(fee_on_b_when_send_back < amount);
+
+ let asset_metadata = parachain::AssetRegistarMetadata {
+ name: b"ParaAToken".to_vec(),
+ symbol: b"ParaA".to_vec(),
+ decimals: 18,
+ evm_address: None,
+ min_balance: 1,
+ is_frozen: false,
+ is_sufficient: true,
+ };
+
+ // register a_currency in ParaA, ParaB
+ ParaA::execute_with(|| {
+ assert_ok!(parachain::AssetManager::register_asset(
+ parachain::Origin::root(),
+ source_location.clone(),
+ asset_metadata.clone()
+ ));
+ assert_ok!(AssetManager::set_units_per_second(
+ parachain::Origin::root(),
+ a_currency_id,
+ 0u128
+ ));
+ assert_eq!(
+ Some(a_currency_id),
+ parachain::AssetManager::location_asset_id(source_location.clone())
+ );
+ });
+
+ ParaB::execute_with(|| {
+ assert_ok!(parachain::AssetManager::register_asset(
+ parachain::Origin::root(),
+ source_location.clone(),
+ asset_metadata.clone()
+ ));
+ assert_ok!(AssetManager::set_units_per_second(
+ parachain::Origin::root(),
+ a_currency_id,
+ 0u128
+ ));
+ assert_eq!(
+ Some(a_currency_id),
+ parachain::AssetManager::location_asset_id(source_location)
+ );
+ });
+
+ let alice_on_b = MultiLocation {
+ parents: 1,
+ interior: X2(
+ Parachain(2),
+ AccountId32 {
+ network: NetworkId::Any,
+ id: ALICE.into(),
+ },
+ ),
+ };
+
+ ParaA::execute_with(|| {
+ assert_ok!(parachain::XTokens::transfer(
+ parachain::Origin::signed(ALICE.into()),
+ parachain::CurrencyId::MantaCurrency(a_currency_id),
+ amount,
+ Box::new(VersionedMultiLocation::V1(alice_on_b)),
+ 800000
+ ));
+ assert_eq!(
+ parachain::Balances::free_balance(&ALICE.into()),
+ INITIAL_BALANCE - amount
+ )
+ });
+
+ // Make sure B received the token
+ ParaB::execute_with(|| {
+ // free execution, full amount received
+ assert_eq!(
+ parachain::Assets::balance(a_currency_id, &ALICE.into()),
+ amount
+ );
+ });
+
+ let alice_on_a = MultiLocation {
+ parents: 1,
+ interior: X2(
+ Parachain(1),
+ AccountId32 {
+ network: NetworkId::Any,
+ id: ALICE.into(),
+ },
+ ),
+ };
+
+ // Send wrapped a back to a
+ ParaB::execute_with(|| {
+ assert_ok!(parachain::XTokens::transfer(
+ parachain::Origin::signed(ALICE.into()),
+ parachain::CurrencyId::MantaCurrency(a_currency_id),
+ amount,
+ Box::new(VersionedMultiLocation::V1(alice_on_a)),
+ 800000
+ ));
+ assert_eq!(parachain::Assets::balance(a_currency_id, &ALICE.into()), 0);
+ });
+
+ // make sure that a received the token
+ ParaA::execute_with(|| {
+ assert_eq!(
+ parachain::Balances::free_balance(&ALICE.into()),
+ INITIAL_BALANCE - fee_on_b_when_send_back
+ )
+ });
+}
+
+#[test]
+fn send_para_a_native_asset_from_para_b_to_para_c() {
+ MockNet::reset();
+
+ // para a asset location
+ let source_location = AssetLocation(VersionedMultiLocation::V1(MultiLocation::new(
+ 1,
+ X1(Parachain(1)),
+ )));
+ let a_currency_id = 0u32;
+ let amount = 8888u128;
+ let weight = 800_000u64;
+ let fee_at_reserve = calculate_fee(ParaTokenPerSecond::get().1, weight);
+ assert!(amount >= fee_at_reserve * 2 as u128);
+
+ let asset_metadata = parachain::AssetRegistarMetadata {
+ name: b"ParaAToken".to_vec(),
+ symbol: b"ParaA".to_vec(),
+ decimals: 18,
+ evm_address: None,
+ min_balance: 1,
+ is_frozen: false,
+ is_sufficient: false,
+ };
+
+ // register a_currency in ParaA, ParaB and ParaC
+ ParaA::execute_with(|| {
+ assert_ok!(parachain::AssetManager::register_asset(
+ parachain::Origin::root(),
+ // we need to change this on/after v0.9.16
+ source_location.clone(),
+ asset_metadata.clone()
+ ));
+ assert_ok!(AssetManager::set_units_per_second(
+ parachain::Origin::root(),
+ a_currency_id,
+ 0u128
+ ));
+ assert_eq!(
+ Some(a_currency_id),
+ parachain::AssetManager::location_asset_id(source_location.clone())
+ );
+ });
+
+ ParaB::execute_with(|| {
+ assert_ok!(parachain::AssetManager::register_asset(
+ parachain::Origin::root(),
+ source_location.clone(),
+ asset_metadata.clone()
+ ));
+ assert_ok!(AssetManager::set_units_per_second(
+ parachain::Origin::root(),
+ a_currency_id,
+ 0u128
+ ));
+ assert_eq!(
+ Some(a_currency_id),
+ parachain::AssetManager::location_asset_id(source_location.clone())
+ );
+ });
+
+ ParaC::execute_with(|| {
+ assert_ok!(parachain::AssetManager::register_asset(
+ parachain::Origin::root(),
+ source_location.clone(),
+ asset_metadata.clone()
+ ));
+ assert_ok!(AssetManager::set_units_per_second(
+ parachain::Origin::root(),
+ a_currency_id,
+ 0u128
+ ));
+ assert_eq!(
+ Some(a_currency_id),
+ parachain::AssetManager::location_asset_id(source_location.clone())
+ );
+ });
+
+ // A send B some token
+ let alice_on_b = MultiLocation {
+ parents: 1,
+ interior: X2(
+ Parachain(2),
+ AccountId32 {
+ network: NetworkId::Any,
+ id: ALICE.into(),
+ },
+ ),
+ };
+
+ ParaA::execute_with(|| {
+ assert_ok!(parachain::XTokens::transfer(
+ parachain::Origin::signed(ALICE.into()),
+ parachain::CurrencyId::MantaCurrency(a_currency_id),
+ amount,
+ Box::new(VersionedMultiLocation::V1(alice_on_b.clone())),
+ 800000
+ ));
+ assert_eq!(
+ parachain::Balances::free_balance(&ALICE.into()),
+ INITIAL_BALANCE - amount
+ )
+ });
+
+ ParaB::execute_with(|| {
+ // free execution, full amount received
+ assert_eq!(
+ parachain::Assets::balance(a_currency_id, &ALICE.into()),
+ amount
+ );
+ });
+
+ // B send C para A asset
+ let alice_on_c = MultiLocation {
+ parents: 1,
+ interior: X2(
+ Parachain(3),
+ AccountId32 {
+ network: NetworkId::Any,
+ id: ALICE.into(),
+ },
+ ),
+ };
+
+ ParaB::execute_with(|| {
+ assert_ok!(parachain::XTokens::transfer(
+ parachain::Origin::signed(ALICE.into()),
+ parachain::CurrencyId::MantaCurrency(a_currency_id),
+ amount,
+ Box::new(VersionedMultiLocation::V1(alice_on_c)),
+ weight,
+ ));
+ assert_eq!(parachain::Assets::balance(a_currency_id, &ALICE.into()), 0);
+ });
+
+ // Make sure C received the token
+ ParaC::execute_with(|| {
+ // free execution, full amount received
+ assert_eq!(
+ parachain::Assets::balance(a_currency_id, &ALICE.into()),
+ amount - fee_at_reserve
+ );
+ });
+}
+
+#[test]
+fn receive_relay_asset_with_trader() {
+ MockNet::reset();
+
+ let relay_asset_id: parachain::AssetId = 0;
+ let source_location = AssetLocation(VersionedMultiLocation::V1(MultiLocation::parent()));
+ let asset_metadata = parachain::AssetRegistarMetadata {
+ name: b"Kusama".to_vec(),
+ symbol: b"KSM".to_vec(),
+ decimals: 12,
+ min_balance: 1u128,
+ evm_address: None,
+ is_frozen: false,
+ is_sufficient: true,
+ };
+ let amount = 666u128;
+ // We charge 10^9 as units per second on ParaA
+ let units_per_second = 1_000_000_000u128;
+ let fee = calculate_fee(units_per_second, RESERVE_TRANSFER_WEIGHT);
+ assert!(fee > 0);
+
+ ParaA::execute_with(|| {
+ assert_ok!(parachain::AssetManager::register_asset(
+ parachain::Origin::root(),
+ source_location,
+ asset_metadata
+ ));
+ assert_ok!(parachain::AssetManager::set_units_per_second(
+ parachain::Origin::root(),
+ relay_asset_id,
+ units_per_second
+ ));
+ });
+
+ let dest: MultiLocation = AccountId32 {
+ network: Any,
+ id: ALICE.into(),
+ }
+ .into();
+
+ Relay::execute_with(|| {
+ assert_ok!(RelayChainPalletXcm::reserve_transfer_assets(
+ relay_chain::Origin::signed(ALICE),
+ Box::new(X1(Parachain(1)).into().into()),
+ Box::new(VersionedMultiLocation::V1(dest).clone().into()),
+ Box::new((Here, amount).into()),
+ 0,
+ ));
+ assert_eq!(
+ relay_chain::Balances::free_balance(¶_account_id(1)),
+ INITIAL_BALANCE + amount
+ );
+ });
+
+ ParaA::execute_with(|| {
+ // ALICE gets amount - fee
+ assert_eq!(
+ parachain::Assets::balance(relay_asset_id, &ALICE.into()),
+ amount - fee
+ );
+ // Fee sink gets fee
+ assert_eq!(
+ parachain::Assets::balance(relay_asset_id, AssetManager::account_id()),
+ fee
+ );
+ });
+}
+
+#[test]
+fn send_para_a_asset_to_para_b_with_trader_and_fee() {
+ MockNet::reset();
+
+ // para a balance location
+ let source_location = AssetLocation(VersionedMultiLocation::V1(MultiLocation::new(
+ 1,
+ X1(Parachain(1)),
+ )));
+ let a_currency_id = 0u32;
+ let amount = 222u128;
+ let units_per_second = 1_250_000u128;
+ let dest_weight = 800_000u64;
+ let fee = calculate_fee(units_per_second, dest_weight);
+
+ let asset_metadata = parachain::AssetRegistarMetadata {
+ name: b"ParaAToken".to_vec(),
+ symbol: b"ParaA".to_vec(),
+ decimals: 18,
+ evm_address: None,
+ min_balance: 1,
+ is_frozen: false,
+ is_sufficient: true,
+ };
+
+ // Register ParaA native asset in ParaA
+ ParaA::execute_with(|| {
+ assert_ok!(AssetManager::register_asset(
+ parachain::Origin::root(),
+ // This need to be changed starting from v0.9.16
+ // need to use something like MultiLocation { parents: 0, interior: here} instead
+ source_location.clone(),
+ asset_metadata.clone()
+ ));
+ assert_ok!(AssetManager::set_units_per_second(
+ parachain::Origin::root(),
+ a_currency_id,
+ 0u128
+ ));
+ assert_eq!(
+ Some(a_currency_id),
+ AssetManager::location_asset_id(source_location.clone())
+ );
+ });
+
+ ParaB::execute_with(|| {
+ assert_ok!(AssetManager::register_asset(
+ parachain::Origin::root(),
+ source_location.clone(),
+ asset_metadata.clone()
+ ));
+ assert_ok!(AssetManager::set_units_per_second(
+ parachain::Origin::root(),
+ a_currency_id,
+ units_per_second
+ ));
+ assert_eq!(
+ Some(a_currency_id),
+ AssetManager::location_asset_id(source_location.clone())
+ );
+ });
+
+ let dest = MultiLocation {
+ parents: 1,
+ interior: X2(
+ Parachain(2),
+ AccountId32 {
+ network: NetworkId::Any,
+ id: ALICE.into(),
+ },
+ ),
+ };
+
+ // Transfer ParaA balance to B
+ ParaA::execute_with(|| {
+ assert_ok!(parachain::XTokens::transfer_with_fee(
+ parachain::Origin::signed(ALICE.into()),
+ parachain::CurrencyId::MantaCurrency(a_currency_id),
+ amount,
+ 1,
+ Box::new(VersionedMultiLocation::V1(dest)),
+ dest_weight,
+ ));
+ assert_eq!(
+ parachain::Balances::free_balance(&ALICE.into()),
+ INITIAL_BALANCE - amount - fee
+ )
+ });
+
+ ParaB::execute_with(|| {
+ assert_eq!(
+ parachain::Assets::balance(a_currency_id, &ALICE.into()),
+ amount
+ );
+ });
+}
+
+#[test]
+fn send_para_a_asset_from_para_b_to_para_c_with_trader() {
+ MockNet::reset();
+
+ // para a balance location
+ let source_location = AssetLocation(VersionedMultiLocation::V1(MultiLocation::new(
+ 1,
+ X1(Parachain(1)),
+ )));
+ let a_currency_id = 0u32;
+ let mut amount = 8888u128;
+ let units_per_second_at_b = 1_250_000u128;
+ let dest_weight = 800_000u64;
+ let fee_at_b = calculate_fee(units_per_second_at_b, dest_weight);
+ let fee_at_a = calculate_fee(ParaTokenPerSecond::get().1, dest_weight);
+ let asset_metadata = parachain::AssetRegistarMetadata {
+ name: b"ParaAToken".to_vec(),
+ symbol: b"ParaA".to_vec(),
+ decimals: 18,
+ evm_address: None,
+ min_balance: 1,
+ is_frozen: false,
+ is_sufficient: true,
+ };
+
+ // register a_currency in ParaA, ParaB and ParaC
+
+ // we don't charge any fee in A
+ ParaA::execute_with(|| {
+ assert_ok!(parachain::AssetManager::register_asset(
+ parachain::Origin::root(),
+ source_location.clone(),
+ asset_metadata.clone()
+ ));
+ assert_ok!(AssetManager::set_units_per_second(
+ parachain::Origin::root(),
+ a_currency_id,
+ 0u128
+ ));
+ assert_eq!(
+ Some(a_currency_id),
+ parachain::AssetManager::location_asset_id(source_location.clone())
+ );
+ });
+
+ // We set units_per_seconds on ParaB to 1_250_000,
+ ParaB::execute_with(|| {
+ assert_ok!(parachain::AssetManager::register_asset(
+ parachain::Origin::root(),
+ source_location.clone(),
+ asset_metadata.clone()
+ ));
+ assert_ok!(AssetManager::set_units_per_second(
+ parachain::Origin::root(),
+ a_currency_id,
+ units_per_second_at_b
+ ));
+ assert_eq!(
+ Some(a_currency_id),
+ parachain::AssetManager::location_asset_id(source_location.clone())
+ );
+ });
+
+ ParaC::execute_with(|| {
+ assert_ok!(parachain::AssetManager::register_asset(
+ parachain::Origin::root(),
+ source_location.clone(),
+ asset_metadata.clone()
+ ));
+ assert_ok!(AssetManager::set_units_per_second(
+ parachain::Origin::root(),
+ a_currency_id,
+ units_per_second_at_b
+ ));
+ assert_eq!(
+ Some(a_currency_id),
+ parachain::AssetManager::location_asset_id(source_location.clone())
+ );
+ });
+
+ // A send B some token
+ let alice_on_b = MultiLocation {
+ parents: 1,
+ interior: X2(
+ Parachain(2),
+ AccountId32 {
+ network: NetworkId::Any,
+ id: ALICE.into(),
+ },
+ ),
+ };
+
+ assert!(amount >= fee_at_b);
+ ParaA::execute_with(|| {
+ assert_ok!(parachain::XTokens::transfer(
+ parachain::Origin::signed(ALICE.into()),
+ parachain::CurrencyId::MantaCurrency(a_currency_id),
+ amount,
+ Box::new(VersionedMultiLocation::V1(alice_on_b.clone())),
+ dest_weight
+ ));
+ assert_eq!(
+ parachain::Balances::free_balance(&ALICE.into()),
+ INITIAL_BALANCE - amount
+ )
+ });
+
+ ParaB::execute_with(|| {
+ amount = amount - fee_at_b;
+ assert_eq!(
+ parachain::Assets::balance(a_currency_id, &ALICE.into()),
+ amount
+ );
+ });
+
+ // B send C para A asset
+ let alice_on_c = MultiLocation {
+ parents: 1,
+ interior: X2(
+ Parachain(3),
+ AccountId32 {
+ network: NetworkId::Any,
+ id: ALICE.into(),
+ },
+ ),
+ };
+
+ assert!(amount >= fee_at_b + fee_at_a);
+ ParaB::execute_with(|| {
+ assert_ok!(parachain::XTokens::transfer(
+ parachain::Origin::signed(ALICE.into()),
+ parachain::CurrencyId::MantaCurrency(a_currency_id),
+ amount,
+ Box::new(VersionedMultiLocation::V1(alice_on_c)),
+ dest_weight
+ ));
+ assert_eq!(parachain::Assets::balance(a_currency_id, &ALICE.into()), 0);
+ });
+
+ // Make sure C received the token
+ ParaC::execute_with(|| {
+ amount = amount - fee_at_b - fee_at_a;
+ assert_eq!(
+ parachain::Assets::balance(a_currency_id, &ALICE.into()),
+ amount
+ );
+ });
+}
+
+#[test]
+fn receive_relay_with_insufficient_fee_payment() {
+ MockNet::reset();
+
+ let relay_asset_id: parachain::AssetId = 0;
+ let source_location = AssetLocation(VersionedMultiLocation::V1(MultiLocation::parent()));
+ let asset_metadata = parachain::AssetRegistarMetadata {
+ name: b"Kusama".to_vec(),
+ symbol: b"KSM".to_vec(),
+ decimals: 12,
+ min_balance: 1u128,
+ evm_address: None,
+ is_frozen: false,
+ is_sufficient: true,
+ };
+ let amount = 20u128;
+ // We charge 2 x 10^10 as units per second on ParaA
+ let units_per_second = 20_000_000_000u128;
+ let fee = calculate_fee(units_per_second, RESERVE_TRANSFER_WEIGHT);
+ assert!(fee > amount);
+
+ ParaA::execute_with(|| {
+ assert_ok!(parachain::AssetManager::register_asset(
+ parachain::Origin::root(),
+ source_location,
+ asset_metadata
+ ));
+ assert_ok!(parachain::AssetManager::set_units_per_second(
+ parachain::Origin::root(),
+ relay_asset_id,
+ units_per_second
+ ));
+ });
+
+ let dest: MultiLocation = AccountId32 {
+ network: Any,
+ id: ALICE.into(),
+ }
+ .into();
+
+ Relay::execute_with(|| {
+ assert_ok!(RelayChainPalletXcm::reserve_transfer_assets(
+ relay_chain::Origin::signed(ALICE),
+ Box::new(X1(Parachain(1)).into().into()),
+ Box::new(VersionedMultiLocation::V1(dest).clone().into()),
+ Box::new((Here, amount).into()),
+ 0,
+ ));
+ assert_eq!(
+ relay_chain::Balances::free_balance(¶_account_id(1)),
+ INITIAL_BALANCE + amount
+ );
+ });
+
+ ParaA::execute_with(|| {
+ // ALICE gets nothing
+ assert_eq!(parachain::Assets::balance(relay_asset_id, &ALICE.into()), 0);
+ // Asset manager gets nothing, all balance stucks
+ assert_eq!(
+ parachain::Assets::balance(relay_asset_id, AssetManager::account_id()),
+ 0
+ );
+ });
+}
+
+#[test]
+fn receive_relay_should_fail_without_specifying_units_per_second() {
+ MockNet::reset();
+
+ let relay_asset_id: parachain::AssetId = 0;
+ let source_location = AssetLocation(VersionedMultiLocation::V1(MultiLocation::parent()));
+ let asset_metadata = parachain::AssetRegistarMetadata {
+ name: b"Kusama".to_vec(),
+ symbol: b"KSM".to_vec(),
+ decimals: 12,
+ min_balance: 1u128,
+ evm_address: None,
+ is_frozen: false,
+ is_sufficient: true,
+ };
+ let amount = 333u128;
+
+ ParaA::execute_with(|| {
+ assert_ok!(parachain::AssetManager::register_asset(
+ parachain::Origin::root(),
+ source_location,
+ asset_metadata
+ ));
+ });
+
+ let dest: MultiLocation = AccountId32 {
+ network: Any,
+ id: ALICE.into(),
+ }
+ .into();
+
+ Relay::execute_with(|| {
+ assert_ok!(RelayChainPalletXcm::reserve_transfer_assets(
+ relay_chain::Origin::signed(ALICE),
+ Box::new(X1(Parachain(1)).into().into()),
+ Box::new(VersionedMultiLocation::V1(dest).clone().into()),
+ Box::new((Here, amount).into()),
+ 0,
+ ));
+ assert_eq!(
+ relay_chain::Balances::free_balance(¶_account_id(1)),
+ INITIAL_BALANCE + amount
+ );
+ });
+
+ ParaA::execute_with(|| {
+ // ALICE gets nothing
+ assert_eq!(parachain::Assets::balance(relay_asset_id, &ALICE.into()), 0);
+ // Asset manager gets nothing, all balance stucks
+ assert_eq!(
+ parachain::Assets::balance(relay_asset_id, AssetManager::account_id()),
+ 0
+ );
+ });
+}
+
+#[test]
+fn send_para_a_asset_to_para_b_with_insufficient_fee() {
+ MockNet::reset();
+
+ // para a balance location
+ let source_location = AssetLocation(VersionedMultiLocation::V1(MultiLocation::new(
+ 1,
+ X1(Parachain(1)),
+ )));
+ let a_currency_id = 0u32;
+ let amount = 15u128;
+ let units_per_second = 20_000_000u128;
+ let dest_weight = 800_000u64;
+ let fee = calculate_fee(units_per_second, dest_weight);
+ assert!(fee > amount);
+
+ let asset_metadata = parachain::AssetRegistarMetadata {
+ name: b"ParaAToken".to_vec(),
+ symbol: b"ParaA".to_vec(),
+ decimals: 18,
+ evm_address: None,
+ min_balance: 1,
+ is_frozen: false,
+ is_sufficient: true,
+ };
+
+ // Register ParaA native asset in ParaA
+ ParaA::execute_with(|| {
+ assert_ok!(AssetManager::register_asset(
+ parachain::Origin::root(),
+ // This need to be changed starting from v0.9.16
+ // need to use something like MultiLocation { parents: 0, interior: here} instead
+ source_location.clone(),
+ asset_metadata.clone()
+ ));
+ assert_ok!(AssetManager::set_units_per_second(
+ parachain::Origin::root(),
+ a_currency_id,
+ 0u128
+ ));
+ assert_eq!(
+ Some(a_currency_id),
+ AssetManager::location_asset_id(source_location.clone())
+ );
+ });
+
+ ParaB::execute_with(|| {
+ assert_ok!(AssetManager::register_asset(
+ parachain::Origin::root(),
+ source_location.clone(),
+ asset_metadata.clone()
+ ));
+ assert_ok!(AssetManager::set_units_per_second(
+ parachain::Origin::root(),
+ a_currency_id,
+ units_per_second
+ ));
+ assert_eq!(
+ Some(a_currency_id),
+ AssetManager::location_asset_id(source_location.clone())
+ );
+ });
+
+ let dest = MultiLocation {
+ parents: 1,
+ interior: X2(
+ Parachain(2),
+ AccountId32 {
+ network: NetworkId::Any,
+ id: ALICE.into(),
+ },
+ ),
+ };
+
+ // Transfer ParaA balance to B
+ ParaA::execute_with(|| {
+ assert_ok!(parachain::XTokens::transfer(
+ parachain::Origin::signed(ALICE.into()),
+ parachain::CurrencyId::MantaCurrency(a_currency_id),
+ amount,
+ Box::new(VersionedMultiLocation::V1(dest)),
+ dest_weight,
+ ));
+ assert_eq!(
+ parachain::Balances::free_balance(&ALICE.into()),
+ INITIAL_BALANCE - amount
+ )
+ });
+
+ // Alice on B should receive nothing since the fee is insufficient
+ ParaB::execute_with(|| {
+ assert_eq!(parachain::Assets::balance(a_currency_id, &ALICE.into()), 0);
+ });
+}
+
+#[test]
+fn send_para_a_asset_to_para_b_without_specifying_units_per_second() {
+ MockNet::reset();
+
+ // para a balance location
+ let source_location = AssetLocation(VersionedMultiLocation::V1(MultiLocation::new(
+ 1,
+ X1(Parachain(1)),
+ )));
+ let a_currency_id = 0u32;
+ let amount = 567u128;
+ let dest_weight = 800_000u64;
+
+ let asset_metadata = parachain::AssetRegistarMetadata {
+ name: b"ParaAToken".to_vec(),
+ symbol: b"ParaA".to_vec(),
+ decimals: 18,
+ evm_address: None,
+ min_balance: 1,
+ is_frozen: false,
+ is_sufficient: true,
+ };
+
+ // Register ParaA native asset in ParaA
+ ParaA::execute_with(|| {
+ assert_ok!(AssetManager::register_asset(
+ parachain::Origin::root(),
+ // This need to be changed starting from v0.9.16
+ // need to use something like MultiLocation { parents: 0, interior: here} instead
+ source_location.clone(),
+ asset_metadata.clone()
+ ));
+ assert_ok!(AssetManager::set_units_per_second(
+ parachain::Origin::root(),
+ a_currency_id,
+ 0u128
+ ));
+ assert_eq!(
+ Some(a_currency_id),
+ AssetManager::location_asset_id(source_location.clone())
+ );
+ });
+
+ // We don't specify units_per_second on B
+ ParaB::execute_with(|| {
+ assert_ok!(AssetManager::register_asset(
+ parachain::Origin::root(),
+ source_location.clone(),
+ asset_metadata.clone()
+ ));
+ assert_eq!(
+ Some(a_currency_id),
+ AssetManager::location_asset_id(source_location.clone())
+ );
+ });
+
+ let dest = MultiLocation {
+ parents: 1,
+ interior: X2(
+ Parachain(2),
+ AccountId32 {
+ network: NetworkId::Any,
+ id: ALICE.into(),
+ },
+ ),
+ };
+
+ // Transfer ParaA balance to B
+ ParaA::execute_with(|| {
+ assert_ok!(parachain::XTokens::transfer(
+ parachain::Origin::signed(ALICE.into()),
+ parachain::CurrencyId::MantaCurrency(a_currency_id),
+ amount,
+ Box::new(VersionedMultiLocation::V1(dest)),
+ dest_weight,
+ ));
+ assert_eq!(
+ parachain::Balances::free_balance(&ALICE.into()),
+ INITIAL_BALANCE - amount
+ )
+ });
+
+ // Alice on B should receive nothing since we didn't specify the unit per second
+ ParaB::execute_with(|| {
+ assert_eq!(parachain::Assets::balance(a_currency_id, &ALICE.into()), 0);
+ });
+}
+
+#[test]
+fn receive_asset_with_is_sufficient_false() {
+ MockNet::reset();
+
+ let new_account = [5u8; 32];
+ let relay_asset_id = 0u32;
+ let source_location = AssetLocation(VersionedMultiLocation::V1(MultiLocation::parent()));
+ let asset_metadata = parachain::AssetRegistarMetadata {
+ name: b"Kusama".to_vec(),
+ symbol: b"KSM".to_vec(),
+ decimals: 12,
+ min_balance: 1u128,
+ evm_address: None,
+ is_frozen: false,
+ is_sufficient: false,
+ };
+ let amount = 123u128;
+
+ // register relay asset in parachain A
+ ParaA::execute_with(|| {
+ assert_ok!(parachain::AssetManager::register_asset(
+ parachain::Origin::root(),
+ source_location,
+ asset_metadata
+ ));
+ // we don't charge anything during test
+ assert_ok!(parachain::AssetManager::set_units_per_second(
+ parachain::Origin::root(),
+ relay_asset_id,
+ 0u128
+ ));
+ });
+
+ let dest: MultiLocation = AccountId32 {
+ network: Any,
+ id: new_account.into(),
+ }
+ .into();
+
+ Relay::execute_with(|| {
+ assert_ok!(RelayChainPalletXcm::reserve_transfer_assets(
+ relay_chain::Origin::signed(ALICE),
+ Box::new(X1(Parachain(1)).into().into()),
+ Box::new(VersionedMultiLocation::V1(dest.clone()).clone().into()),
+ Box::new((Here, amount).into()),
+ 0,
+ ));
+ assert_eq!(
+ relay_chain::Balances::free_balance(¶_account_id(1)),
+ INITIAL_BALANCE + amount
+ );
+ });
+
+ // parachain should not have received assets
+ ParaA::execute_with(|| {
+ assert_eq!(
+ parachain::Assets::balance(relay_asset_id, &new_account.into()),
+ 0
+ );
+ });
+
+ // Send native token to fresh_account
+ ParaA::execute_with(|| {
+ assert_ok!(parachain::Balances::transfer(
+ parachain::Origin::signed(ALICE.into()),
+ new_account.into(),
+ 100
+ ));
+ });
+
+ Relay::execute_with(|| {
+ assert_ok!(RelayChainPalletXcm::reserve_transfer_assets(
+ relay_chain::Origin::signed(ALICE),
+ Box::new(X1(Parachain(1)).into().into()),
+ Box::new(VersionedMultiLocation::V1(dest).clone().into()),
+ Box::new((Here, amount).into()),
+ 0,
+ ));
+ assert_eq!(
+ relay_chain::Balances::free_balance(¶_account_id(1)),
+ INITIAL_BALANCE + amount + amount
+ );
+ });
+
+ // parachain should not have received assets
+ ParaA::execute_with(|| {
+ println!(
+ "fresh account bal: {}",
+ parachain::Assets::balance(relay_asset_id, &new_account.into())
+ );
+ });
+}
+
+#[test]
+fn receive_asset_with_is_sufficient_true() {
+ MockNet::reset();
+
+ let new_account = [5u8; 32];
+ let relay_asset_id = 0u32;
+ let source_location = AssetLocation(VersionedMultiLocation::V1(MultiLocation::parent()));
+ let asset_metadata = parachain::AssetRegistarMetadata {
+ name: b"Kusama".to_vec(),
+ symbol: b"KSM".to_vec(),
+ decimals: 12,
+ min_balance: 1u128,
+ evm_address: None,
+ is_frozen: false,
+ is_sufficient: true,
+ };
+ let amount = 123u128;
+
+ // register relay asset in parachain A
+ ParaA::execute_with(|| {
+ assert_ok!(parachain::AssetManager::register_asset(
+ parachain::Origin::root(),
+ source_location,
+ asset_metadata
+ ));
+ // we don't charge anything during test
+ assert_ok!(parachain::AssetManager::set_units_per_second(
+ parachain::Origin::root(),
+ relay_asset_id,
+ 0u128
+ ));
+ });
+
+ let dest: MultiLocation = AccountId32 {
+ network: Any,
+ id: new_account.into(),
+ }
+ .into();
+
+ Relay::execute_with(|| {
+ assert_ok!(RelayChainPalletXcm::reserve_transfer_assets(
+ relay_chain::Origin::signed(ALICE),
+ Box::new(X1(Parachain(1)).into().into()),
+ Box::new(VersionedMultiLocation::V1(dest.clone()).clone().into()),
+ Box::new((Here, amount).into()),
+ 0,
+ ));
+ assert_eq!(
+ relay_chain::Balances::free_balance(¶_account_id(1)),
+ INITIAL_BALANCE + amount
+ );
+ });
+
+ // parachain should have received assets
+ ParaA::execute_with(|| {
+ assert_eq!(
+ parachain::Assets::balance(relay_asset_id, &new_account.into()),
+ amount
+ );
+ });
+}
+
+/// Scenario:
+/// A parachain transfers funds on the relay chain to another parachain account.
+///
+/// Asserts that the parachain accounts are updated as expected.
+#[test]
+fn withdraw_and_deposit() {
+ MockNet::reset();
+
+ let send_amount = 10;
+
+ ParaA::execute_with(|| {
+ let message = Xcm(vec![
+ WithdrawAsset((Here, send_amount).into()),
+ buy_execution((Here, send_amount)),
+ DepositAsset {
+ assets: All.into(),
+ max_assets: 1,
+ beneficiary: Parachain(2).into(),
+ },
+ ]);
+ // Send withdraw and deposit
+ assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent, message.clone()));
+ });
+
+ Relay::execute_with(|| {
+ assert_eq!(
+ relay_chain::Balances::free_balance(para_account_id(1)),
+ INITIAL_BALANCE - send_amount
+ );
+ assert_eq!(
+ relay_chain::Balances::free_balance(para_account_id(2)),
+ send_amount
+ );
+ });
+}
+
+/// Scenario:
+/// A parachain wants to be notified that a transfer worked correctly.
+/// It sends a `QueryHolding` after the deposit to get notified on success.
+///
+/// Asserts that the balances are updated correctly and the expected XCM is sent.
+#[test]
+fn query_holding() {
+ MockNet::reset();
+
+ let send_amount = 10;
+ let query_id_set = 1234;
+
+ // Send a message which fully succeeds on the relay chain
+ ParaA::execute_with(|| {
+ let message = Xcm(vec![
+ WithdrawAsset((Here, send_amount).into()),
+ buy_execution((Here, send_amount)),
+ DepositAsset {
+ assets: All.into(),
+ max_assets: 1,
+ beneficiary: Parachain(2).into(),
+ },
+ QueryHolding {
+ query_id: query_id_set,
+ dest: Parachain(1).into(),
+ assets: All.into(),
+ max_response_weight: 1_000_000_000,
+ },
+ ]);
+ // Send withdraw and deposit with query holding
+ assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent, message.clone(),));
+ });
+
+ // Check that transfer was executed
+ Relay::execute_with(|| {
+ // Withdraw executed
+ assert_eq!(
+ relay_chain::Balances::free_balance(para_account_id(1)),
+ INITIAL_BALANCE - send_amount
+ );
+ // Deposit executed
+ assert_eq!(
+ relay_chain::Balances::free_balance(para_account_id(2)),
+ send_amount
+ );
+ });
+
+ // Check that QueryResponse message was received
+ ParaA::execute_with(|| {
+ assert_eq!(
+ parachain::MsgQueue::received_dmp(),
+ vec![Xcm(vec![QueryResponse {
+ query_id: query_id_set,
+ response: Response::Assets(MultiAssets::new()),
+ max_weight: 1_000_000_000,
+ }])],
+ );
+ });
+}
+
+#[test]
+fn test_versioning_on_runtime_upgrade_with_relay() {
+ MockNet::reset();
+
+ let relay_asset_id: parachain::AssetId = 0;
+ let source_location = AssetLocation(VersionedMultiLocation::V1(MultiLocation::parent()));
+ let asset_metadata = parachain::AssetRegistarMetadata {
+ name: b"Kusama".to_vec(),
+ symbol: b"KSM".to_vec(),
+ decimals: 12,
+ min_balance: 1u128,
+ evm_address: None,
+ is_frozen: false,
+ is_sufficient: true,
+ };
+
+ // register relay asset in parachain A (XCM version 1)
+ ParaA::execute_with(|| {
+ parachain::XcmVersioner::set_version(1);
+ assert_ok!(parachain::AssetManager::register_asset(
+ parachain::Origin::root(),
+ source_location,
+ asset_metadata
+ ));
+ // we don't charge anything during test
+ assert_ok!(parachain::AssetManager::set_units_per_second(
+ parachain::Origin::root(),
+ relay_asset_id,
+ 0u128
+ ));
+ });
+
+ let response = Response::Version(2);
+
+ // This is irrelevant, nothing will be done with this message,
+ // but we need to pass a message as an argument to trigger the storage change
+ let mock_message: Xcm<()> = Xcm(vec![QueryResponse {
+ query_id: 0,
+ response,
+ max_weight: 0,
+ }]);
+
+ let dest: MultiLocation = AccountId32 {
+ network: Any,
+ id: ALICE.into(),
+ }
+ .into();
+
+ Relay::execute_with(|| {
+ // This sets the default version, for not known destinations
+ assert_ok!(RelayChainPalletXcm::force_default_xcm_version(
+ relay_chain::Origin::root(),
+ Some(2)
+ ));
+
+ // Wrap version, which sets VersionedStorage
+ // This is necessary because the mock router does not use wrap_version, but
+ // this is not necessary in prod.
+ // more specifically, this will trigger `note_unknown_version` to put the
+ // version to `VersionDiscoveryQueue` on relay-chain's pallet-xcm
+ assert_ok!(::wrap_version(
+ &Parachain(1).into(),
+ mock_message
+ ));
+
+ // Transfer assets. Since it is an unknown destination, it will query for version
+ assert_ok!(RelayChainPalletXcm::reserve_transfer_assets(
+ relay_chain::Origin::signed(ALICE),
+ Box::new(Parachain(1).into().into()),
+ Box::new(VersionedMultiLocation::V1(dest).clone().into()),
+ Box::new((Here, 123).into()),
+ 0,
+ ));
+
+ // Let's advance the relay. This should trigger the subscription message
+ relay_chain::relay_roll_to(2);
+
+ // queries should have been updated
+ assert!(RelayChainPalletXcm::query(0).is_some());
+ });
+
+ let expected_supported_version: relay_chain::Event =
+ pallet_xcm::Event::SupportedVersionChanged(
+ MultiLocation {
+ parents: 0,
+ interior: X1(Parachain(1)),
+ },
+ 1,
+ )
+ .into();
+
+ Relay::execute_with(|| {
+ // Assert that the events vector contains the version change
+ assert!(relay_chain::relay_events().contains(&expected_supported_version));
+ });
+
+ let expected_version_notified: parachain::Event = pallet_xcm::Event::VersionChangeNotified(
+ MultiLocation {
+ parents: 1,
+ interior: Here,
+ },
+ 2,
+ )
+ .into();
+
+ // ParaA changes version to 2, and calls on_runtime_upgrade. This should notify the targets
+ // of the new version change
+ ParaA::execute_with(|| {
+ // Set version
+ parachain::XcmVersioner::set_version(2);
+ // Do runtime upgrade
+ parachain::on_runtime_upgrade();
+ // Initialize block, to call on_initialize and notify targets
+ parachain::para_roll_to(2);
+ // Expect the event in the parachain
+ assert!(parachain::para_events().contains(&expected_version_notified));
+ });
+
+ // This event should have been seen in the relay
+ let expected_supported_version_2: relay_chain::Event =
+ pallet_xcm::Event::SupportedVersionChanged(
+ MultiLocation {
+ parents: 0,
+ interior: X1(Parachain(1)),
+ },
+ 2,
+ )
+ .into();
+
+ Relay::execute_with(|| {
+ // Assert that the events vector contains the new version change
+ assert!(relay_chain::relay_events().contains(&expected_supported_version_2));
+ });
+}
+
+#[test]
+fn test_automatic_versioning_on_runtime_upgrade_with_para_b() {
+ MockNet::reset();
+
+ // para a balance location
+ let source_location = AssetLocation(VersionedMultiLocation::V1(MultiLocation::new(
+ 1,
+ X1(Parachain(1)),
+ )));
+ let a_currency_id = 0u32;
+
+ let asset_metadata = parachain::AssetRegistarMetadata {
+ name: b"ParaAToken".to_vec(),
+ symbol: b"ParaA".to_vec(),
+ decimals: 18,
+ evm_address: None,
+ min_balance: 1,
+ is_frozen: false,
+ is_sufficient: true,
+ };
+ let response = Response::Version(2);
+
+ // This is irrelevant, nothing will be done with this message,
+ // but we need to pass a message as an argument to trigger the storage change
+ let mock_message: Xcm<()> = Xcm(vec![QueryResponse {
+ query_id: 0,
+ response,
+ max_weight: 0,
+ }]);
+
+ ParaA::execute_with(|| {
+ // advertised version
+ parachain::XcmVersioner::set_version(2);
+ // Register ParaA native asset in ParaA
+ assert_ok!(AssetManager::register_asset(
+ parachain::Origin::root(),
+ // This need to be changed starting from v0.9.16
+ // need to use something like MultiLocation { parents: 0, interior: here} instead
+ source_location.clone(),
+ asset_metadata.clone()
+ ));
+ assert_ok!(AssetManager::set_units_per_second(
+ parachain::Origin::root(),
+ a_currency_id,
+ 0u128
+ ));
+ assert_eq!(
+ Some(a_currency_id),
+ AssetManager::location_asset_id(source_location.clone())
+ );
+ });
+
+ ParaB::execute_with(|| {
+ // Let's try with v0
+ parachain::XcmVersioner::set_version(0);
+
+ assert_ok!(AssetManager::register_asset(
+ parachain::Origin::root(),
+ source_location,
+ asset_metadata
+ ));
+ assert_ok!(AssetManager::set_units_per_second(
+ parachain::Origin::root(),
+ a_currency_id,
+ 0u128
+ ));
+ });
+
+ ParaA::execute_with(|| {
+ // This sets the default version, for not known destinations
+ assert_ok!(ParachainPalletXcm::force_default_xcm_version(
+ parachain::Origin::root(),
+ Some(2)
+ ));
+ // Wrap version, which sets VersionedStorage
+ assert_ok!(::wrap_version(
+ &MultiLocation::new(1, X1(Parachain(2))).into(),
+ mock_message
+ ));
+
+ parachain::para_roll_to(2);
+
+ // queries should have been updated
+ assert!(ParachainPalletXcm::query(0).is_some());
+ });
+
+ let expected_supported_version: parachain::Event = pallet_xcm::Event::SupportedVersionChanged(
+ MultiLocation {
+ parents: 1,
+ interior: X1(Parachain(2)),
+ },
+ 0,
+ )
+ .into();
+
+ ParaA::execute_with(|| {
+ // Assert that the events vector contains the version change
+ assert!(parachain::para_events().contains(&expected_supported_version));
+ });
+
+ // Let's ensure talking in v0 works
+ let dest = MultiLocation {
+ parents: 1,
+ interior: X2(
+ Parachain(2),
+ AccountId32 {
+ network: NetworkId::Any,
+ id: ALICE.into(),
+ },
+ ),
+ };
+
+ ParaA::execute_with(|| {
+ // free execution, full amount received
+ assert_ok!(parachain::XTokens::transfer(
+ parachain::Origin::signed(ALICE.into()),
+ parachain::CurrencyId::MantaCurrency(a_currency_id),
+ 100,
+ Box::new(VersionedMultiLocation::V1(dest)),
+ 80
+ ));
+ // free execution, full amount received
+ assert_eq!(
+ parachain::Balances::free_balance(&ALICE.into()),
+ INITIAL_BALANCE - 100
+ );
+ });
+
+ ParaB::execute_with(|| {
+ // free execution, full amount received
+ assert_eq!(
+ parachain::Assets::balance(a_currency_id, &ALICE.into()),
+ 100
+ );
+ });
+
+ let expected_version_notified: parachain::Event = pallet_xcm::Event::VersionChangeNotified(
+ MultiLocation {
+ parents: 1,
+ interior: X1(Parachain(1)),
+ },
+ 2,
+ )
+ .into();
+
+ // ParaB changes version to 2, and calls on_runtime_upgrade. This should notify the targets
+ // of the new version change
+ ParaB::execute_with(|| {
+ // Set version
+ parachain::XcmVersioner::set_version(2);
+ // Do runtime upgrade
+ parachain::on_runtime_upgrade();
+ // Initialize block, to call on_initialize and notify targets
+ parachain::para_roll_to(2);
+ // Expect the event in the parachain
+ assert!(parachain::para_events().contains(&expected_version_notified));
+ });
+
+ // This event should have been seen in para A
+ let expected_supported_version_2: parachain::Event =
+ pallet_xcm::Event::SupportedVersionChanged(
+ MultiLocation {
+ parents: 1,
+ interior: X1(Parachain(2)),
+ },
+ 2,
+ )
+ .into();
+
+ // Para A should have received the version change
+ ParaA::execute_with(|| {
+ // Assert that the events vector contains the new version change
+ assert!(parachain::para_events().contains(&expected_supported_version_2));
+ });
+}
diff --git a/runtime/manta/Cargo.toml b/runtime/manta/Cargo.toml
index ccc60f9db..3dd6efb2a 100644
--- a/runtime/manta/Cargo.toml
+++ b/runtime/manta/Cargo.toml
@@ -75,7 +75,7 @@ pallet-xcm = { git = 'https://github.com/paritytech/polkadot.git', default-featu
# Self dependencies
manta-primitives = { path = '../primitives', default-features = false }
-pallet-tx-pause = { path = '../../pallets/pallet-tx-pause', default-features = false }
+pallet-tx-pause = { path = '../../pallets/tx-pause', default-features = false }
[package.metadata.docs.rs]
targets = ['x86_64-unknown-linux-gnu']
diff --git a/runtime/primitives/Cargo.toml b/runtime/primitives/Cargo.toml
index 19a94c92e..067d123d5 100644
--- a/runtime/primitives/Cargo.toml
+++ b/runtime/primitives/Cargo.toml
@@ -12,22 +12,36 @@ targets = ['x86_64-unknown-linux-gnu']
[dependencies]
codec = { package = "parity-scale-codec", version = "2.3.1", default-features = false }
+scale-info = { version = "1.0", default-features = false, features = [ "derive" ] }
smallvec = "1.6.1"
+log = "0.4.14"
# Substrate primitives
+frame-support = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.16" }
sp-consensus-aura = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.16" }
sp-core = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.16" }
sp-std = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.16" }
sp-io = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.16" }
sp-runtime = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.16" }
+xcm-executor = { git = 'https://github.com/paritytech/polkadot.git', default-features = false, branch = "release-v0.9.16"}
+xcm = { git = 'https://github.com/paritytech/polkadot.git', default-features = false, branch = "release-v0.9.16" }
+xcm-builder = { git = 'https://github.com/paritytech/polkadot.git', default-features = false, branch = "release-v0.9.16"}
[features]
default = ["std"]
std = [
'codec/std',
'sp-consensus-aura/std',
- 'sp-core/std',
+ 'scale-info/std',
'sp-io/std',
'sp-std/std',
+ 'log/std',
+ 'frame-support/std',
+ 'sp-consensus-aura/std',
+ 'sp-core/std',
+ 'sp-io/std',
'sp-runtime/std',
+ 'xcm-executor/std',
+ 'xcm-builder/std',
+ 'xcm/std',
]
diff --git a/runtime/primitives/src/assets.rs b/runtime/primitives/src/assets.rs
new file mode 100644
index 000000000..6fbbc78a2
--- /dev/null
+++ b/runtime/primitives/src/assets.rs
@@ -0,0 +1,136 @@
+// Copyright 2020-2022 Manta Network.
+// This file is part of Manta.
+//
+// Manta 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.
+//
+// Manta 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 Manta. If not, see .
+
+use codec::{Decode, Encode};
+use scale_info::TypeInfo;
+use sp_core::H160;
+use sp_std::{borrow::Borrow, marker::PhantomData, prelude::Vec};
+
+///! Manta/Calamari/Dolphin Asset
+use xcm::{
+ v1::{Junctions, MultiLocation},
+ VersionedMultiLocation,
+};
+
+/// The metadata of a Manta Asset
+#[derive(Clone, Default, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
+pub struct AssetRegistarMetadata {
+ pub name: Vec,
+ pub symbol: Vec,
+ pub decimals: u8,
+ pub evm_address: Option,
+ pub is_frozen: bool,
+ pub min_balance: Balance,
+ /// `is_sufficient`: Whether a non-zero balance of this asset is deposit of sufficient
+ /// value to account for the state bloat associated with its balance storage. If set to
+ /// `true`, then non-zero balances may be stored without a `consumer` reference (and thus
+ /// an ED in the Balances pallet or whatever else is used to control user-account state
+ /// growth).
+ /// For example, if is_sufficient set to `false`, a fresh account cannot receive XCM tokens.
+ pub is_sufficient: bool,
+}
+
+/// Asset storage metadata
+/// Currently, `AssetStorageMetadata` is stored at `pallet-asset`.
+#[derive(Clone, Default, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
+pub struct AssetStorageMetadata {
+ pub name: Vec,
+ pub symbol: Vec,
+ pub decimals: u8,
+ pub is_frozen: bool,
+}
+
+impl From> for AssetStorageMetadata {
+ fn from(source: AssetRegistarMetadata) -> Self {
+ AssetStorageMetadata {
+ name: source.name,
+ symbol: source.symbol,
+ decimals: source.decimals,
+ is_frozen: source.is_frozen,
+ }
+ }
+}
+
+#[derive(Clone, Eq, Debug, PartialEq, Encode, Decode, TypeInfo)]
+pub struct AssetLocation(pub VersionedMultiLocation);
+
+/// This cannot act as the default before v0.9.16 and need overwrite
+/// https://docs.google.com/document/d/1W8y00IcJb0JXPBF59aP4nm-c7DY8Ld02-yIAO7UxR80
+impl Default for AssetLocation {
+ fn default() -> Self {
+ AssetLocation(VersionedMultiLocation::V1(MultiLocation {
+ parents: 0,
+ interior: Junctions::Here,
+ }))
+ }
+}
+
+/// Convert a `MultiLocaiton` to an `AssetLocation`
+/// Note: This does not guaranttee the `AssetLocation` is registered (i.e. have an AssetId)
+impl From for AssetLocation {
+ fn from(location: MultiLocation) -> Self {
+ AssetLocation(VersionedMultiLocation::V1(location))
+ }
+}
+
+/// Convert an `AssetLocation` to a MultiLocation
+/// If Native, return none.
+impl From