diff --git a/.gitignore b/.gitignore index 8842a3d8224..b9638af74d7 100644 --- a/.gitignore +++ b/.gitignore @@ -49,6 +49,7 @@ tsconfig.tsbuildinfo skills/ .factory/ .windsurf/ +.pi/ # AI Agent instruction files CLAUDE.md diff --git a/Cargo.lock b/Cargo.lock index c71fd22b393..16af46034f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -127,6 +127,307 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "alloy-consensus" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4ff99651d46cef43767b5e8262ea228cd05287409ccb0c947cc25e70a952f9" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "alloy-trie", + "alloy-tx-macros", + "auto_impl", + "borsh", + "c-kzg", + "derive_more 2.1.1", + "either", + "k256", + "once_cell", + "rand 0.8.5", + "secp256k1 0.30.0", + "serde", + "serde_json", + "serde_with", + "thiserror 2.0.18", +] + +[[package]] +name = "alloy-core" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23e8604b0c092fabc80d075ede181c9b9e596249c70b99253082d7e689836529" +dependencies = [ + "alloy-dyn-abi", + "alloy-json-abi", + "alloy-primitives", + "alloy-rlp", + "alloy-sol-types", +] + +[[package]] +name = "alloy-dyn-abi" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2db5c583aaef0255aa63a4fe827f826090142528bba48d1bf4119b62780cad" +dependencies = [ + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-type-parser", + "alloy-sol-types", + "itoa", + "serde", + "serde_json", + "winnow", +] + +[[package]] +name = "alloy-eip2124" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "741bdd7499908b3aa0b159bba11e71c8cddd009a2c2eb7a06e825f1ec87900a5" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "crc", + "serde", + "thiserror 2.0.18", +] + +[[package]] +name = "alloy-eip2930" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9441120fa82df73e8959ae0e4ab8ade03de2aaae61be313fbf5746277847ce25" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "borsh", + "serde", +] + +[[package]] +name = "alloy-eip7702" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2919c5a56a1007492da313e7a3b6d45ef5edc5d33416fdec63c0d7a2702a0d20" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "borsh", + "k256", + "serde", + "thiserror 2.0.18", +] + +[[package]] +name = "alloy-eip7928" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8222b1d88f9a6d03be84b0f5e76bb60cd83991b43ad8ab6477f0e4a7809b98d" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "borsh", + "serde", +] + +[[package]] +name = "alloy-eips" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "def1626eea28d48c6cc0a6f16f34d4af0001906e4f889df6c660b39c86fd044d" +dependencies = [ + "alloy-eip2124", + "alloy-eip2930", + "alloy-eip7702", + "alloy-eip7928", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "auto_impl", + "borsh", + "c-kzg", + "derive_more 2.1.1", + "either", + "serde", + "serde_with", + "sha2 0.10.9", + "thiserror 2.0.18", +] + +[[package]] +name = "alloy-json-abi" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9dbe713da0c737d9e5e387b0ba790eb98b14dd207fe53eef50e19a5a8ec3dac" +dependencies = [ + "alloy-primitives", + "alloy-sol-type-parser", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-primitives" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3b431b4e72cd8bd0ec7a50b4be18e73dab74de0dba180eef171055e5d5926e" +dependencies = [ + "alloy-rlp", + "bytes", + "cfg-if", + "const-hex", + "derive_more 2.1.1", + "foldhash 0.2.0", + "hashbrown 0.16.1", + "indexmap 2.13.0", + "itoa", + "k256", + "keccak-asm", + "paste", + "proptest", + "rand 0.9.2", + "rapidhash", + "ruint", + "rustc-hash 2.1.1", + "serde", + "sha3", +] + +[[package]] +name = "alloy-rlp" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc90b1e703d3c03f4ff7f48e82dd0bc1c8211ab7d079cd836a06fcfeb06651cb" +dependencies = [ + "alloy-rlp-derive", + "arrayvec 0.7.6", + "bytes", +] + +[[package]] +name = "alloy-rlp-derive" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36834a5c0a2fa56e171bf256c34d70fca07d0c0031583edea1c4946b7889c9e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "alloy-serde" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e6d631f8b975229361d8af7b2c749af31c73b3cf1352f90e144ddb06227105e" +dependencies = [ + "alloy-primitives", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-sol-macro" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab81bab693da9bb79f7a95b64b394718259fdd7e41dceeced4cad57cb71c4f6a" +dependencies = [ + "alloy-sol-macro-expander", + "alloy-sol-macro-input", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "alloy-sol-macro-expander" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "489f1620bb7e2483fb5819ed01ab6edc1d2f93939dce35a5695085a1afd1d699" +dependencies = [ + "alloy-sol-macro-input", + "const-hex", + "heck 0.5.0", + "indexmap 2.13.0", + "proc-macro-error2", + "proc-macro2", + "quote", + "sha3", + "syn 2.0.117", + "syn-solidity", +] + +[[package]] +name = "alloy-sol-macro-input" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56cef806ad22d4392c5fc83cf8f2089f988eb99c7067b4e0c6f1971fc1cca318" +dependencies = [ + "const-hex", + "dunce", + "heck 0.5.0", + "macro-string", + "proc-macro2", + "quote", + "syn 2.0.117", + "syn-solidity", +] + +[[package]] +name = "alloy-sol-type-parser" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6df77fea9d6a2a75c0ef8d2acbdfd92286cc599983d3175ccdc170d3433d249" +dependencies = [ + "serde", + "winnow", +] + +[[package]] +name = "alloy-sol-types" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64612d29379782a5dde6f4b6570d9c756d734d760c0c94c254d361e678a6591f" +dependencies = [ + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-macro", + "serde", +] + +[[package]] +name = "alloy-trie" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f14b5d9b2c2173980202c6ff470d96e7c5e202c65a9f67884ad565226df7fbb" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "derive_more 2.1.1", + "nybbles", + "serde", + "smallvec", + "thiserror 2.0.18", + "tracing", +] + +[[package]] +name = "alloy-tx-macros" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397406cf04b11ca2a48e6f81804c70af3f40a36abf648e11dc7416043eb0834d" +dependencies = [ + "darling 0.21.3", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "always-assert" version = "0.1.3" @@ -271,6 +572,18 @@ dependencies = [ "ark-std 0.5.0", ] +[[package]] +name = "ark-bn254" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d69eab57e8d2663efa5c63135b2af4f396d66424f88954c21104125ab6b3e6bc" +dependencies = [ + "ark-ec 0.5.0", + "ark-ff 0.5.0", + "ark-r1cs-std", + "ark-std 0.5.0", +] + [[package]] name = "ark-ec" version = "0.4.2" @@ -321,6 +634,24 @@ dependencies = [ "ark-std 0.5.0", ] +[[package]] +name = "ark-ff" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" +dependencies = [ + "ark-ff-asm 0.3.0", + "ark-ff-macros 0.3.0", + "ark-serialize 0.3.0", + "ark-std 0.3.0", + "derivative", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.3.3", + "zeroize", +] + [[package]] name = "ark-ff" version = "0.4.2" @@ -337,7 +668,7 @@ dependencies = [ "num-bigint", "num-traits", "paste", - "rustc_version", + "rustc_version 0.4.1", "zeroize", ] @@ -361,6 +692,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ark-ff-asm" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" +dependencies = [ + "quote", + "syn 1.0.109", +] + [[package]] name = "ark-ff-asm" version = "0.4.2" @@ -381,6 +722,18 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "ark-ff-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" +dependencies = [ + "num-bigint", + "num-traits", + "quote", + "syn 1.0.109", +] + [[package]] name = "ark-ff-macros" version = "0.4.2" @@ -435,6 +788,45 @@ dependencies = [ "hashbrown 0.15.5", ] +[[package]] +name = "ark-r1cs-std" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "941551ef1df4c7a401de7068758db6503598e6f01850bdb2cfdb614a1f9dbea1" +dependencies = [ + "ark-ec 0.5.0", + "ark-ff 0.5.0", + "ark-relations", + "ark-std 0.5.0", + "educe", + "num-bigint", + "num-integer", + "num-traits", + "tracing", +] + +[[package]] +name = "ark-relations" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec46ddc93e7af44bcab5230937635b06fb5744464dd6a7e7b083e80ebd274384" +dependencies = [ + "ark-ff 0.5.0", + "ark-std 0.5.0", + "tracing", + "tracing-subscriber 0.2.25", +] + +[[package]] +name = "ark-serialize" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" +dependencies = [ + "ark-std 0.3.0", + "digest 0.9.0", +] + [[package]] name = "ark-serialize" version = "0.4.2" @@ -482,6 +874,16 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "ark-std" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + [[package]] name = "ark-std" version = "0.4.0" @@ -645,6 +1047,161 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" +[[package]] +name = "asset-hub-westend-runtime" +version = "0.42.3" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" +dependencies = [ + "assets-common", + "bp-asset-hub-rococo", + "bp-asset-hub-westend", + "bp-bridge-hub-rococo", + "bp-bridge-hub-westend", + "cumulus-pallet-aura-ext", + "cumulus-pallet-parachain-system", + "cumulus-pallet-session-benchmarking", + "cumulus-pallet-weight-reclaim", + "cumulus-pallet-xcm", + "cumulus-pallet-xcmp-queue", + "cumulus-primitives-aura", + "cumulus-primitives-core", + "cumulus-primitives-utility", + "frame-benchmarking", + "frame-election-provider-support", + "frame-executive", + "frame-metadata-hash-extension", + "frame-support", + "frame-system", + "frame-system-benchmarking", + "frame-system-rpc-runtime-api", + "frame-try-runtime", + "hex-literal", + "log", + "pallet-ah-ops", + "pallet-asset-conversion", + "pallet-asset-conversion-ops", + "pallet-asset-conversion-tx-payment", + "pallet-asset-rate", + "pallet-asset-rewards", + "pallet-assets", + "pallet-assets-freezer", + "pallet-assets-precompiles", + "pallet-aura", + "pallet-authorship", + "pallet-bags-list", + "pallet-balances", + "pallet-collator-selection", + "pallet-conviction-voting", + "pallet-dap", + "pallet-delegated-staking", + "pallet-election-provider-multi-block", + "pallet-fast-unstake", + "pallet-indices", + "pallet-message-queue", + "pallet-migrations", + "pallet-multi-asset-bounties", + "pallet-multisig", + "pallet-nft-fractionalization", + "pallet-nfts", + "pallet-nfts-runtime-api", + "pallet-nomination-pools", + "pallet-nomination-pools-runtime-api", + "pallet-preimage", + "pallet-proxy", + "pallet-referenda", + "pallet-revive", + "pallet-scheduler", + "pallet-session", + "pallet-staking", + "pallet-staking-async", + "pallet-staking-async-rc-client", + "pallet-staking-runtime-api", + "pallet-state-trie-migration", + "pallet-sudo", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-treasury", + "pallet-uniques", + "pallet-utility", + "pallet-vesting", + "pallet-whitelist", + "pallet-xcm", + "pallet-xcm-benchmarks", + "pallet-xcm-bridge-hub-router", + "pallet-xcm-precompiles", + "parachains-common", + "parity-scale-codec", + "polkadot-parachain-primitives", + "polkadot-primitives", + "polkadot-runtime-common", + "primitive-types 0.13.1", + "scale-info", + "serde_json", + "snowbridge-outbound-queue-primitives", + "snowbridge-pallet-system-frontend", + "snowbridge-runtime-common", + "sp-api", + "sp-arithmetic", + "sp-authority-discovery", + "sp-block-builder", + "sp-consensus-aura", + "sp-consensus-babe", + "sp-consensus-beefy", + "sp-consensus-grandpa", + "sp-core", + "sp-genesis-builder", + "sp-inherents", + "sp-io", + "sp-keyring", + "sp-npos-elections", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std", + "sp-storage", + "sp-transaction-pool", + "sp-version", + "staging-parachain-info", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", + "substrate-wasm-builder", + "testnet-parachains-constants", + "tracing", + "westend-runtime-constants", + "xcm-runtime-apis", +] + +[[package]] +name = "assets-common" +version = "0.27.1" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" +dependencies = [ + "cumulus-primitives-core", + "ethereum-standards", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "pallet-asset-conversion", + "pallet-assets", + "pallet-revive", + "pallet-revive-uapi", + "pallet-xcm", + "parachains-common", + "parity-scale-codec", + "scale-info", + "serde", + "sp-api", + "sp-core", + "sp-runtime", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", + "tracing", +] + [[package]] name = "async-backing-primitives" version = "0.9.0" @@ -882,6 +1439,16 @@ dependencies = [ "url", ] +[[package]] +name = "aurora-engine-modexp" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "518bc5745a6264b5fd7b09dffb9667e400ee9e2bbe18555fac75e1fe9afa0df9" +dependencies = [ + "hex", + "num", +] + [[package]] name = "auto_impl" version = "1.3.0" @@ -899,6 +1466,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "az" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be5eb007b7cacc6c660343e96f650fedf4b5a77512399eb952ca6642cf8d13f7" + [[package]] name = "backtrace" version = "0.3.76" @@ -957,7 +1530,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "binary-merkle-tree" version = "16.1.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "hash-db", "log", @@ -973,7 +1546,7 @@ dependencies = [ "bitflags 2.11.0", "cexpr", "clang-sys", - "itertools 0.13.0", + "itertools 0.10.5", "proc-macro2", "quote", "regex", @@ -1014,6 +1587,21 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + [[package]] name = "bitcoin-io" version = "0.1.4" @@ -1142,32 +1730,104 @@ dependencies = [ "piper", ] +[[package]] +name = "blst" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcdb4c7013139a150f9fc55d123186dbfaba0d912817466282c73ac49e71fb45" +dependencies = [ + "cc", + "glob", + "threadpool", + "zeroize", +] + +[[package]] +name = "borsh" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfd1e3f8955a5d7de9fab72fc8373fade9fb8a703968cb200ae3dc6cf08e185a" +dependencies = [ + "borsh-derive", + "bytes", + "cfg_aliases 0.2.1", +] + +[[package]] +name = "borsh-derive" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfcfdc083699101d5a7965e49925975f2f55060f94f9a05e7187be95d530ca59" +dependencies = [ + "once_cell", + "proc-macro-crate 3.5.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "bounded-collections" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dee8eddd066a8825ec5570528e6880471210fd5d88cb6abbe1cfdd51ca249c33" dependencies = [ - "jam-codec", - "log", + "jam-codec", + "log", + "parity-scale-codec", + "scale-info", + "serde", +] + +[[package]] +name = "bounded-vec" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68534a48cbf63a4b1323c433cf21238c9ec23711e0df13b08c33e5c2082663ce" +dependencies = [ + "thiserror 1.0.69", +] + +[[package]] +name = "bp-asset-hub-rococo" +version = "0.23.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" +dependencies = [ + "bp-bridge-hub-cumulus", + "bp-messages", + "bp-runtime", + "bp-xcm-bridge-hub-router", + "frame-support", "parity-scale-codec", "scale-info", - "serde", + "sp-api", + "sp-core", + "staging-xcm", + "testnet-parachains-constants", ] [[package]] -name = "bounded-vec" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68534a48cbf63a4b1323c433cf21238c9ec23711e0df13b08c33e5c2082663ce" +name = "bp-asset-hub-westend" +version = "0.22.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ - "thiserror 1.0.69", + "bp-bridge-hub-cumulus", + "bp-messages", + "bp-runtime", + "bp-xcm-bridge-hub-router", + "frame-support", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-core", + "staging-xcm", + "testnet-parachains-constants", ] [[package]] name = "bp-bridge-hub-cumulus" version = "0.27.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "bp-messages", "bp-polkadot-core", @@ -1180,10 +1840,42 @@ dependencies = [ "sp-std", ] +[[package]] +name = "bp-bridge-hub-rococo" +version = "0.27.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" +dependencies = [ + "bp-bridge-hub-cumulus", + "bp-messages", + "bp-runtime", + "bp-xcm-bridge-hub", + "frame-support", + "parity-scale-codec", + "sp-api", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "bp-bridge-hub-westend" +version = "0.23.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" +dependencies = [ + "bp-bridge-hub-cumulus", + "bp-messages", + "bp-runtime", + "bp-xcm-bridge-hub", + "frame-support", + "parity-scale-codec", + "sp-api", + "sp-runtime", + "sp-std", +] + [[package]] name = "bp-header-chain" version = "0.25.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "bp-runtime", "finality-grandpa", @@ -1200,7 +1892,7 @@ dependencies = [ [[package]] name = "bp-messages" version = "0.25.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "bp-header-chain", "bp-runtime", @@ -1248,7 +1940,7 @@ dependencies = [ [[package]] name = "bp-parachains" version = "0.25.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "bp-header-chain", "bp-polkadot-core", @@ -1265,7 +1957,7 @@ dependencies = [ [[package]] name = "bp-polkadot-core" version = "0.25.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "bp-messages", "bp-runtime", @@ -1282,7 +1974,7 @@ dependencies = [ [[package]] name = "bp-relayers" version = "0.25.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "bp-header-chain", "bp-messages", @@ -1300,7 +1992,7 @@ dependencies = [ [[package]] name = "bp-runtime" version = "0.25.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-support", "frame-system", @@ -1323,7 +2015,7 @@ dependencies = [ [[package]] name = "bp-test-utils" version = "0.25.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "bp-header-chain", "bp-parachains", @@ -1343,7 +2035,7 @@ dependencies = [ [[package]] name = "bp-xcm-bridge-hub" version = "0.11.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "bp-messages", "bp-runtime", @@ -1360,7 +2052,7 @@ dependencies = [ [[package]] name = "bp-xcm-bridge-hub-router" version = "0.22.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "parity-scale-codec", "scale-info", @@ -1372,7 +2064,7 @@ dependencies = [ [[package]] name = "bridge-hub-common" version = "0.18.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "cumulus-primitives-core", "frame-support", @@ -1391,7 +2083,7 @@ dependencies = [ [[package]] name = "bridge-runtime-common" version = "0.26.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "bp-header-chain", "bp-messages", @@ -1487,6 +2179,9 @@ name = "bytes" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +dependencies = [ + "serde", +] [[package]] name = "bzip2-sys" @@ -1498,6 +2193,21 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "c-kzg" +version = "2.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6648ed1e4ea8e8a1a4a2c78e1cda29a3fd500bc622899c340d8525ea9a76b24a" +dependencies = [ + "blst", + "cc", + "glob", + "hex", + "libc", + "once_cell", + "serde", +] + [[package]] name = "c2-chacha" version = "0.3.3" @@ -1846,6 +2556,16 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "const-crypto" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c06f1eb05f06cf2e380fdded278fbf056a38974299d77960555a311dcf91a52" +dependencies = [ + "keccak-const", + "sha2-const-stable", +] + [[package]] name = "const-hex" version = "1.18.1" @@ -2295,7 +3015,7 @@ dependencies = [ [[package]] name = "cumulus-client-bootnodes" version = "0.7.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "array-bytes 6.2.3", "async-channel 1.9.0", @@ -2319,7 +3039,7 @@ dependencies = [ [[package]] name = "cumulus-client-cli" version = "0.28.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "clap", "parity-scale-codec", @@ -2336,7 +3056,7 @@ dependencies = [ [[package]] name = "cumulus-client-collator" version = "0.28.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "cumulus-client-consensus-common", "cumulus-client-network", @@ -2359,7 +3079,7 @@ dependencies = [ [[package]] name = "cumulus-client-consensus-aura" version = "0.28.1" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "async-trait", "cumulus-client-collator", @@ -2407,7 +3127,7 @@ dependencies = [ [[package]] name = "cumulus-client-consensus-common" version = "0.28.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "async-trait", "cumulus-client-pov-recovery", @@ -2439,7 +3159,7 @@ dependencies = [ [[package]] name = "cumulus-client-consensus-proposer" version = "0.24.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "anyhow", "async-trait", @@ -2459,7 +3179,7 @@ dependencies = [ [[package]] name = "cumulus-client-consensus-relay-chain" version = "0.28.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "async-trait", "cumulus-client-consensus-common", @@ -2482,7 +3202,7 @@ dependencies = [ [[package]] name = "cumulus-client-network" version = "0.28.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "async-trait", "cumulus-relay-chain-interface", @@ -2510,7 +3230,7 @@ dependencies = [ [[package]] name = "cumulus-client-parachain-inherent" version = "0.22.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "async-trait", "cumulus-primitives-core", @@ -2532,7 +3252,7 @@ dependencies = [ [[package]] name = "cumulus-client-pov-recovery" version = "0.28.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "async-trait", "cumulus-primitives-core", @@ -2560,7 +3280,7 @@ dependencies = [ [[package]] name = "cumulus-client-service" version = "0.31.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "async-channel 1.9.0", "cumulus-client-cli", @@ -2600,10 +3320,27 @@ dependencies = [ "sp-trie", ] +[[package]] +name = "cumulus-pallet-aura-ext" +version = "0.25.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" +dependencies = [ + "cumulus-pallet-parachain-system", + "frame-support", + "frame-system", + "pallet-aura", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-application-crypto", + "sp-consensus-aura", + "sp-runtime", +] + [[package]] name = "cumulus-pallet-parachain-system" version = "0.25.1" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "array-bytes 6.2.3", "bytes", @@ -2641,7 +3378,7 @@ dependencies = [ [[package]] name = "cumulus-pallet-parachain-system-proc-macro" version = "0.7.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "proc-macro-crate 3.5.0", "proc-macro2", @@ -2649,10 +3386,23 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "cumulus-pallet-session-benchmarking" +version = "26.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-session", + "parity-scale-codec", + "sp-runtime", +] + [[package]] name = "cumulus-pallet-weight-reclaim" version = "0.7.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "cumulus-primitives-storage-weight-reclaim", "derive-where", @@ -2671,7 +3421,7 @@ dependencies = [ [[package]] name = "cumulus-pallet-xcm" version = "0.24.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "cumulus-primitives-core", "frame-support", @@ -2686,7 +3436,7 @@ dependencies = [ [[package]] name = "cumulus-pallet-xcmp-queue" version = "0.25.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "approx", "bounded-collections", @@ -2712,7 +3462,7 @@ dependencies = [ [[package]] name = "cumulus-primitives-aura" version = "0.21.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "sp-api", "sp-consensus-aura", @@ -2721,7 +3471,7 @@ dependencies = [ [[package]] name = "cumulus-primitives-core" version = "0.23.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "parity-scale-codec", "polkadot-core-primitives", @@ -2738,7 +3488,7 @@ dependencies = [ [[package]] name = "cumulus-primitives-parachain-inherent" version = "0.23.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "async-trait", "cumulus-primitives-core", @@ -2752,7 +3502,7 @@ dependencies = [ [[package]] name = "cumulus-primitives-proof-size-hostfunction" version = "0.16.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "sp-externalities", "sp-runtime-interface", @@ -2762,7 +3512,7 @@ dependencies = [ [[package]] name = "cumulus-primitives-storage-weight-reclaim" version = "16.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "cumulus-primitives-core", "cumulus-primitives-proof-size-hostfunction", @@ -2779,7 +3529,7 @@ dependencies = [ [[package]] name = "cumulus-primitives-utility" version = "0.25.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "cumulus-primitives-core", "frame-support", @@ -2796,7 +3546,7 @@ dependencies = [ [[package]] name = "cumulus-relay-chain-inprocess-interface" version = "0.31.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "async-channel 1.9.0", "async-trait", @@ -2824,7 +3574,7 @@ dependencies = [ [[package]] name = "cumulus-relay-chain-interface" version = "0.28.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "async-trait", "cumulus-primitives-core", @@ -2844,7 +3594,7 @@ dependencies = [ [[package]] name = "cumulus-relay-chain-minimal-node" version = "0.31.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "array-bytes 6.2.3", "async-channel 1.9.0", @@ -2880,7 +3630,7 @@ dependencies = [ [[package]] name = "cumulus-relay-chain-rpc-interface" version = "0.28.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "async-trait", "cumulus-primitives-core", @@ -2913,7 +3663,7 @@ dependencies = [ [[package]] name = "cumulus-relay-chain-streams" version = "0.6.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "cumulus-relay-chain-interface", "futures 0.3.32", @@ -2927,7 +3677,7 @@ dependencies = [ [[package]] name = "cumulus-test-relay-sproof-builder" version = "0.24.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "cumulus-primitives-core", "parity-scale-codec", @@ -2950,7 +3700,7 @@ dependencies = [ "curve25519-dalek-derive", "digest 0.10.7", "fiat-crypto", - "rustc_version", + "rustc_version 0.4.1", "subtle 2.6.1", "zeroize", ] @@ -2989,7 +3739,7 @@ checksum = "b0f4697d190a142477b16aef7da8a99bfdc41e7e8b1687583c0d23a79c7afc1e" dependencies = [ "cc", "codespan-reporting", - "indexmap", + "indexmap 2.13.0", "proc-macro2", "quote", "scratch", @@ -3004,7 +3754,7 @@ checksum = "d0956799fa8678d4c50eed028f2de1c0552ae183c76e976cf7ca8c4e36a7c328" dependencies = [ "clap", "codespan-reporting", - "indexmap", + "indexmap 2.13.0", "proc-macro2", "quote", "syn 2.0.117", @@ -3022,7 +3772,7 @@ version = "1.0.194" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6acc6b5822b9526adfb4fc377b67128fdd60aac757cc4a741a6278603f763cf" dependencies = [ - "indexmap", + "indexmap 2.13.0", "proc-macro2", "quote", "syn 2.0.117", @@ -3072,6 +3822,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", + "serde", "strsim", "syn 2.0.117", ] @@ -3192,6 +3943,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ "powerfmt", + "serde_core", ] [[package]] @@ -3236,7 +3988,7 @@ dependencies = [ "convert_case 0.4.0", "proc-macro2", "quote", - "rustc_version", + "rustc_version 0.4.1", "syn 2.0.117", ] @@ -3279,7 +4031,7 @@ dependencies = [ "convert_case 0.10.0", "proc-macro2", "quote", - "rustc_version", + "rustc_version 0.4.1", "syn 2.0.117", "unicode-xid", ] @@ -3427,6 +4179,12 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590" +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + [[package]] name = "dyn-clonable" version = "0.9.2" @@ -3681,7 +4439,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.59.0", +] + +[[package]] +name = "ethabi-decode" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52029c4087f9f01108f851d0d02df9c21feb5660a19713466724b7f95bd2d773" +dependencies = [ + "ethereum-types", + "tiny-keccak", ] [[package]] @@ -3692,7 +4460,7 @@ checksum = "8c321610643004cf908ec0f5f2aa0d8f1f8e14b540562a2887a1111ff1ecbf7b" dependencies = [ "crunchy", "fixed-hash", - "impl-codec", + "impl-codec 0.7.1", "impl-rlp", "impl-serde", "scale-info", @@ -3710,13 +4478,21 @@ dependencies = [ "hash256-std-hasher", "k256", "parity-scale-codec", - "rlp", + "rlp 0.6.1", "scale-info", "serde", "sha3", "trie-root", ] +[[package]] +name = "ethereum-standards" +version = "0.1.2" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" +dependencies = [ + "alloy-core", +] + [[package]] name = "ethereum-types" version = "0.15.1" @@ -3725,10 +4501,10 @@ checksum = "1ab15ed80916029f878e0267c3a9f92b67df55e79af370bf66199059ae2b4ee3" dependencies = [ "ethbloom", "fixed-hash", - "impl-codec", + "impl-codec 0.7.1", "impl-rlp", "impl-serde", - "primitive-types", + "primitive-types 0.13.1", "scale-info", "uint 0.10.0", ] @@ -3773,8 +4549,8 @@ dependencies = [ "evm-runtime", "log", "parity-scale-codec", - "primitive-types", - "rlp", + "primitive-types 0.13.1", + "rlp 0.6.1", "scale-info", "serde", "sha3", @@ -3786,7 +4562,7 @@ version = "0.43.0" source = "git+https://github.com/moonbeam-foundation/evm?branch=moonbeam-polkadot-stable2512#bb9cdde4034856f055cca7208375e11741245b49" dependencies = [ "parity-scale-codec", - "primitive-types", + "primitive-types 0.13.1", "scale-info", "serde", ] @@ -3799,7 +4575,7 @@ dependencies = [ "environmental", "evm-core", "evm-runtime", - "primitive-types", + "primitive-types 0.13.1", ] [[package]] @@ -3810,7 +4586,7 @@ dependencies = [ "auto_impl", "environmental", "evm-core", - "primitive-types", + "primitive-types 0.13.1", "sha3", ] @@ -3871,6 +4647,28 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "fastrlp" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" +dependencies = [ + "arrayvec 0.7.6", + "auto_impl", + "bytes", +] + +[[package]] +name = "fastrlp" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce8dba4714ef14b8274c371879b175aa55b16b30f269663f19d576f380018dc4" +dependencies = [ + "arrayvec 0.7.6", + "auto_impl", + "bytes", +] + [[package]] name = "fatality" version = "0.1.1" @@ -3888,7 +4686,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb42427514b063d97ce21d5199f36c0c307d981434a6be32582bc79fe5bd2303" dependencies = [ "expander", - "indexmap", + "indexmap 2.13.0", "proc-macro-crate 3.5.0", "proc-macro2", "quote", @@ -4001,7 +4799,7 @@ dependencies = [ "parity-scale-codec", "prometheus", "rand 0.9.2", - "rlp", + "rlp 0.6.1", "sc-client-api", "sc-consensus-aura", "sc-network", @@ -4039,7 +4837,7 @@ dependencies = [ "ethereum", "ethereum-types", "jsonrpsee", - "rlp", + "rlp 0.6.1", "rustc-hex", "serde", "serde_json", @@ -4238,7 +5036,7 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "fork-tree" version = "13.0.1" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "parity-scale-codec", ] @@ -4365,7 +5163,7 @@ checksum = "28dd6caf6059519a65843af8fe2a3ae298b14b80179855aeb4adc2c1934ee619" [[package]] name = "frame-benchmarking" version = "45.0.3" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-support", "frame-support-procedural", @@ -4389,7 +5187,7 @@ dependencies = [ [[package]] name = "frame-benchmarking-cli" version = "53.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "Inflector", "array-bytes 6.2.3", @@ -4468,7 +5266,7 @@ dependencies = [ [[package]] name = "frame-election-provider-solution-type" version = "16.1.1" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "proc-macro-crate 3.5.0", "proc-macro2", @@ -4479,7 +5277,7 @@ dependencies = [ [[package]] name = "frame-election-provider-support" version = "45.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-election-provider-solution-type", "frame-support", @@ -4496,7 +5294,7 @@ dependencies = [ [[package]] name = "frame-executive" version = "45.0.1" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "aquamarine", "frame-support", @@ -4526,7 +5324,7 @@ dependencies = [ [[package]] name = "frame-metadata-hash-extension" version = "0.13.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "array-bytes 6.2.3", "const-hex", @@ -4542,7 +5340,7 @@ dependencies = [ [[package]] name = "frame-storage-access-test-runtime" version = "0.6.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "cumulus-pallet-parachain-system", "parity-scale-codec", @@ -4556,7 +5354,7 @@ dependencies = [ [[package]] name = "frame-support" version = "45.1.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "aquamarine", "array-bytes 6.2.3", @@ -4597,7 +5395,7 @@ dependencies = [ [[package]] name = "frame-support-procedural" version = "36.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "Inflector", "cfg-expr", @@ -4617,7 +5415,7 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools" version = "13.0.1" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-support-procedural-tools-derive", "proc-macro-crate 3.5.0", @@ -4629,7 +5427,7 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools-derive" version = "12.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "proc-macro2", "quote", @@ -4639,7 +5437,7 @@ dependencies = [ [[package]] name = "frame-system" version = "45.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "cfg-if", "docify", @@ -4658,7 +5456,7 @@ dependencies = [ [[package]] name = "frame-system-benchmarking" version = "45.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-benchmarking", "frame-support", @@ -4672,7 +5470,7 @@ dependencies = [ [[package]] name = "frame-system-rpc-runtime-api" version = "40.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "docify", "parity-scale-codec", @@ -4682,7 +5480,7 @@ dependencies = [ [[package]] name = "frame-try-runtime" version = "0.51.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-support", "parity-scale-codec", @@ -5019,7 +5817,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" dependencies = [ "fallible-iterator", - "indexmap", + "indexmap 2.13.0", "stable_deref_trait", ] @@ -5035,6 +5833,16 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +[[package]] +name = "gmp-mpfr-sys" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cfc928d8ff4ab3767a3674cf55f81186436fb6070866bb1443ffe65a640d2d6" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + [[package]] name = "governor" version = "0.6.3" @@ -5078,7 +5886,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap", + "indexmap 2.13.0", "slab", "tokio", "tokio-util", @@ -5097,7 +5905,7 @@ dependencies = [ "futures-core", "futures-sink", "http 1.4.0", - "indexmap", + "indexmap 2.13.0", "slab", "tokio", "tokio-util", @@ -5178,6 +5986,11 @@ name = "hashbrown" version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "foldhash 0.2.0", + "serde", + "serde_core", +] [[package]] name = "hashlink" @@ -5443,6 +6256,22 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "humantime" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" + +[[package]] +name = "humantime-serde" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c" +dependencies = [ + "humantime", + "serde", +] + [[package]] name = "hyper" version = "0.14.32" @@ -5718,6 +6547,15 @@ dependencies = [ "xmltree", ] +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + [[package]] name = "impl-codec" version = "0.7.1" @@ -5744,7 +6582,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54ed8ad1f3877f7e775b8cbf30ed1bd3209a95401817f19a0eb4402d13f8cf90" dependencies = [ - "rlp", + "rlp 0.6.1", ] [[package]] @@ -5786,6 +6624,17 @@ dependencies = [ "quote", ] +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + [[package]] name = "indexmap" version = "2.13.0" @@ -5868,7 +6717,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi 0.5.2", "libc", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -5904,15 +6753,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.13.0" @@ -6206,13 +7046,29 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "keccak-asm" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa468878266ad91431012b3e5ef1bf9b170eab22883503a318d46857afa4579a" +dependencies = [ + "digest 0.10.7", + "sha3-asm", +] + +[[package]] +name = "keccak-const" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d8d8ce877200136358e0bbff3a77965875db3af755a11e1fa6b1b3e2df13ea" + [[package]] name = "keccak-hash" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e1b8590eb6148af2ea2d75f38e7d29f5ca970d5a4df456b3ef19b8b415d0264" dependencies = [ - "primitive-types", + "primitive-types 0.13.1", "tiny-keccak", ] @@ -6895,7 +7751,7 @@ dependencies = [ "futures 0.3.32", "futures-timer", "hickory-resolver 0.25.2", - "indexmap", + "indexmap 2.13.0", "ip_network", "libc", "mockall", @@ -7006,6 +7862,17 @@ dependencies = [ "libc", ] +[[package]] +name = "macro-string" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "macro_magic" version = "0.5.1" @@ -7214,7 +8081,7 @@ dependencies = [ [[package]] name = "mmr-gadget" version = "50.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "futures 0.3.32", "log", @@ -7233,7 +8100,7 @@ dependencies = [ [[package]] name = "mmr-rpc" version = "44.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "jsonrpsee", "parity-scale-codec", @@ -7293,6 +8160,7 @@ name = "moonbase-runtime" version = "0.8.4" dependencies = [ "account", + "asset-hub-westend-runtime", "async-backing-primitives", "cumulus-pallet-parachain-system", "cumulus-pallet-weight-reclaim", @@ -7408,7 +8276,7 @@ dependencies = [ "polkadot-runtime-common", "polkadot-runtime-parachains", "precompile-utils", - "rlp", + "rlp 0.6.1", "scale-info", "serde", "serde_json", @@ -7439,10 +8307,11 @@ dependencies = [ "strum 0.26.3", "strum_macros 0.24.3", "substrate-wasm-builder", + "westend-runtime", + "xcm-emulator", "xcm-primitives 0.1.0", "xcm-primitives 0.1.1", "xcm-runtime-apis", - "xcm-simulator", ] [[package]] @@ -7490,7 +8359,7 @@ dependencies = [ "clap", "libsecp256k1", "polkadot-omni-node-lib", - "primitive-types", + "primitive-types 0.13.1", "sp-runtime", "tiny-bip39", "url", @@ -7689,6 +8558,7 @@ name = "moonbeam-runtime" version = "0.8.4" dependencies = [ "account", + "asset-hub-westend-runtime", "async-backing-primitives", "bp-header-chain", "bp-messages", @@ -7745,6 +8615,7 @@ dependencies = [ "pallet-collective", "pallet-conviction-voting", "pallet-crowdloan-rewards", + "pallet-delegated-staking", "pallet-emergency-para-xcm", "pallet-erc20-xcm-bridge", "pallet-ethereum", @@ -7816,7 +8687,7 @@ dependencies = [ "polkadot-runtime-common", "polkadot-runtime-parachains", "precompile-utils", - "rlp", + "rlp 0.6.1", "scale-info", "serde", "serde_json", @@ -7846,10 +8717,11 @@ dependencies = [ "strum 0.26.3", "strum_macros 0.24.3", "substrate-wasm-builder", + "westend-runtime", + "xcm-emulator", "xcm-primitives 0.1.0", "xcm-primitives 0.1.1", "xcm-runtime-apis", - "xcm-simulator", ] [[package]] @@ -8038,6 +8910,7 @@ name = "moonriver-runtime" version = "0.8.4" dependencies = [ "account", + "asset-hub-westend-runtime", "async-backing-primitives", "bp-header-chain", "bp-messages", @@ -8165,7 +9038,7 @@ dependencies = [ "polkadot-runtime-common", "polkadot-runtime-parachains", "precompile-utils", - "rlp", + "rlp 0.6.1", "scale-info", "serde", "serde_json", @@ -8196,10 +9069,11 @@ dependencies = [ "strum 0.26.3", "strum_macros 0.24.3", "substrate-wasm-builder", + "westend-runtime", + "xcm-emulator", "xcm-primitives 0.1.0", "xcm-primitives 0.1.1", "xcm-runtime-apis", - "xcm-simulator", ] [[package]] @@ -8584,7 +9458,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -8730,6 +9604,20 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "nybbles" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d49ff0c0d00d4a502b39df9af3a525e1efeb14b9dabb5bb83335284c1309210" +dependencies = [ + "alloy-rlp", + "cfg-if", + "proptest", + "ruint", + "serde", + "smallvec", +] + [[package]] name = "object" version = "0.36.7" @@ -8738,7 +9626,7 @@ checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "crc32fast", "hashbrown 0.15.5", - "indexmap", + "indexmap 2.13.0", "memchr", ] @@ -8871,7 +9759,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43dfaf083aef571385fccfdc3a2f8ede8d0a1863160455d4f2b014d8f7d04a3f" dependencies = [ "expander", - "indexmap", + "indexmap 2.13.0", "itertools 0.11.0", "petgraph 0.6.5", "proc-macro-crate 3.5.0", @@ -8893,41 +9781,113 @@ dependencies = [ ] [[package]] -name = "pallet-asset-conversion" -version = "27.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +name = "pallet-ah-ops" +version = "0.5.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-balances", + "pallet-timestamp", + "pallet-utility", + "parity-scale-codec", + "scale-info", + "sp-application-crypto", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-asset-conversion" +version = "27.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", +] + +[[package]] +name = "pallet-asset-conversion-ops" +version = "0.13.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-benchmarking", "frame-support", "frame-system", "log", + "pallet-asset-conversion", "parity-scale-codec", "scale-info", - "sp-api", "sp-arithmetic", "sp-core", "sp-io", "sp-runtime", ] +[[package]] +name = "pallet-asset-conversion-tx-payment" +version = "27.0.1" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-asset-conversion", + "pallet-transaction-payment", + "parity-scale-codec", + "scale-info", + "sp-runtime", +] + [[package]] name = "pallet-asset-rate" version = "24.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", +] + +[[package]] +name = "pallet-asset-rewards" +version = "0.7.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-benchmarking", "frame-support", "frame-system", "parity-scale-codec", "scale-info", + "sp-api", + "sp-arithmetic", "sp-core", + "sp-io", "sp-runtime", + "sp-std", ] [[package]] name = "pallet-asset-tx-payment" version = "45.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-benchmarking", "frame-support", @@ -8943,7 +9903,7 @@ dependencies = [ [[package]] name = "pallet-assets" version = "48.1.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-benchmarking", "frame-support", @@ -8956,6 +9916,29 @@ dependencies = [ "sp-runtime", ] +[[package]] +name = "pallet-assets-freezer" +version = "0.13.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" +dependencies = [ + "log", + "pallet-assets", + "parity-scale-codec", + "polkadot-sdk-frame", + "scale-info", +] + +[[package]] +name = "pallet-assets-precompiles" +version = "0.4.1" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" +dependencies = [ + "ethereum-standards", + "frame-support", + "pallet-assets", + "pallet-revive", +] + [[package]] name = "pallet-async-backing" version = "0.9.0" @@ -8976,6 +9959,22 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-aura" +version = "44.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" +dependencies = [ + "frame-support", + "frame-system", + "log", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-application-crypto", + "sp-consensus-aura", + "sp-runtime", +] + [[package]] name = "pallet-author-inherent" version = "0.9.0" @@ -9036,7 +10035,7 @@ dependencies = [ [[package]] name = "pallet-authority-discovery" version = "45.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-support", "frame-system", @@ -9051,7 +10050,7 @@ dependencies = [ [[package]] name = "pallet-authorship" version = "45.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-support", "frame-system", @@ -9064,7 +10063,7 @@ dependencies = [ [[package]] name = "pallet-babe" version = "45.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-benchmarking", "frame-support", @@ -9087,7 +10086,7 @@ dependencies = [ [[package]] name = "pallet-bags-list" version = "44.0.1" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "aquamarine", "docify", @@ -9108,7 +10107,7 @@ dependencies = [ [[package]] name = "pallet-balances" version = "46.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "docify", "frame-benchmarking", @@ -9124,7 +10123,7 @@ dependencies = [ [[package]] name = "pallet-beefy" version = "46.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-support", "frame-system", @@ -9143,7 +10142,7 @@ dependencies = [ [[package]] name = "pallet-beefy-mmr" version = "46.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "array-bytes 6.2.3", "binary-merkle-tree", @@ -9168,7 +10167,7 @@ dependencies = [ [[package]] name = "pallet-bounties" version = "44.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-benchmarking", "frame-support", @@ -9185,7 +10184,7 @@ dependencies = [ [[package]] name = "pallet-bridge-grandpa" version = "0.25.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "bp-header-chain", "bp-runtime", @@ -9204,7 +10203,7 @@ dependencies = [ [[package]] name = "pallet-bridge-messages" version = "0.25.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "bp-header-chain", "bp-messages", @@ -9223,7 +10222,7 @@ dependencies = [ [[package]] name = "pallet-bridge-parachains" version = "0.25.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "bp-header-chain", "bp-parachains", @@ -9243,7 +10242,7 @@ dependencies = [ [[package]] name = "pallet-bridge-relayers" version = "0.25.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "bp-header-chain", "bp-messages", @@ -9266,7 +10265,7 @@ dependencies = [ [[package]] name = "pallet-broker" version = "0.24.1" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "bitvec", "frame-benchmarking", @@ -9284,7 +10283,7 @@ dependencies = [ [[package]] name = "pallet-child-bounties" version = "44.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-benchmarking", "frame-support", @@ -9302,7 +10301,7 @@ dependencies = [ [[package]] name = "pallet-collator-selection" version = "26.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-benchmarking", "frame-support", @@ -9321,7 +10320,7 @@ dependencies = [ [[package]] name = "pallet-collective" version = "45.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "docify", "frame-benchmarking", @@ -9338,7 +10337,7 @@ dependencies = [ [[package]] name = "pallet-conviction-voting" version = "45.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "assert_matches", "frame-benchmarking", @@ -9372,10 +10371,24 @@ dependencies = [ "sp-trie", ] +[[package]] +name = "pallet-dap" +version = "0.2.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-runtime", +] + [[package]] name = "pallet-delegated-staking" version = "12.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-support", "frame-system", @@ -9390,7 +10403,7 @@ dependencies = [ [[package]] name = "pallet-democracy" version = "45.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-benchmarking", "frame-support", @@ -9404,10 +10417,31 @@ dependencies = [ "sp-runtime", ] +[[package]] +name = "pallet-election-provider-multi-block" +version = "0.6.1" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" +dependencies = [ + "frame-benchmarking", + "frame-election-provider-support", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "rand 0.8.5", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-npos-elections", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-election-provider-multi-phase" version = "44.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -9428,7 +10462,7 @@ dependencies = [ [[package]] name = "pallet-election-provider-support-benchmarking" version = "44.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -9441,7 +10475,7 @@ dependencies = [ [[package]] name = "pallet-elections-phragmen" version = "46.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-benchmarking", "frame-support", @@ -9544,7 +10578,7 @@ dependencies = [ "pallet-proxy", "pallet-timestamp", "parity-scale-codec", - "rlp", + "rlp 0.6.1", "scale-info", "sp-core", "sp-io", @@ -10167,7 +11201,7 @@ dependencies = [ [[package]] name = "pallet-fast-unstake" version = "44.1.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "docify", "frame-benchmarking", @@ -10185,7 +11219,7 @@ dependencies = [ [[package]] name = "pallet-grandpa" version = "45.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-benchmarking", "frame-support", @@ -10207,7 +11241,7 @@ dependencies = [ [[package]] name = "pallet-identity" version = "45.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "enumflags2", "frame-benchmarking", @@ -10223,7 +11257,7 @@ dependencies = [ [[package]] name = "pallet-im-online" version = "44.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-benchmarking", "frame-support", @@ -10242,7 +11276,7 @@ dependencies = [ [[package]] name = "pallet-indices" version = "45.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-benchmarking", "frame-support", @@ -10273,7 +11307,7 @@ dependencies = [ [[package]] name = "pallet-message-queue" version = "48.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "environmental", "frame-benchmarking", @@ -10292,7 +11326,7 @@ dependencies = [ [[package]] name = "pallet-meta-tx" version = "0.7.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "docify", "frame-benchmarking", @@ -10310,7 +11344,7 @@ dependencies = [ [[package]] name = "pallet-migrations" version = "15.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "docify", "frame-benchmarking", @@ -10329,7 +11363,7 @@ dependencies = [ [[package]] name = "pallet-mmr" version = "45.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "log", "parity-scale-codec", @@ -10378,7 +11412,7 @@ dependencies = [ "pallet-timestamp", "parity-scale-codec", "precompile-utils", - "rlp", + "rlp 0.6.1", "scale-info", "sp-core", "sp-io", @@ -10406,7 +11440,7 @@ dependencies = [ [[package]] name = "pallet-multi-asset-bounties" version = "0.2.1" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "docify", "frame-benchmarking", @@ -10423,18 +11457,57 @@ dependencies = [ [[package]] name = "pallet-multisig" version = "45.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" +dependencies = [ + "log", + "parity-scale-codec", + "polkadot-sdk-frame", + "scale-info", +] + +[[package]] +name = "pallet-nft-fractionalization" +version = "29.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "log", + "pallet-assets", + "pallet-nfts", "parity-scale-codec", "polkadot-sdk-frame", "scale-info", ] +[[package]] +name = "pallet-nfts" +version = "39.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" +dependencies = [ + "enumflags2", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", +] + +[[package]] +name = "pallet-nfts-runtime-api" +version = "30.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" +dependencies = [ + "parity-scale-codec", + "sp-api", +] + [[package]] name = "pallet-nis" version = "45.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "parity-scale-codec", "polkadot-sdk-frame", @@ -10444,7 +11517,7 @@ dependencies = [ [[package]] name = "pallet-nomination-pools" version = "43.1.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-support", "frame-system", @@ -10462,7 +11535,7 @@ dependencies = [ [[package]] name = "pallet-nomination-pools-benchmarking" version = "43.0.1" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -10482,7 +11555,7 @@ dependencies = [ [[package]] name = "pallet-nomination-pools-runtime-api" version = "41.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "pallet-nomination-pools", "parity-scale-codec", @@ -10492,7 +11565,7 @@ dependencies = [ [[package]] name = "pallet-offences" version = "44.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-support", "frame-system", @@ -10507,7 +11580,7 @@ dependencies = [ [[package]] name = "pallet-offences-benchmarking" version = "45.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -10552,7 +11625,7 @@ dependencies = [ [[package]] name = "pallet-parameters" version = "0.16.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "cumulus-primitives-storage-weight-reclaim", "docify", @@ -10590,7 +11663,7 @@ dependencies = [ [[package]] name = "pallet-preimage" version = "45.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-benchmarking", "frame-support", @@ -10606,7 +11679,7 @@ dependencies = [ [[package]] name = "pallet-proxy" version = "45.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "parity-scale-codec", "polkadot-sdk-frame", @@ -10659,7 +11732,7 @@ dependencies = [ [[package]] name = "pallet-ranked-collective" version = "45.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-benchmarking", "frame-support", @@ -10677,7 +11750,7 @@ dependencies = [ [[package]] name = "pallet-recovery" version = "45.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "parity-scale-codec", "polkadot-sdk-frame", @@ -10687,7 +11760,7 @@ dependencies = [ [[package]] name = "pallet-referenda" version = "45.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "assert_matches", "frame-benchmarking", @@ -10725,10 +11798,103 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-revive" +version = "0.12.2" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" +dependencies = [ + "alloy-consensus", + "alloy-core", + "alloy-trie", + "derive_more 0.99.20", + "environmental", + "ethereum-standards", + "ethereum-types", + "frame-benchmarking", + "frame-support", + "frame-system", + "hex-literal", + "humantime-serde", + "impl-trait-for-tuples", + "k256", + "log", + "num-bigint", + "num-integer", + "num-traits", + "pallet-revive-fixtures", + "pallet-revive-proc-macro", + "pallet-revive-uapi", + "pallet-transaction-payment", + "parity-scale-codec", + "paste", + "polkavm", + "polkavm-common", + "rand 0.8.5", + "revm", + "ripemd", + "rlp 0.6.1", + "scale-info", + "serde", + "serde_json", + "sp-api", + "sp-arithmetic", + "sp-consensus-aura", + "sp-consensus-babe", + "sp-consensus-slots", + "sp-core", + "sp-io", + "sp-runtime", + "sp-version", + "substrate-bn", + "subxt-signer", +] + +[[package]] +name = "pallet-revive-fixtures" +version = "0.9.1" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" +dependencies = [ + "alloy-core", + "anyhow", + "cargo_metadata", + "hex", + "pallet-revive-uapi", + "polkavm-linker", + "serde_json", + "sp-core", + "sp-io", + "toml 0.8.23", +] + +[[package]] +name = "pallet-revive-proc-macro" +version = "0.7.1" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "pallet-revive-uapi" +version = "0.10.1" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" +dependencies = [ + "alloy-core", + "bitflags 1.3.2", + "const-crypto", + "hex-literal", + "pallet-revive-proc-macro", + "parity-scale-codec", + "polkavm-derive", + "scale-info", +] + [[package]] name = "pallet-root-offences" version = "43.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-support", "frame-system", @@ -10744,7 +11910,7 @@ dependencies = [ [[package]] name = "pallet-root-testing" version = "21.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-support", "frame-system", @@ -10757,7 +11923,7 @@ dependencies = [ [[package]] name = "pallet-scheduler" version = "46.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "docify", "frame-benchmarking", @@ -10774,7 +11940,7 @@ dependencies = [ [[package]] name = "pallet-session" version = "45.2.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-support", "frame-system", @@ -10796,7 +11962,7 @@ dependencies = [ [[package]] name = "pallet-session-benchmarking" version = "45.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-benchmarking", "frame-support", @@ -10812,7 +11978,7 @@ dependencies = [ [[package]] name = "pallet-society" version = "45.1.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-benchmarking", "frame-support", @@ -10829,7 +11995,7 @@ dependencies = [ [[package]] name = "pallet-staking" version = "45.1.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -10848,10 +12014,35 @@ dependencies = [ "sp-staking", ] +[[package]] +name = "pallet-staking-async" +version = "0.10.4" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" +dependencies = [ + "frame-benchmarking", + "frame-election-provider-support", + "frame-support", + "frame-system", + "log", + "pallet-dap", + "pallet-staking-async-rc-client", + "parity-scale-codec", + "rand 0.8.5", + "rand_chacha 0.3.1", + "scale-info", + "serde", + "sp-application-crypto", + "sp-core", + "sp-io", + "sp-npos-elections", + "sp-runtime", + "sp-staking", +] + [[package]] name = "pallet-staking-async-ah-client" version = "0.7.2" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-benchmarking", "frame-support", @@ -10871,7 +12062,7 @@ dependencies = [ [[package]] name = "pallet-staking-async-rc-client" version = "0.7.3" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-benchmarking", "frame-support", @@ -10891,7 +12082,7 @@ dependencies = [ [[package]] name = "pallet-staking-reward-fn" version = "24.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "log", "sp-arithmetic", @@ -10900,7 +12091,7 @@ dependencies = [ [[package]] name = "pallet-staking-runtime-api" version = "30.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "parity-scale-codec", "sp-api", @@ -10910,7 +12101,7 @@ dependencies = [ [[package]] name = "pallet-state-trie-migration" version = "50.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-benchmarking", "frame-support", @@ -10926,7 +12117,7 @@ dependencies = [ [[package]] name = "pallet-sudo" version = "45.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "docify", "frame-benchmarking", @@ -10941,7 +12132,7 @@ dependencies = [ [[package]] name = "pallet-timestamp" version = "44.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "docify", "frame-benchmarking", @@ -10959,7 +12150,7 @@ dependencies = [ [[package]] name = "pallet-tips" version = "44.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-benchmarking", "frame-support", @@ -10977,7 +12168,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment" version = "45.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-benchmarking", "frame-support", @@ -10993,7 +12184,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment-rpc" version = "48.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "jsonrpsee", "pallet-transaction-payment-rpc-runtime-api", @@ -11009,7 +12200,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment-rpc-runtime-api" version = "45.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "pallet-transaction-payment", "parity-scale-codec", @@ -11021,7 +12212,7 @@ dependencies = [ [[package]] name = "pallet-treasury" version = "44.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "docify", "frame-benchmarking", @@ -11037,10 +12228,24 @@ dependencies = [ "sp-runtime", ] +[[package]] +name = "pallet-uniques" +version = "45.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-runtime", +] + [[package]] name = "pallet-utility" version = "45.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-benchmarking", "frame-support", @@ -11055,7 +12260,7 @@ dependencies = [ [[package]] name = "pallet-verify-signature" version = "0.8.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-benchmarking", "frame-support", @@ -11070,7 +12275,7 @@ dependencies = [ [[package]] name = "pallet-vesting" version = "45.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-benchmarking", "frame-support", @@ -11084,7 +12289,7 @@ dependencies = [ [[package]] name = "pallet-whitelist" version = "44.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "parity-scale-codec", "polkadot-sdk-frame", @@ -11094,7 +12299,7 @@ dependencies = [ [[package]] name = "pallet-xcm" version = "25.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "bounded-collections", "frame-benchmarking", @@ -11119,7 +12324,7 @@ dependencies = [ [[package]] name = "pallet-xcm-benchmarks" version = "25.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-benchmarking", "frame-support", @@ -11136,21 +12341,55 @@ dependencies = [ [[package]] name = "pallet-xcm-bridge-hub" version = "0.21.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "bp-messages", "bp-runtime", "bp-xcm-bridge-hub", "frame-support", "frame-system", - "pallet-bridge-messages", + "pallet-bridge-messages", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", + "tracing", +] + +[[package]] +name = "pallet-xcm-bridge-hub-router" +version = "0.23.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" +dependencies = [ + "bp-xcm-bridge-hub-router", + "frame-benchmarking", + "frame-support", + "frame-system", "parity-scale-codec", + "polkadot-runtime-parachains", "scale-info", "sp-core", "sp-runtime", "sp-std", "staging-xcm", "staging-xcm-builder", + "tracing", +] + +[[package]] +name = "pallet-xcm-precompiles" +version = "0.3.1" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" +dependencies = [ + "frame-support", + "pallet-revive", + "pallet-xcm", + "parity-scale-codec", + "staging-xcm", "staging-xcm-executor", "tracing", ] @@ -11205,7 +12444,7 @@ dependencies = [ [[package]] name = "parachains-common" version = "27.1.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "cumulus-primitives-core", "cumulus-primitives-utility", @@ -11234,6 +12473,12 @@ dependencies = [ "tracing", ] +[[package]] +name = "parity-bytes" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b56e3a2420138bdb970f84dfb9c774aea80fa0e7371549eedec0d80c209c67" + [[package]] name = "parity-db" version = "0.4.13" @@ -11483,7 +12728,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset 0.4.2", - "indexmap", + "indexmap 2.13.0", ] [[package]] @@ -11494,7 +12739,49 @@ checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" dependencies = [ "fixedbitset 0.5.7", "hashbrown 0.15.5", - "indexmap", + "indexmap 2.13.0", +] + +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher 1.0.2", ] [[package]] @@ -11571,7 +12858,7 @@ checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" [[package]] name = "polkadot-approval-distribution" version = "28.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "futures 0.3.32", "futures-timer", @@ -11589,7 +12876,7 @@ dependencies = [ [[package]] name = "polkadot-availability-bitfield-distribution" version = "28.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "futures 0.3.32", "futures-timer", @@ -11604,7 +12891,7 @@ dependencies = [ [[package]] name = "polkadot-availability-distribution" version = "28.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "fatality", "futures 0.3.32", @@ -11627,7 +12914,7 @@ dependencies = [ [[package]] name = "polkadot-availability-recovery" version = "28.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "async-trait", "fatality", @@ -11660,7 +12947,7 @@ dependencies = [ [[package]] name = "polkadot-cli" version = "31.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "clap", "frame-benchmarking-cli", @@ -11685,7 +12972,7 @@ dependencies = [ [[package]] name = "polkadot-collator-protocol" version = "28.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "bitvec", "fatality", @@ -11708,7 +12995,7 @@ dependencies = [ [[package]] name = "polkadot-core-primitives" version = "21.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "parity-scale-codec", "scale-info", @@ -11719,12 +13006,12 @@ dependencies = [ [[package]] name = "polkadot-dispute-distribution" version = "28.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "fatality", "futures 0.3.32", "futures-timer", - "indexmap", + "indexmap 2.13.0", "parity-scale-codec", "polkadot-node-network-protocol", "polkadot-node-primitives", @@ -11741,7 +13028,7 @@ dependencies = [ [[package]] name = "polkadot-erasure-coding" version = "23.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "parity-scale-codec", "polkadot-node-primitives", @@ -11755,7 +13042,7 @@ dependencies = [ [[package]] name = "polkadot-gossip-support" version = "28.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "futures 0.3.32", "futures-timer", @@ -11776,7 +13063,7 @@ dependencies = [ [[package]] name = "polkadot-network-bridge" version = "28.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "always-assert", "async-trait", @@ -11799,7 +13086,7 @@ dependencies = [ [[package]] name = "polkadot-node-collation-generation" version = "28.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "futures 0.3.32", "parity-scale-codec", @@ -11817,7 +13104,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-approval-voting" version = "28.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "async-trait", "bitvec", @@ -11849,7 +13136,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-approval-voting-parallel" version = "0.11.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "async-trait", "futures 0.3.32", @@ -11873,7 +13160,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-av-store" version = "28.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "bitvec", "futures 0.3.32", @@ -11892,7 +13179,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-backing" version = "28.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "bitvec", "fatality", @@ -11913,7 +13200,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-bitfield-signing" version = "28.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "futures 0.3.32", "polkadot-node-subsystem", @@ -11928,7 +13215,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-candidate-validation" version = "28.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "async-trait", "futures 0.3.32", @@ -11950,7 +13237,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-chain-api" version = "28.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "futures 0.3.32", "polkadot-node-metrics", @@ -11964,7 +13251,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-chain-selection" version = "28.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "futures 0.3.32", "futures-timer", @@ -11980,7 +13267,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-dispute-coordinator" version = "28.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "fatality", "futures 0.3.32", @@ -11998,7 +13285,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-parachains-inherent" version = "28.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "async-trait", "futures 0.3.32", @@ -12015,7 +13302,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-prospective-parachains" version = "27.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "fatality", "futures 0.3.32", @@ -12029,7 +13316,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-provisioner" version = "28.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "bitvec", "fatality", @@ -12048,7 +13335,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-pvf" version = "28.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "always-assert", "array-bytes 6.2.3", @@ -12076,7 +13363,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-pvf-checker" version = "28.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "futures 0.3.32", "polkadot-node-subsystem", @@ -12089,7 +13376,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-pvf-common" version = "24.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "cpu-time", "futures 0.3.32", @@ -12116,7 +13403,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-runtime-api" version = "28.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "futures 0.3.32", "polkadot-node-metrics", @@ -12131,7 +13418,7 @@ dependencies = [ [[package]] name = "polkadot-node-metrics" version = "28.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "bs58", "futures 0.3.32", @@ -12148,7 +13435,7 @@ dependencies = [ [[package]] name = "polkadot-node-network-protocol" version = "28.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "async-channel 1.9.0", "async-trait", @@ -12173,7 +13460,7 @@ dependencies = [ [[package]] name = "polkadot-node-primitives" version = "23.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "bitvec", "bounded-vec", @@ -12197,7 +13484,7 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem" version = "28.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "polkadot-node-subsystem-types", "polkadot-overseer", @@ -12206,7 +13493,7 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-types" version = "28.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "async-trait", "derive_more 0.99.20", @@ -12234,7 +13521,7 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-util" version = "28.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "fatality", "futures 0.3.32", @@ -12264,7 +13551,7 @@ dependencies = [ [[package]] name = "polkadot-omni-node-lib" version = "0.13.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "async-trait", "clap", @@ -12349,7 +13636,7 @@ dependencies = [ [[package]] name = "polkadot-overseer" version = "28.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "async-trait", "futures 0.3.32", @@ -12369,7 +13656,7 @@ dependencies = [ [[package]] name = "polkadot-parachain-primitives" version = "20.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "array-bytes 6.2.3", "bounded-collections", @@ -12386,7 +13673,7 @@ dependencies = [ [[package]] name = "polkadot-primitives" version = "22.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "bitvec", "bounded-collections", @@ -12415,7 +13702,7 @@ dependencies = [ [[package]] name = "polkadot-primitives-test-helpers" version = "0.2.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "parity-scale-codec", "polkadot-primitives", @@ -12430,7 +13717,7 @@ dependencies = [ [[package]] name = "polkadot-rpc" version = "29.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "jsonrpsee", "mmr-rpc", @@ -12463,7 +13750,7 @@ dependencies = [ [[package]] name = "polkadot-runtime-common" version = "24.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "bitvec", "frame-benchmarking", @@ -12513,7 +13800,7 @@ dependencies = [ [[package]] name = "polkadot-runtime-metrics" version = "25.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "bs58", "frame-benchmarking", @@ -12525,7 +13812,7 @@ dependencies = [ [[package]] name = "polkadot-runtime-parachains" version = "24.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "bitflags 1.3.2", "bitvec", @@ -12573,7 +13860,7 @@ dependencies = [ [[package]] name = "polkadot-sdk-frame" version = "0.14.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "docify", "frame-benchmarking", @@ -12608,7 +13895,7 @@ dependencies = [ [[package]] name = "polkadot-service" version = "31.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "async-trait", "frame-benchmarking", @@ -12717,7 +14004,7 @@ dependencies = [ [[package]] name = "polkadot-statement-distribution" version = "28.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "bitvec", "fatality", @@ -12737,7 +14024,7 @@ dependencies = [ [[package]] name = "polkadot-statement-table" version = "23.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "parity-scale-codec", "polkadot-primitives", @@ -12773,6 +14060,7 @@ version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed1b408db93d4f49f5c651a7844682b9d7a561827b4dc6202c10356076c055c9" dependencies = [ + "blake3", "log", "picosimd", "polkavm-assembler", @@ -13014,6 +14302,17 @@ dependencies = [ "elliptic-curve", ] +[[package]] +name = "primitive-types" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" +dependencies = [ + "fixed-hash", + "impl-codec 0.6.0", + "uint 0.9.5", +] + [[package]] name = "primitive-types" version = "0.13.1" @@ -13021,7 +14320,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d15600a7d856470b7d278b3fe0e311fe28c2526348549f8ef2ff7db3299c87f5" dependencies = [ "fixed-hash", - "impl-codec", + "impl-codec 0.7.1", "impl-num-traits", "impl-rlp", "impl-serde", @@ -13173,12 +14472,16 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37566cb3fdacef14c0737f9546df7cfeadbfbc9fef10991038bf5015d0c80532" dependencies = [ + "bit-set", + "bit-vec", "bitflags 2.11.0", "num-traits", "rand 0.9.2", "rand_chacha 0.9.0", "rand_xorshift", "regex-syntax", + "rusty-fork", + "tempfile", "unarray", ] @@ -13219,7 +14522,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf" dependencies = [ "heck 0.5.0", - "itertools 0.14.0", + "itertools 0.10.5", "log", "multimap", "once_cell", @@ -13239,7 +14542,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "343d3bd7056eda839b03204e68deff7d1b13aba7af2b2fd16890697274262ee7" dependencies = [ "heck 0.5.0", - "itertools 0.14.0", + "itertools 0.10.5", "log", "multimap", "petgraph 0.8.3", @@ -13258,7 +14561,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", - "itertools 0.12.1", + "itertools 0.10.5", "proc-macro2", "quote", "syn 2.0.117", @@ -13271,7 +14574,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools 0.14.0", + "itertools 0.10.5", "proc-macro2", "quote", "syn 2.0.117", @@ -13284,7 +14587,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" dependencies = [ "anyhow", - "itertools 0.14.0", + "itertools 0.10.5", "proc-macro2", "quote", "syn 2.0.117", @@ -13346,6 +14649,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + [[package]] name = "quick-protobuf" version = "0.8.1" @@ -13421,7 +14730,7 @@ dependencies = [ "once_cell", "socket2 0.6.2", "tracing", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] @@ -13473,6 +14782,7 @@ dependencies = [ "libc", "rand_chacha 0.3.1", "rand_core 0.6.4", + "serde", ] [[package]] @@ -13483,6 +14793,7 @@ checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.5", + "serde", ] [[package]] @@ -13540,6 +14851,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" dependencies = [ "getrandom 0.3.4", + "serde", ] [[package]] @@ -13579,6 +14891,15 @@ dependencies = [ "rand_core 0.9.5", ] +[[package]] +name = "rapidhash" +version = "4.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e48930979c155e2f33aa36ab3119b5ee81332beb6482199a8ecd6029b80b59" +dependencies = [ + "rustversion", +] + [[package]] name = "raw-cpuid" version = "11.6.0" @@ -13758,6 +15079,195 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e061d1b48cb8d38042de4ae0a7a6401009d6143dc80d2e2d6f31f0bdd6470c7" +[[package]] +name = "revm" +version = "27.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6bf82101a1ad8a2b637363a37aef27f88b4efc8a6e24c72bf5f64923dc5532" +dependencies = [ + "revm-bytecode", + "revm-context", + "revm-context-interface", + "revm-database", + "revm-database-interface", + "revm-handler", + "revm-inspector", + "revm-interpreter", + "revm-precompile", + "revm-primitives", + "revm-state", +] + +[[package]] +name = "revm-bytecode" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66c52031b73cae95d84cd1b07725808b5fd1500da3e5e24574a3b2dc13d9f16d" +dependencies = [ + "bitvec", + "phf", + "revm-primitives", + "serde", +] + +[[package]] +name = "revm-context" +version = "8.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cd508416a35a4d8a9feaf5ccd06ac6d6661cd31ee2dc0252f9f7316455d71f9" +dependencies = [ + "cfg-if", + "derive-where", + "revm-bytecode", + "revm-context-interface", + "revm-database-interface", + "revm-primitives", + "revm-state", + "serde", +] + +[[package]] +name = "revm-context-interface" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc90302642d21c8f93e0876e201f3c5f7913c4fcb66fb465b0fd7b707dfe1c79" +dependencies = [ + "alloy-eip2930", + "alloy-eip7702", + "auto_impl", + "either", + "revm-database-interface", + "revm-primitives", + "revm-state", + "serde", +] + +[[package]] +name = "revm-database" +version = "7.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39a276ed142b4718dcf64bc9624f474373ed82ef20611025045c3fb23edbef9c" +dependencies = [ + "alloy-eips", + "revm-bytecode", + "revm-database-interface", + "revm-primitives", + "revm-state", + "serde", +] + +[[package]] +name = "revm-database-interface" +version = "7.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c523c77e74eeedbac5d6f7c092e3851dbe9c7fec6f418b85992bd79229db361" +dependencies = [ + "auto_impl", + "either", + "revm-primitives", + "revm-state", + "serde", +] + +[[package]] +name = "revm-handler" +version = "8.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1529c8050e663be64010e80ec92bf480315d21b1f2dbf65540028653a621b27d" +dependencies = [ + "auto_impl", + "derive-where", + "revm-bytecode", + "revm-context", + "revm-context-interface", + "revm-database-interface", + "revm-interpreter", + "revm-precompile", + "revm-primitives", + "revm-state", + "serde", +] + +[[package]] +name = "revm-inspector" +version = "8.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78db140e332489094ef314eaeb0bd1849d6d01172c113ab0eb6ea8ab9372926" +dependencies = [ + "auto_impl", + "either", + "revm-context", + "revm-database-interface", + "revm-handler", + "revm-interpreter", + "revm-primitives", + "revm-state", + "serde", + "serde_json", +] + +[[package]] +name = "revm-interpreter" +version = "24.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff9d7d9d71e8a33740b277b602165b6e3d25fff091ba3d7b5a8d373bf55f28a7" +dependencies = [ + "revm-bytecode", + "revm-context-interface", + "revm-primitives", + "serde", +] + +[[package]] +name = "revm-precompile" +version = "25.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cee3f336b83621294b4cfe84d817e3eef6f3d0fce00951973364cc7f860424d" +dependencies = [ + "ark-bls12-381 0.5.0", + "ark-bn254", + "ark-ec 0.5.0", + "ark-ff 0.5.0", + "ark-serialize 0.5.0", + "arrayref", + "aurora-engine-modexp", + "c-kzg", + "cfg-if", + "k256", + "libsecp256k1", + "once_cell", + "p256", + "revm-primitives", + "ripemd", + "rug", + "secp256k1 0.31.1", + "sha2 0.10.9", +] + +[[package]] +name = "revm-primitives" +version = "20.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa29d9da06fe03b249b6419b33968ecdf92ad6428e2f012dc57bcd619b5d94e" +dependencies = [ + "alloy-primitives", + "num_enum 0.7.5", + "once_cell", + "serde", +] + +[[package]] +name = "revm-state" +version = "7.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f64fbacb86008394aaebd3454f9643b7d5a782bd251135e17c5b33da592d84d" +dependencies = [ + "bitflags 2.11.0", + "revm-bytecode", + "revm-primitives", + "serde", +] + [[package]] name = "rfc6979" version = "0.4.0" @@ -13806,6 +15316,16 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rustc-hex", +] + [[package]] name = "rlp" version = "0.6.1" @@ -13841,7 +15361,7 @@ dependencies = [ [[package]] name = "rococo-runtime" version = "29.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "binary-merkle-tree", "bitvec", @@ -13939,7 +15459,7 @@ dependencies = [ [[package]] name = "rococo-runtime-constants" version = "25.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-support", "polkadot-primitives", @@ -13997,6 +15517,52 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rug" +version = "1.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25f6c8f906c90b48e0c1745c9f814c3a31c5eba847043b05c3e9a934dec7c4b3" +dependencies = [ + "az", + "gmp-mpfr-sys", + "libc", + "libm", +] + +[[package]] +name = "ruint" +version = "1.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c141e807189ad38a07276942c6623032d3753c8859c146104ac2e4d68865945a" +dependencies = [ + "alloy-rlp", + "ark-ff 0.3.0", + "ark-ff 0.4.2", + "ark-ff 0.5.0", + "bytes", + "fastrlp 0.3.1", + "fastrlp 0.4.0", + "num-bigint", + "num-integer", + "num-traits", + "parity-scale-codec", + "primitive-types 0.12.2", + "proptest", + "rand 0.8.5", + "rand 0.9.2", + "rlp 0.5.2", + "ruint-macro", + "serde_core", + "valuable", + "zeroize", +] + +[[package]] +name = "ruint-macro" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" + [[package]] name = "rustc-demangle" version = "0.1.27" @@ -14021,6 +15587,15 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver 0.11.0", +] + [[package]] name = "rustc_version" version = "0.4.1" @@ -14076,7 +15651,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -14170,6 +15745,18 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +[[package]] +name = "rusty-fork" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + [[package]] name = "ruzstd" version = "0.8.2" @@ -14223,7 +15810,7 @@ dependencies = [ [[package]] name = "sc-allocator" version = "35.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "log", "sp-core", @@ -14234,7 +15821,7 @@ dependencies = [ [[package]] name = "sc-authority-discovery" version = "0.55.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "async-trait", "futures 0.3.32", @@ -14266,7 +15853,7 @@ dependencies = [ [[package]] name = "sc-basic-authorship" version = "0.53.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "futures 0.3.32", "log", @@ -14288,7 +15875,7 @@ dependencies = [ [[package]] name = "sc-block-builder" version = "0.48.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "parity-scale-codec", "sp-api", @@ -14303,7 +15890,7 @@ dependencies = [ [[package]] name = "sc-chain-spec" version = "48.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "array-bytes 6.2.3", "clap", @@ -14330,7 +15917,7 @@ dependencies = [ [[package]] name = "sc-chain-spec-derive" version = "12.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "proc-macro-crate 3.5.0", "proc-macro2", @@ -14341,7 +15928,7 @@ dependencies = [ [[package]] name = "sc-cli" version = "0.57.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "array-bytes 6.2.3", "bip39", @@ -14386,7 +15973,7 @@ dependencies = [ [[package]] name = "sc-client-api" version = "44.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "fnv", "futures 0.3.32", @@ -14412,7 +15999,7 @@ dependencies = [ [[package]] name = "sc-client-db" version = "0.51.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "hash-db", "kvdb", @@ -14440,7 +16027,7 @@ dependencies = [ [[package]] name = "sc-consensus" version = "0.54.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "async-trait", "futures 0.3.32", @@ -14463,7 +16050,7 @@ dependencies = [ [[package]] name = "sc-consensus-aura" version = "0.55.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "async-trait", "fork-tree", @@ -14494,7 +16081,7 @@ dependencies = [ [[package]] name = "sc-consensus-babe" version = "0.55.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "async-trait", "fork-tree", @@ -14531,7 +16118,7 @@ dependencies = [ [[package]] name = "sc-consensus-babe-rpc" version = "0.55.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "futures 0.3.32", "jsonrpsee", @@ -14553,7 +16140,7 @@ dependencies = [ [[package]] name = "sc-consensus-beefy" version = "34.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "array-bytes 6.2.3", "async-channel 1.9.0", @@ -14587,7 +16174,7 @@ dependencies = [ [[package]] name = "sc-consensus-beefy-rpc" version = "34.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "futures 0.3.32", "jsonrpsee", @@ -14607,7 +16194,7 @@ dependencies = [ [[package]] name = "sc-consensus-epochs" version = "0.54.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "fork-tree", "parity-scale-codec", @@ -14620,7 +16207,7 @@ dependencies = [ [[package]] name = "sc-consensus-grandpa" version = "0.40.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "ahash 0.8.12", "array-bytes 6.2.3", @@ -14664,7 +16251,7 @@ dependencies = [ [[package]] name = "sc-consensus-grandpa-rpc" version = "0.40.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "finality-grandpa", "futures 0.3.32", @@ -14684,7 +16271,7 @@ dependencies = [ [[package]] name = "sc-consensus-manual-seal" version = "0.56.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "assert_matches", "async-trait", @@ -14719,7 +16306,7 @@ dependencies = [ [[package]] name = "sc-consensus-slots" version = "0.54.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "async-trait", "futures 0.3.32", @@ -14742,7 +16329,7 @@ dependencies = [ [[package]] name = "sc-executor" version = "0.47.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "log", "parity-scale-codec", @@ -14766,7 +16353,7 @@ dependencies = [ [[package]] name = "sc-executor-common" version = "0.43.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "parity-scale-codec", "polkavm", @@ -14780,7 +16367,7 @@ dependencies = [ [[package]] name = "sc-executor-polkavm" version = "0.40.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "log", "polkavm", @@ -14791,7 +16378,7 @@ dependencies = [ [[package]] name = "sc-executor-wasmtime" version = "0.43.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "anyhow", "log", @@ -14808,7 +16395,7 @@ dependencies = [ [[package]] name = "sc-informant" version = "0.54.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "console", "futures 0.3.32", @@ -14824,7 +16411,7 @@ dependencies = [ [[package]] name = "sc-keystore" version = "39.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "array-bytes 6.2.3", "parking_lot 0.12.5", @@ -14838,7 +16425,7 @@ dependencies = [ [[package]] name = "sc-mixnet" version = "0.25.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "array-bytes 6.2.3", "arrayvec 0.7.6", @@ -14866,7 +16453,7 @@ dependencies = [ [[package]] name = "sc-network" version = "0.55.2" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "array-bytes 6.2.3", "async-channel 1.9.0", @@ -14915,7 +16502,7 @@ dependencies = [ [[package]] name = "sc-network-common" version = "0.52.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "bitflags 1.3.2", "parity-scale-codec", @@ -14925,7 +16512,7 @@ dependencies = [ [[package]] name = "sc-network-gossip" version = "0.55.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "ahash 0.8.12", "futures 0.3.32", @@ -14944,7 +16531,7 @@ dependencies = [ [[package]] name = "sc-network-light" version = "0.54.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "array-bytes 6.2.3", "async-channel 1.9.0", @@ -14965,7 +16552,7 @@ dependencies = [ [[package]] name = "sc-network-statement" version = "0.37.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "array-bytes 6.2.3", "async-channel 1.9.0", @@ -14986,7 +16573,7 @@ dependencies = [ [[package]] name = "sc-network-sync" version = "0.54.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "array-bytes 6.2.3", "async-channel 1.9.0", @@ -15021,7 +16608,7 @@ dependencies = [ [[package]] name = "sc-network-transactions" version = "0.54.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "array-bytes 6.2.3", "futures 0.3.32", @@ -15040,7 +16627,7 @@ dependencies = [ [[package]] name = "sc-network-types" version = "0.20.2" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "bs58", "bytes", @@ -15061,7 +16648,7 @@ dependencies = [ [[package]] name = "sc-offchain" version = "50.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "bytes", "fnv", @@ -15095,7 +16682,7 @@ dependencies = [ [[package]] name = "sc-proposer-metrics" version = "0.20.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "log", "substrate-prometheus-endpoint", @@ -15104,7 +16691,7 @@ dependencies = [ [[package]] name = "sc-rpc" version = "50.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "futures 0.3.32", "jsonrpsee", @@ -15136,7 +16723,7 @@ dependencies = [ [[package]] name = "sc-rpc-api" version = "0.54.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "jsonrpsee", "parity-scale-codec", @@ -15156,7 +16743,7 @@ dependencies = [ [[package]] name = "sc-rpc-server" version = "27.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "dyn-clone", "forwarded-header-value", @@ -15180,7 +16767,7 @@ dependencies = [ [[package]] name = "sc-rpc-spec-v2" version = "0.55.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "array-bytes 6.2.3", "futures 0.3.32", @@ -15213,7 +16800,7 @@ dependencies = [ [[package]] name = "sc-runtime-utilities" version = "0.7.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "parity-scale-codec", "sc-executor", @@ -15228,7 +16815,7 @@ dependencies = [ [[package]] name = "sc-service" version = "0.56.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "async-trait", "directories", @@ -15292,7 +16879,7 @@ dependencies = [ [[package]] name = "sc-state-db" version = "0.41.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "log", "parity-scale-codec", @@ -15303,7 +16890,7 @@ dependencies = [ [[package]] name = "sc-statement-store" version = "26.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "log", "parity-db 0.4.13", @@ -15323,7 +16910,7 @@ dependencies = [ [[package]] name = "sc-storage-monitor" version = "0.27.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "clap", "fs4", @@ -15336,7 +16923,7 @@ dependencies = [ [[package]] name = "sc-sync-state-rpc" version = "0.55.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "jsonrpsee", "parity-scale-codec", @@ -15355,7 +16942,7 @@ dependencies = [ [[package]] name = "sc-sysinfo" version = "46.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "derive_more 0.99.20", "futures 0.3.32", @@ -15375,7 +16962,7 @@ dependencies = [ [[package]] name = "sc-telemetry" version = "30.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "chrono", "futures 0.3.32", @@ -15394,7 +16981,7 @@ dependencies = [ [[package]] name = "sc-tracing" version = "44.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "chrono", "console", @@ -15416,13 +17003,13 @@ dependencies = [ "thiserror 1.0.69", "tracing", "tracing-log", - "tracing-subscriber", + "tracing-subscriber 0.3.22", ] [[package]] name = "sc-tracing-proc-macro" version = "11.1.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "proc-macro-crate 3.5.0", "proc-macro2", @@ -15433,12 +17020,12 @@ dependencies = [ [[package]] name = "sc-transaction-pool" version = "44.1.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "async-trait", "futures 0.3.32", "futures-timer", - "indexmap", + "indexmap 2.13.0", "itertools 0.11.0", "linked-hash-map", "parity-scale-codec", @@ -15465,11 +17052,11 @@ dependencies = [ [[package]] name = "sc-transaction-pool-api" version = "43.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "async-trait", "futures 0.3.32", - "indexmap", + "indexmap 2.13.0", "log", "parity-scale-codec", "serde", @@ -15483,7 +17070,7 @@ dependencies = [ [[package]] name = "sc-utils" version = "20.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "async-channel 1.9.0", "futures 0.3.32", @@ -15513,7 +17100,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d6ed61699ad4d54101ab5a817169259b5b0efc08152f8632e61482d8a27ca3d" dependencies = [ "parity-scale-codec", - "primitive-types", + "primitive-types 0.13.1", "scale-bits", "scale-decode-derive", "scale-type-resolver", @@ -15540,7 +17127,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2a976d73564a59e482b74fd5d95f7518b79ca8c8ca5865398a4d629dd15ee50" dependencies = [ "parity-scale-codec", - "primitive-types", + "primitive-types 0.13.1", "scale-bits", "scale-encode-derive", "scale-type-resolver", @@ -15638,6 +17225,30 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + [[package]] name = "schnellru" version = "0.2.4" @@ -15743,6 +17354,18 @@ dependencies = [ "bitcoin_hashes", "rand 0.8.5", "secp256k1-sys 0.10.1", + "serde", +] + +[[package]] +name = "secp256k1" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c3c81b43dc2d8877c216a3fccf76677ee1ebccd429566d3e67447290d0c42b2" +dependencies = [ + "bitcoin_hashes", + "rand 0.9.2", + "secp256k1-sys 0.11.0", ] [[package]] @@ -15772,6 +17395,15 @@ dependencies = [ "cc", ] +[[package]] +name = "secp256k1-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb913707158fadaf0d8702c2db0e857de66eb003ccfdda5924b5f5ac98efb38" +dependencies = [ + "cc", +] + [[package]] name = "secrecy" version = "0.8.0" @@ -15819,7 +17451,16 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a3186ec9e65071a2095434b1f5bb24838d4e8e130f584c790f6033c79943537" dependencies = [ - "semver-parser", + "semver-parser 0.7.0", +] + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser 0.10.3", ] [[package]] @@ -15838,6 +17479,15 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +[[package]] +name = "semver-parser" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9900206b54a3527fdc7b8a938bffd94a568bac4f4aa8113b209df75a09c0dec2" +dependencies = [ + "pest", +] + [[package]] name = "serde" version = "1.0.228" @@ -15848,6 +17498,15 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-big-array" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd31f59f6fe2b0c055371bb2f16d7f0aa7d8881676c04a55b1596d1a17cd10a4" +dependencies = [ + "serde", +] + [[package]] name = "serde_bytes" version = "0.11.19" @@ -15884,6 +17543,7 @@ version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ + "indexmap 2.13.0", "itoa", "memchr", "serde", @@ -15921,6 +17581,10 @@ dependencies = [ "base64", "chrono", "hex", + "indexmap 1.9.3", + "indexmap 2.13.0", + "schemars 0.9.0", + "schemars 1.2.1", "serde_core", "serde_json", "serde_with_macros", @@ -16004,6 +17668,12 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "sha2-const-stable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f179d4e11094a893b82fff208f74d448a7512f99f5a0acbd5c679b705f83ed9" + [[package]] name = "sha3" version = "0.10.8" @@ -16014,6 +17684,16 @@ dependencies = [ "keccak", ] +[[package]] +name = "sha3-asm" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59cbb88c189d6352cc8ae96a39d19c7ecad8f7330b29461187f2587fdc2988d5" +dependencies = [ + "cc", + "cfg-if", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -16124,7 +17804,7 @@ checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" [[package]] name = "slot-range-helper" version = "21.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "enumn", "parity-scale-codec", @@ -16275,15 +17955,47 @@ dependencies = [ "curve25519-dalek", "rand_core 0.6.4", "ring 0.17.14", - "rustc_version", + "rustc_version 0.4.1", "sha2 0.10.9", "subtle 2.6.1", ] +[[package]] +name = "snowbridge-amcl" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460a9ed63cdf03c1b9847e8a12a5f5ba19c4efd5869e4a737e05be25d7c427e5" +dependencies = [ + "parity-scale-codec", + "scale-info", +] + +[[package]] +name = "snowbridge-beacon-primitives" +version = "0.18.1" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" +dependencies = [ + "byte-slice-cast", + "frame-support", + "hex", + "parity-scale-codec", + "rlp 0.6.1", + "scale-info", + "serde", + "snowbridge-ethereum", + "snowbridge-milagro-bls", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "ssz_rs", + "ssz_rs_derive", +] + [[package]] name = "snowbridge-core" version = "0.18.1" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "bp-relayers", "frame-support", @@ -16304,6 +18016,122 @@ dependencies = [ "tracing", ] +[[package]] +name = "snowbridge-ethereum" +version = "0.16.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" +dependencies = [ + "alloy-consensus", + "alloy-core", + "alloy-primitives", + "alloy-rlp", + "ethabi-decode", + "ethbloom", + "ethereum-types", + "hex-literal", + "parity-bytes", + "parity-scale-codec", + "rlp 0.6.1", + "scale-info", + "serde", + "serde-big-array", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "snowbridge-milagro-bls" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "026aa8638f690a53e3f7676024b9e913b1cab0111d1b7b92669d40a188f9d7e6" +dependencies = [ + "hex", + "lazy_static", + "parity-scale-codec", + "rand 0.8.5", + "scale-info", + "snowbridge-amcl", + "zeroize", +] + +[[package]] +name = "snowbridge-outbound-queue-primitives" +version = "0.7.1" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" +dependencies = [ + "alloy-core", + "ethabi-decode", + "frame-support", + "frame-system", + "hex-literal", + "parity-scale-codec", + "polkadot-parachain-primitives", + "scale-info", + "snowbridge-core", + "snowbridge-verification-primitives", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", + "tracing", +] + +[[package]] +name = "snowbridge-pallet-system-frontend" +version = "0.7.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-asset-conversion", + "parity-scale-codec", + "scale-info", + "snowbridge-core", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "staging-xcm", + "staging-xcm-executor", + "tracing", +] + +[[package]] +name = "snowbridge-runtime-common" +version = "0.19.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" +dependencies = [ + "frame-support", + "frame-system", + "pallet-xcm", + "parity-scale-codec", + "sp-arithmetic", + "sp-std", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", + "tracing", +] + +[[package]] +name = "snowbridge-verification-primitives" +version = "0.7.1" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" +dependencies = [ + "frame-support", + "parity-scale-codec", + "scale-info", + "snowbridge-beacon-primitives", + "sp-core", + "sp-std", +] + [[package]] name = "socket2" version = "0.4.10" @@ -16353,7 +18181,7 @@ dependencies = [ [[package]] name = "sp-api" version = "40.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "docify", "hash-db", @@ -16375,7 +18203,7 @@ dependencies = [ [[package]] name = "sp-api-proc-macro" version = "26.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "Inflector", "blake2 0.10.6", @@ -16389,7 +18217,7 @@ dependencies = [ [[package]] name = "sp-application-crypto" version = "44.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "parity-scale-codec", "scale-info", @@ -16401,7 +18229,7 @@ dependencies = [ [[package]] name = "sp-arithmetic" version = "28.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "docify", "integer-sqrt", @@ -16415,7 +18243,7 @@ dependencies = [ [[package]] name = "sp-authority-discovery" version = "40.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "parity-scale-codec", "scale-info", @@ -16427,7 +18255,7 @@ dependencies = [ [[package]] name = "sp-block-builder" version = "40.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "sp-api", "sp-inherents", @@ -16437,7 +18265,7 @@ dependencies = [ [[package]] name = "sp-blockchain" version = "43.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "futures 0.3.32", "parity-scale-codec", @@ -16456,7 +18284,7 @@ dependencies = [ [[package]] name = "sp-consensus" version = "0.46.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "async-trait", "futures 0.3.32", @@ -16470,7 +18298,7 @@ dependencies = [ [[package]] name = "sp-consensus-aura" version = "0.46.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "async-trait", "parity-scale-codec", @@ -16486,7 +18314,7 @@ dependencies = [ [[package]] name = "sp-consensus-babe" version = "0.46.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "async-trait", "parity-scale-codec", @@ -16504,7 +18332,7 @@ dependencies = [ [[package]] name = "sp-consensus-beefy" version = "28.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "parity-scale-codec", "scale-info", @@ -16524,7 +18352,7 @@ dependencies = [ [[package]] name = "sp-consensus-grandpa" version = "27.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "finality-grandpa", "log", @@ -16541,7 +18369,7 @@ dependencies = [ [[package]] name = "sp-consensus-slots" version = "0.46.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "parity-scale-codec", "scale-info", @@ -16552,7 +18380,7 @@ dependencies = [ [[package]] name = "sp-core" version = "39.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "ark-vrf", "array-bytes 6.2.3", @@ -16575,7 +18403,7 @@ dependencies = [ "parity-scale-codec", "parking_lot 0.12.5", "paste", - "primitive-types", + "primitive-types 0.13.1", "rand 0.8.5", "scale-info", "schnorrkel", @@ -16613,7 +18441,7 @@ dependencies = [ [[package]] name = "sp-crypto-hashing" version = "0.1.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "blake2b_simd", "byteorder", @@ -16626,7 +18454,7 @@ dependencies = [ [[package]] name = "sp-crypto-hashing-proc-macro" version = "0.1.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "quote", "sp-crypto-hashing 0.1.0 (git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512)", @@ -16636,7 +18464,7 @@ dependencies = [ [[package]] name = "sp-database" version = "10.0.1" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "kvdb", "kvdb-rocksdb", @@ -16646,7 +18474,7 @@ dependencies = [ [[package]] name = "sp-debug-derive" version = "14.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "proc-macro2", "quote", @@ -16656,7 +18484,7 @@ dependencies = [ [[package]] name = "sp-externalities" version = "0.31.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "environmental", "parity-scale-codec", @@ -16666,7 +18494,7 @@ dependencies = [ [[package]] name = "sp-genesis-builder" version = "0.21.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "parity-scale-codec", "scale-info", @@ -16678,7 +18506,7 @@ dependencies = [ [[package]] name = "sp-inherents" version = "40.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "async-trait", "impl-trait-for-tuples", @@ -16691,7 +18519,7 @@ dependencies = [ [[package]] name = "sp-io" version = "44.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "bytes", "docify", @@ -16717,7 +18545,7 @@ dependencies = [ [[package]] name = "sp-keyring" version = "45.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "sp-core", "sp-runtime", @@ -16727,7 +18555,7 @@ dependencies = [ [[package]] name = "sp-keystore" version = "0.45.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "parity-scale-codec", "parking_lot 0.12.5", @@ -16738,7 +18566,7 @@ dependencies = [ [[package]] name = "sp-maybe-compressed-blob" version = "11.1.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "thiserror 1.0.69", "zstd 0.12.4", @@ -16747,7 +18575,7 @@ dependencies = [ [[package]] name = "sp-metadata-ir" version = "0.12.1" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-metadata", "parity-scale-codec", @@ -16757,7 +18585,7 @@ dependencies = [ [[package]] name = "sp-mixnet" version = "0.18.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "parity-scale-codec", "scale-info", @@ -16768,7 +18596,7 @@ dependencies = [ [[package]] name = "sp-mmr-primitives" version = "40.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "log", "parity-scale-codec", @@ -16785,7 +18613,7 @@ dependencies = [ [[package]] name = "sp-npos-elections" version = "40.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "parity-scale-codec", "scale-info", @@ -16798,7 +18626,7 @@ dependencies = [ [[package]] name = "sp-offchain" version = "40.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "sp-api", "sp-core", @@ -16808,7 +18636,7 @@ dependencies = [ [[package]] name = "sp-panic-handler" version = "13.0.2" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "backtrace", "regex", @@ -16817,7 +18645,7 @@ dependencies = [ [[package]] name = "sp-rpc" version = "37.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "rustc-hash 1.1.0", "serde", @@ -16827,7 +18655,7 @@ dependencies = [ [[package]] name = "sp-runtime" version = "45.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "binary-merkle-tree", "bytes", @@ -16858,7 +18686,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface" version = "33.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "bytes", "impl-trait-for-tuples", @@ -16876,7 +18704,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface-proc-macro" version = "20.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "Inflector", "expander", @@ -16889,7 +18717,7 @@ dependencies = [ [[package]] name = "sp-session" version = "42.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "parity-scale-codec", "scale-info", @@ -16903,7 +18731,7 @@ dependencies = [ [[package]] name = "sp-staking" version = "42.2.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "impl-trait-for-tuples", "parity-scale-codec", @@ -16916,7 +18744,7 @@ dependencies = [ [[package]] name = "sp-state-machine" version = "0.49.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "hash-db", "log", @@ -16936,7 +18764,7 @@ dependencies = [ [[package]] name = "sp-statement-store" version = "24.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "aes-gcm", "curve25519-dalek", @@ -16960,12 +18788,12 @@ dependencies = [ [[package]] name = "sp-std" version = "14.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" [[package]] name = "sp-storage" version = "22.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "impl-serde", "parity-scale-codec", @@ -16977,7 +18805,7 @@ dependencies = [ [[package]] name = "sp-timestamp" version = "40.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "async-trait", "parity-scale-codec", @@ -16989,19 +18817,19 @@ dependencies = [ [[package]] name = "sp-tracing" version = "19.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "parity-scale-codec", "regex", "tracing", "tracing-core", - "tracing-subscriber", + "tracing-subscriber 0.3.22", ] [[package]] name = "sp-transaction-pool" version = "40.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "sp-api", "sp-runtime", @@ -17010,7 +18838,7 @@ dependencies = [ [[package]] name = "sp-transaction-storage-proof" version = "40.1.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "async-trait", "parity-scale-codec", @@ -17025,7 +18853,7 @@ dependencies = [ [[package]] name = "sp-trie" version = "42.0.1" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "ahash 0.8.12", "foldhash 0.1.5", @@ -17050,7 +18878,7 @@ dependencies = [ [[package]] name = "sp-version" version = "43.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "impl-serde", "parity-scale-codec", @@ -17067,7 +18895,7 @@ dependencies = [ [[package]] name = "sp-version-proc-macro" version = "15.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "parity-scale-codec", "proc-macro-warning", @@ -17079,7 +18907,7 @@ dependencies = [ [[package]] name = "sp-wasm-interface" version = "24.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "anyhow", "impl-trait-for-tuples", @@ -17091,7 +18919,7 @@ dependencies = [ [[package]] name = "sp-weights" version = "33.2.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "bounded-collections", "parity-scale-codec", @@ -17165,7 +18993,7 @@ dependencies = [ "futures-util", "hashbrown 0.15.5", "hashlink 0.10.0", - "indexmap", + "indexmap 2.13.0", "log", "memchr", "native-tls", @@ -17256,6 +19084,29 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "ssz_rs" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "057291e5631f280978fa9c8009390663ca4613359fc1318e36a8c24c392f6d1f" +dependencies = [ + "bitvec", + "num-bigint", + "sha2 0.9.9", + "ssz_rs_derive", +] + +[[package]] +name = "ssz_rs_derive" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f07d54c4d01a1713eb363b55ba51595da15f6f1211435b71466460da022aa140" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -17265,7 +19116,7 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "staging-chain-spec-builder" version = "16.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "clap", "docify", @@ -17278,7 +19129,7 @@ dependencies = [ [[package]] name = "staging-parachain-info" version = "0.25.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "cumulus-primitives-core", "frame-support", @@ -17291,7 +19142,7 @@ dependencies = [ [[package]] name = "staging-xcm" version = "21.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "array-bytes 6.2.3", "bounded-collections", @@ -17312,7 +19163,7 @@ dependencies = [ [[package]] name = "staging-xcm-builder" version = "25.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "environmental", "frame-support", @@ -17336,7 +19187,7 @@ dependencies = [ [[package]] name = "staging-xcm-executor" version = "24.0.1" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "environmental", "frame-benchmarking", @@ -17452,7 +19303,7 @@ dependencies = [ [[package]] name = "substrate-bip39" version = "0.6.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "hmac 0.12.1", "pbkdf2 0.12.2", @@ -17477,7 +19328,7 @@ dependencies = [ [[package]] name = "substrate-build-script-utils" version = "11.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" [[package]] name = "substrate-fixed" @@ -17493,7 +19344,7 @@ dependencies = [ [[package]] name = "substrate-frame-rpc-system" version = "49.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "docify", "frame-system-rpc-runtime-api", @@ -17513,7 +19364,7 @@ dependencies = [ [[package]] name = "substrate-prometheus-endpoint" version = "0.17.7" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "http-body-util", "hyper 1.8.1", @@ -17527,7 +19378,7 @@ dependencies = [ [[package]] name = "substrate-rpc-client" version = "0.54.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "async-trait", "jsonrpsee", @@ -17540,7 +19391,7 @@ dependencies = [ [[package]] name = "substrate-state-trie-migration-rpc" version = "48.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "jsonrpsee", "parity-scale-codec", @@ -17557,7 +19408,7 @@ dependencies = [ [[package]] name = "substrate-test-client" version = "2.0.1" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "array-bytes 6.2.3", "async-trait", @@ -17582,7 +19433,7 @@ dependencies = [ [[package]] name = "substrate-test-runtime" version = "2.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "array-bytes 6.2.3", "frame-executive", @@ -17628,7 +19479,7 @@ dependencies = [ [[package]] name = "substrate-test-runtime-client" version = "2.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "futures 0.3.32", "sc-block-builder", @@ -17656,7 +19507,7 @@ dependencies = [ [[package]] name = "substrate-wasm-builder" version = "31.1.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "array-bytes 6.2.3", "build-helper", @@ -17708,7 +19559,7 @@ dependencies = [ "futures 0.3.32", "hex", "parity-scale-codec", - "primitive-types", + "primitive-types 0.13.1", "scale-bits", "scale-decode", "scale-encode", @@ -17763,7 +19614,7 @@ dependencies = [ "impl-serde", "keccak-hash", "parity-scale-codec", - "primitive-types", + "primitive-types 0.13.1", "scale-bits", "scale-decode", "scale-encode", @@ -17839,7 +19690,7 @@ dependencies = [ "impl-serde", "jsonrpsee", "parity-scale-codec", - "primitive-types", + "primitive-types 0.13.1", "serde", "serde_json", "subxt-core", @@ -17924,6 +19775,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn-solidity" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53f425ae0b12e2f5ae65542e00898d500d4d318b4baf09f40fd0d410454e9947" +dependencies = [ + "paste", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "synstructure" version = "0.12.6" @@ -18011,7 +19874,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix 1.1.4", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -18039,6 +19902,21 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" +[[package]] +name = "testnet-parachains-constants" +version = "18.0.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" +dependencies = [ + "cumulus-primitives-core", + "frame-support", + "polkadot-core-primitives", + "rococo-runtime-constants", + "smallvec", + "sp-runtime", + "staging-xcm", + "westend-runtime-constants", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -18344,7 +20222,7 @@ version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap", + "indexmap 2.13.0", "serde", "serde_spanned", "toml_datetime 0.6.11", @@ -18358,7 +20236,7 @@ version = "0.25.4+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7193cbd0ce53dc966037f54351dbbcf0d5a642c7f0038c382ef9e677ce8c13f2" dependencies = [ - "indexmap", + "indexmap 2.13.0", "toml_datetime 1.0.0+spec-1.1.0", "toml_parser", "winnow", @@ -18468,7 +20346,7 @@ dependencies = [ [[package]] name = "tracing-gum" version = "23.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "coarsetime", "polkadot-primitives", @@ -18479,7 +20357,7 @@ dependencies = [ [[package]] name = "tracing-gum-proc-macro" version = "5.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "expander", "proc-macro-crate 3.5.0", @@ -18501,6 +20379,15 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-subscriber" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" +dependencies = [ + "tracing-core", +] + [[package]] name = "tracing-subscriber" version = "0.3.22" @@ -18850,6 +20737,15 @@ dependencies = [ "w3f-plonk-common", ] +[[package]] +name = "wait-timeout" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" +dependencies = [ + "libc", +] + [[package]] name = "waker-fn" version = "1.2.0" @@ -19009,7 +20905,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" dependencies = [ "anyhow", - "indexmap", + "indexmap 2.13.0", "wasm-encoder 0.244.0", "wasmparser 0.244.0", ] @@ -19127,7 +21023,7 @@ checksum = "161296c618fa2d63f6ed5fffd1112937e803cb9ec71b32b01a76321555660917" dependencies = [ "bitflags 2.11.0", "hashbrown 0.15.5", - "indexmap", + "indexmap 2.13.0", "semver 1.0.27", "serde", ] @@ -19140,7 +21036,7 @@ checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ "bitflags 2.11.0", "hashbrown 0.15.5", - "indexmap", + "indexmap 2.13.0", "semver 1.0.27", ] @@ -19170,7 +21066,7 @@ dependencies = [ "fxprof-processed-profile", "gimli 0.31.1", "hashbrown 0.15.5", - "indexmap", + "indexmap 2.13.0", "ittapi", "libc", "log", @@ -19214,7 +21110,7 @@ dependencies = [ "cranelift-bitset", "cranelift-entity", "gimli 0.31.1", - "indexmap", + "indexmap 2.13.0", "log", "object 0.36.7", "postcard", @@ -19427,7 +21323,7 @@ checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "westend-runtime" version = "30.2.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "binary-merkle-tree", "bitvec", @@ -19535,7 +21431,7 @@ dependencies = [ [[package]] name = "westend-runtime-constants" version = "25.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-support", "polkadot-primitives", @@ -19586,7 +21482,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -20088,7 +21984,7 @@ checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" dependencies = [ "anyhow", "heck 0.5.0", - "indexmap", + "indexmap 2.13.0", "prettyplease", "syn 2.0.117", "wasm-metadata", @@ -20119,7 +22015,7 @@ checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", "bitflags 2.11.0", - "indexmap", + "indexmap 2.13.0", "log", "serde", "serde_derive", @@ -20138,7 +22034,7 @@ checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" dependencies = [ "anyhow", "id-arena", - "indexmap", + "indexmap 2.13.0", "log", "semver 1.0.27", "serde", @@ -20209,6 +22105,42 @@ dependencies = [ "time", ] +[[package]] +name = "xcm-emulator" +version = "0.26.0" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" +dependencies = [ + "array-bytes 6.2.3", + "cumulus-pallet-parachain-system", + "cumulus-primitives-core", + "cumulus-primitives-parachain-inherent", + "cumulus-test-relay-sproof-builder", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "pallet-aura", + "pallet-balances", + "pallet-message-queue", + "pallet-timestamp", + "parachains-common", + "parity-scale-codec", + "paste", + "polkadot-parachain-primitives", + "polkadot-primitives", + "polkadot-runtime-parachains", + "sp-arithmetic", + "sp-consensus-aura", + "sp-core", + "sp-crypto-hashing 0.1.0 (git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512)", + "sp-io", + "sp-runtime", + "sp-tracing", + "staging-xcm", + "staging-xcm-executor", + "tracing", + "xcm-simulator", +] + [[package]] name = "xcm-primitives" version = "0.1.0" @@ -20218,7 +22150,7 @@ dependencies = [ "impl-trait-for-tuples", "log", "parity-scale-codec", - "primitive-types", + "primitive-types 0.13.1", "sp-core", "sp-runtime", "sp-std", @@ -20249,7 +22181,7 @@ dependencies = [ [[package]] name = "xcm-procedural" version = "11.0.2" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "Inflector", "proc-macro2", @@ -20260,7 +22192,7 @@ dependencies = [ [[package]] name = "xcm-runtime-apis" version = "0.12.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-support", "parity-scale-codec", @@ -20274,7 +22206,7 @@ dependencies = [ [[package]] name = "xcm-simulator" version = "25.0.0" -source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#e4d0430705cf7e0c31ef3ad6a8de8e671212a679" +source = "git+https://github.com/moonbeam-foundation/polkadot-sdk?branch=moonbeam-polkadot-stable2512#04250209b768e246445fde923db68de27a192797" dependencies = [ "frame-support", "frame-system", diff --git a/Cargo.toml b/Cargo.toml index 8f06b11a8ca..2d29fe5f310 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -145,6 +145,7 @@ pallet-assets = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", b pallet-balances = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-stable2512", default-features = false } pallet-collective = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-stable2512", default-features = false } pallet-conviction-voting = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-stable2512", default-features = false } +pallet-delegated-staking = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-stable2512", default-features = false } pallet-identity = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-stable2512", default-features = false } pallet-message-queue = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-stable2512", default-features = false } pallet-multisig = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-stable2512", default-features = false } @@ -318,7 +319,9 @@ xcm-runtime-apis = { git = "https://github.com/moonbeam-foundation/polkadot-sdk" polkadot-cli = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-stable2512" } polkadot-primitives = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-stable2512" } polkadot-service = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-stable2512" } +asset-hub-westend-runtime = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-stable2512" } westend-runtime = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-stable2512" } +xcm-emulator = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-stable2512" } xcm-simulator = { git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-stable2512" } # Bridge dependencies diff --git a/docs/cherry-picks/polkadot-sdk-stable2512.md b/docs/cherry-picks/polkadot-sdk-stable2512.md index 73d6557801f..bbca8f5939c 100644 --- a/docs/cherry-picks/polkadot-sdk-stable2512.md +++ b/docs/cherry-picks/polkadot-sdk-stable2512.md @@ -28,6 +28,7 @@ | Yes | Backport PR#10305 | | Dropped | PR Upstream Merged | [paritytech/polkadot-sdk#10305](https://github.com/paritytech/polkadot-sdk/pull/10305) | | | Yes | Backport PR#9703 | | Dropped | PR Upstream Merged | [paritytech/polkadot-sdk#9703](https://github.com/paritytech/polkadot-sdk/pull/9703) | | | Yes | Backport PR#9990 | | Dropped | PR Upstream Merged | [paritytech/polkadot-sdk#9990](https://github.com/paritytech/polkadot-sdk/pull/9990) | | +| Yes | xcm-emulator: overridable block producer for non-Aura parachains | [moonbeam-foundation/polkadot-sdk@04250209](https://github.com/moonbeam-foundation/polkadot-sdk/commit/04250209b76) | Included | PR Upstream Merged | [paritytech/polkadot-sdk#11791](https://github.com/paritytech/polkadot-sdk/pull/11791) | Adds a `BlockProducer` trait (default `AuraBlockProducer`) and an optional `BlockProducer:` field to `decl_test_parachains!` so Nimbus-based runtimes can plug in a custom slot duration and pre-runtime digest. Required to wire Moonbeam runtimes into xcm-emulator-based integration tests without implementing `pallet_aura::Config`. | ## `ethereum` diff --git a/runtime/moonbase/Cargo.toml b/runtime/moonbase/Cargo.toml index fdfcf348460..4f52be6bc8b 100644 --- a/runtime/moonbase/Cargo.toml +++ b/runtime/moonbase/Cargo.toml @@ -188,7 +188,9 @@ cumulus-test-relay-sproof-builder = { workspace = true } frame-metadata = { workspace = true } polkadot-runtime-parachains = { workspace = true } sp-timestamp = { workspace = true } -xcm-simulator = { workspace = true } +xcm-emulator = { workspace = true } +westend-runtime = { workspace = true } +asset-hub-westend-runtime = { workspace = true } pallet-assets = { workspace = true } precompile-utils = { workspace = true, features = ["std", "testing"] } diff --git a/runtime/moonbase/tests/xcm_config/barriers.rs b/runtime/moonbase/tests/xcm_config/barriers.rs new file mode 100644 index 00000000000..f3bc28afc7d --- /dev/null +++ b/runtime/moonbase/tests/xcm_config/barriers.rs @@ -0,0 +1,404 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Tests for XcmBarrier configuration. +//! +//! The barrier determines which XCM messages are allowed to execute. +//! Moonbase's barrier allows: +//! - TakeWeightCredit: Messages that consume credited weight +//! - AllowKnownQueryResponses: Expected query responses +//! - AllowTopLevelPaidExecutionFrom: Paid execution from any origin +//! - AllowSubscriptionsFrom: Version subscription messages + +use crate::xcm_common::*; +use moonbase_runtime::{Runtime, RuntimeCall}; +use parity_scale_codec::Encode; +use xcm::latest::prelude::*; +use xcm_executor::traits::QueryHandler; + +#[test] +fn barrier_allows_paid_execution_from_relay() { + ExtBuilder::default().build().execute_with(|| { + let origin = Location::parent(); + let message: Xcm = Xcm(vec![ + WithdrawAsset((Location::parent(), ONE_DOT).into()), + BuyExecution { + fees: (Location::parent(), ONE_DOT).into(), + weight_limit: WeightLimit::Unlimited, + }, + DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: ALICE, + }], + ), + }, + ]); + + let outcome = execute_xcm(origin, message); + // Should not be blocked by barrier (may fail later due to no funds, but not barrier) + assert!(!is_barrier_error(&outcome)); + }); +} + +#[test] +fn barrier_allows_paid_execution_from_sibling() { + ExtBuilder::default().build().execute_with(|| { + let origin = Location::new(1, [Parachain(2000)]); + let message: Xcm = Xcm(vec![ + WithdrawAsset((Location::parent(), ONE_DOT).into()), + BuyExecution { + fees: (Location::parent(), ONE_DOT).into(), + weight_limit: WeightLimit::Unlimited, + }, + DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: BOB, + }], + ), + }, + ]); + + let outcome = execute_xcm(origin, message); + assert!(!is_barrier_error(&outcome)); + }); +} + +#[test] +fn barrier_passes_unpaid_with_weight_credit() { + ExtBuilder::default().build().execute_with(|| { + // TakeWeightCredit is the first barrier and passes when the message + // weight is within the pre-credited amount. `execute_xcm` passes + // Weight::zero() as credit, so unpaid messages are rejected there. + // Use `execute_xcm_with_credit` with a generous credit instead. + let origin = Location::parent(); + let message: Xcm = Xcm(vec![DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: ALICE, + }], + ), + }]); + + let outcome = execute_xcm_with_credit(origin, message, Weight::MAX); + // TakeWeightCredit allows this to pass the barrier (may fail later for other reasons) + assert!( + !is_barrier_error(&outcome), + "TakeWeightCredit should allow messages with credited weight" + ); + }); +} + +#[test] +fn barrier_allows_subscription_messages() { + ExtBuilder::default().build().execute_with(|| { + let origin = Location::parent(); + // SubscribeVersion is allowed by AllowSubscriptionsFrom + let message: Xcm = Xcm(vec![SubscribeVersion { + query_id: 0, + max_response_weight: Weight::from_parts(1_000_000, 64 * 1024), + }]); + + let outcome = execute_xcm(origin, message); + // Should not be a barrier error - subscriptions are allowed + assert!(!is_barrier_error(&outcome)); + }); +} + +#[test] +fn barrier_allows_unsubscribe_messages() { + ExtBuilder::default().build().execute_with(|| { + let origin = Location::parent(); + let message: Xcm = Xcm(vec![UnsubscribeVersion]); + + let outcome = execute_xcm(origin, message); + assert!(!is_barrier_error(&outcome)); + }); +} + +#[test] +fn barrier_allows_paid_execution_with_descend_origin() { + ExtBuilder::default().build().execute_with(|| { + // Test that WithComputedOrigin allows descending origin + let origin = Location::parent(); + let message: Xcm = Xcm(vec![ + DescendOrigin( + [AccountId32 { + network: None, + id: [1u8; 32], + }] + .into(), + ), + WithdrawAsset((Location::parent(), ONE_DOT).into()), + BuyExecution { + fees: (Location::parent(), ONE_DOT).into(), + weight_limit: WeightLimit::Unlimited, + }, + DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: ALICE, + }], + ), + }, + ]); + + let outcome = execute_xcm(origin, message); + assert!(!is_barrier_error(&outcome)); + }); +} + +#[test] +fn barrier_allows_set_topic() { + ExtBuilder::default().build().execute_with(|| { + // SetTopic is wrapped by TrailingSetTopicAsId so should be handled + let origin = Location::parent(); + let message: Xcm = Xcm(vec![ + WithdrawAsset((Location::parent(), ONE_DOT).into()), + BuyExecution { + fees: (Location::parent(), ONE_DOT).into(), + weight_limit: WeightLimit::Unlimited, + }, + SetTopic([0u8; 32]), + ]); + + let outcome = execute_xcm(origin, message); + assert!(!is_barrier_error(&outcome)); + }); +} + +#[test] +fn barrier_with_computed_origin_rejects_when_depth_limit_exceeded() { + ExtBuilder::default().build().execute_with(|| { + // WithComputedOrigin is configured with ConstU32<8>, meaning it will skip at most 8 + // DescendOrigin (or similar) instructions when computing the origin. If the message + // contains more than 8 such instructions, WithComputedOrigin cannot reach the inner + // barriers (AllowTopLevelPaidExecutionFrom, AllowSubscriptionsFrom) and the message + // is rejected. + let origin = Location::parent(); + + // Build a message with 9 DescendOrigin instructions, exceeding the limit of 8 + let mut instructions: Vec> = Vec::new(); + for i in 0..9 { + instructions.push(DescendOrigin( + [AccountId32 { + network: None, + id: [i as u8; 32], + }] + .into(), + )); + } + instructions.push(WithdrawAsset((Location::parent(), ONE_DOT).into())); + instructions.push(BuyExecution { + fees: (Location::parent(), ONE_DOT).into(), + weight_limit: WeightLimit::Unlimited, + }); + + let message: Xcm = Xcm(instructions); + let outcome = execute_xcm(origin, message); + assert!( + is_barrier_error(&outcome), + "Message exceeding WithComputedOrigin depth limit of 8 should be rejected" + ); + }); +} + +#[test] +fn barrier_with_computed_origin_allows_at_depth_limit() { + ExtBuilder::default().build().execute_with(|| { + // WithComputedOrigin is configured with ConstU32<8>. A message with exactly 8 + // DescendOrigin instructions should still be processed by the inner barriers. + let origin = Location::parent(); + + let mut instructions: Vec> = Vec::new(); + for i in 0..8 { + instructions.push(DescendOrigin( + [AccountId32 { + network: None, + id: [i as u8; 32], + }] + .into(), + )); + } + instructions.push(WithdrawAsset((Location::parent(), ONE_DOT).into())); + instructions.push(BuyExecution { + fees: (Location::parent(), ONE_DOT).into(), + weight_limit: WeightLimit::Unlimited, + }); + + let message: Xcm = Xcm(instructions); + let outcome = execute_xcm(origin, message); + // Should pass the barrier (may fail later for other reasons like no funds) + assert!( + !is_barrier_error(&outcome), + "Message within WithComputedOrigin depth limit of 8 should pass the barrier" + ); + }); +} + +#[test] +fn barrier_allows_paid_execution_from_account_key20() { + ExtBuilder::default().build().execute_with(|| { + let origin = Location::new( + 0, + [AccountKey20 { + network: Some(NetworkId::Polkadot), + key: ALICE, + }], + ); + let message: Xcm = Xcm(vec![ + WithdrawAsset((Location::parent(), ONE_DOT).into()), + BuyExecution { + fees: (Location::parent(), ONE_DOT).into(), + weight_limit: WeightLimit::Unlimited, + }, + DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: BOB, + }], + ), + }, + ]); + + let outcome = execute_xcm(origin, message); + assert!( + !is_barrier_error(&outcome), + "Paid execution from AccountKey20 should pass the barrier" + ); + }); +} + +#[test] +fn barrier_rejects_unpaid_execution_from_sibling() { + ExtBuilder::default().build().execute_with(|| { + let origin = Location::new(1, [Parachain(2000)]); + let message: Xcm = Xcm(vec![ + UnpaidExecution { + weight_limit: Unlimited, + check_origin: None, + }, + DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: ALICE, + }], + ), + }, + ]); + + let outcome = execute_xcm(origin, message); + assert!( + is_barrier_error(&outcome), + "UnpaidExecution from sibling should be rejected by barrier" + ); + }); +} + +#[test] +fn barrier_rejects_unpaid_transact_from_sibling() { + ExtBuilder::default().build().execute_with(|| { + let origin = Location::new(1, [Parachain(2000)]); + let encoded_call = RuntimeCall::System(frame_system::Call::remark_with_event { + remark: vec![1, 2, 3], + }) + .encode(); + + let message: Xcm = Xcm(vec![ + UnpaidExecution { + weight_limit: Unlimited, + check_origin: None, + }, + Transact { + origin_kind: OriginKind::SovereignAccount, + call: encoded_call.into(), + fallback_max_weight: Some(Weight::from_parts(100_000_000, 10_000)), + }, + ]); + + let outcome = execute_xcm(origin, message); + assert!( + is_barrier_error(&outcome), + "UnpaidExecution + Transact from sibling should be rejected" + ); + }); +} + +#[test] +fn barrier_allows_known_query_response() { + ExtBuilder::default().build().execute_with(|| { + let relay_origin = Location::parent(); + + let query_id = pallet_xcm::Pallet::::new_query( + relay_origin.clone(), + 100u32.into(), + Location::here(), + ); + + let message: Xcm = Xcm(vec![QueryResponse { + query_id, + response: Response::Null, + max_weight: Weight::from_parts(1_000_000, 64 * 1024), + querier: Some(Location::here()), + }]); + + let outcome = execute_xcm(relay_origin, message); + assert!( + !is_barrier_error(&outcome), + "Known query response should pass AllowKnownQueryResponses barrier" + ); + }); +} + +#[test] +fn barrier_rejects_unknown_query_response() { + ExtBuilder::default().build().execute_with(|| { + let origin = Location::parent(); + let unknown_query_id = 999_999u64; + + let message: Xcm = Xcm(vec![QueryResponse { + query_id: unknown_query_id, + response: Response::Null, + max_weight: Weight::from_parts(1_000_000, 64 * 1024), + querier: Some(Location::here()), + }]); + + let outcome = execute_xcm(origin, message); + assert!( + is_barrier_error(&outcome), + "Unknown query response should be rejected by barrier" + ); + }); +} diff --git a/runtime/moonbase/tests/xcm_config/location.rs b/runtime/moonbase/tests/xcm_config/location.rs new file mode 100644 index 00000000000..b0566e3dd8b --- /dev/null +++ b/runtime/moonbase/tests/xcm_config/location.rs @@ -0,0 +1,181 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Tests for LocationToAccountId configuration. +//! +//! LocationToAccountId converts XCM Locations to AccountIds. Moonbase uses: +//! - ParentIsPreset: Relay chain maps to a preset account +//! - SiblingParachainConvertsVia: Sibling parachains map to sovereign accounts +//! - AccountKey20Aliases: AccountKey20 junctions map directly to AccountId +//! - HashedDescription: Other locations map via a hashed description +//! - ExternalConsensusLocationsConverterFor: Bridged locations map to accounts + +use crate::xcm_common::*; +use moonbase_runtime::{ + xcm_config::{LocationToAccountId, LocationToH160}, + AccountId, +}; +use polkadot_parachain::primitives::Sibling; +use sp_core::H160; +use sp_runtime::traits::AccountIdConversion; +use xcm::latest::prelude::*; +use xcm_executor::traits::ConvertLocation; + +#[test] +fn location_converts_relay_to_account() { + ExtBuilder::default().build().execute_with(|| { + let relay_location = Location::parent(); + let account = LocationToAccountId::convert_location(&relay_location); + + assert!( + account.is_some(), + "Relay location should convert to account" + ); + + // ParentIsPreset decodes b"Parent" padded with zeros into AccountId (H160). + let relay_account = account.unwrap(); + let expected: [u8; 20] = { + let mut buf = [0u8; 20]; + buf[..6].copy_from_slice(b"Parent"); + buf + }; + assert_eq!( + relay_account, + AccountId::from(expected), + "Relay sovereign should be derived from b\"Parent\" via ParentIsPreset" + ); + }); +} + +#[test] +fn location_converts_sibling_parachain_to_sovereign_account() { + ExtBuilder::default().build().execute_with(|| { + // SiblingParachainConvertsVia derives a sovereign account from the parachain ID + // using Sibling's AccountIdConversion::into_account_truncating. + let sibling_para_id = 2000u32; + let sibling_location = Location::new(1, [Parachain(sibling_para_id)]); + let account = LocationToAccountId::convert_location(&sibling_location); + + let expected: AccountId = Sibling::from(sibling_para_id).into_account_truncating(); + assert_eq!( + account, + Some(expected), + "Sibling parachain should convert to sovereign account derived from para ID" + ); + }); +} + +#[test] +fn location_converts_different_siblings_to_different_accounts() { + ExtBuilder::default().build().execute_with(|| { + let account_a = LocationToAccountId::convert_location(&Location::new(1, [Parachain(2000)])); + let account_b = LocationToAccountId::convert_location(&Location::new(1, [Parachain(3000)])); + + assert!( + account_a.is_some(), + "Sibling 2000 should convert to account" + ); + assert!( + account_b.is_some(), + "Sibling 3000 should convert to account" + ); + assert_ne!( + account_a, account_b, + "Different sibling parachains should have different sovereign accounts" + ); + }); +} + +#[test] +fn location_converts_account_key20_directly() { + ExtBuilder::default().build().execute_with(|| { + let expected_account = ALICE; + let location = Location::new( + 0, + [AccountKey20 { + network: Some(NetworkId::ByGenesis(xcm::v5::WESTEND_GENESIS_HASH)), + key: expected_account, + }], + ); + + let account = LocationToAccountId::convert_location(&location); + + assert!(account.is_some(), "AccountKey20 should convert to account"); + assert_eq!( + account.unwrap(), + AccountId::from(expected_account), + "AccountKey20 should map directly to the same account" + ); + }); +} + +#[test] +fn location_rejects_unsupported_multi_junction_patterns() { + ExtBuilder::default().build().execute_with(|| { + // HashedDescription<_, DescribeFamily> only describes + // locations whose tail (after the family prefix) is a single terminal junction. + // A sibling with multiple interior junctions beyond Parachain is not describable + // by DescribeAllTerminal and no other converter handles it, so it must return None. + let complex_location = + Location::new(1, [Parachain(2000), PalletInstance(10), GeneralIndex(42)]); + + assert_eq!( + LocationToAccountId::convert_location(&complex_location), + None, + "Multi-junction sibling location should not convert to an account" + ); + }); +} + +#[test] +fn location_converts_bridged_parachain() { + ExtBuilder::default().build().execute_with(|| { + // A parachain from another consensus (bridged) + let bridged_location = + Location::new(2, [GlobalConsensus(NetworkId::Kusama), Parachain(1000)]); + + let account = LocationToAccountId::convert_location(&bridged_location); + + // ExternalConsensusLocationsConverterFor should handle this + assert!( + account.is_some(), + "Bridged parachain should convert to account" + ); + }); +} + +#[test] +fn location_to_h160_converts_account_key20() { + ExtBuilder::default().build().execute_with(|| { + // LocationToH160 wraps LocationToAccountId and converts to H160, + // used by EVM-related XCM operations. + let location = Location::new( + 0, + [AccountKey20 { + network: Some(NetworkId::ByGenesis(xcm::v5::WESTEND_GENESIS_HASH)), + key: ALICE, + }], + ); + + let h160 = LocationToH160::convert_location(&location); + + assert_eq!( + h160, + Some(H160::from(ALICE)), + "LocationToH160 should convert AccountKey20 to the matching H160" + ); + }); +} diff --git a/runtime/moonbase/tests/xcm_config/main.rs b/runtime/moonbase/tests/xcm_config/main.rs new file mode 100644 index 00000000000..db968130f8f --- /dev/null +++ b/runtime/moonbase/tests/xcm_config/main.rs @@ -0,0 +1,32 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! XCM Configuration Tests (Level 1) — Moonbase + +#![cfg(test)] + +#[path = "../common/mod.rs"] +mod common; + +mod xcm_common; + +mod barriers; +mod location; +mod origin; +mod reserves; +mod traders; +mod transactors; +mod weigher; diff --git a/runtime/moonbase/tests/xcm_config/origin.rs b/runtime/moonbase/tests/xcm_config/origin.rs new file mode 100644 index 00000000000..8c608ce0a0b --- /dev/null +++ b/runtime/moonbase/tests/xcm_config/origin.rs @@ -0,0 +1,177 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Tests for XcmOriginToTransactDispatchOrigin and SafeCallFilter configuration. +//! +//! XcmOriginToTransactDispatchOrigin converts XCM locations + OriginKind into +//! dispatch origins for Transact. Moonbase uses: +//! - SovereignSignedViaLocation: SovereignAccount kind → Signed origin +//! - RelayChainAsNative: Native kind from relay → relay origin +//! - SiblingParachainAsNative: Native kind from sibling → sibling origin +//! - XcmPassthrough: Xcm kind → pallet_xcm origin +//! - SignedAccountKey20AsNative: Native kind from AccountKey20 → Signed origin +//! +//! SafeCallFilter determines which calls are allowed via XCM Transact. + +use crate::xcm_common::*; +use moonbase_runtime::{ + xcm_config::{SafeCallFilter, XcmOriginToTransactDispatchOrigin}, + RuntimeCall, RuntimeOrigin, +}; +use xcm::latest::prelude::*; +use xcm_executor::traits::ConvertOrigin; + +#[test] +fn origin_converts_relay_with_sovereign_kind() { + ExtBuilder::default().build().execute_with(|| { + // SovereignSignedViaLocation converts relay location + SovereignAccount kind + // into a Signed origin using the relay's sovereign account. + let relay = Location::parent(); + + let result = + >::convert_origin( + relay, + OriginKind::SovereignAccount, + ); + + assert!( + result.is_ok(), + "Relay + SovereignAccount should convert to dispatch origin" + ); + }); +} + +#[test] +fn origin_converts_sibling_with_sovereign_kind() { + ExtBuilder::default().build().execute_with(|| { + let sibling = Location::new(1, [Parachain(2000)]); + + let result = + >::convert_origin( + sibling, + OriginKind::SovereignAccount, + ); + + assert!( + result.is_ok(), + "Sibling + SovereignAccount should convert to dispatch origin" + ); + }); +} + +#[test] +fn origin_converts_relay_with_native_kind() { + ExtBuilder::default().build().execute_with(|| { + // RelayChainAsNative converts relay location + Native kind into the + // relay chain origin (used for governance-like calls). + let relay = Location::parent(); + + let result = + >::convert_origin( + relay, + OriginKind::Native, + ); + + assert!( + result.is_ok(), + "Relay + Native should convert via RelayChainAsNative" + ); + }); +} + +#[test] +fn origin_converts_relay_with_xcm_kind() { + ExtBuilder::default().build().execute_with(|| { + // XcmPassthrough converts any location + Xcm kind into a pallet_xcm origin. + let relay = Location::parent(); + + let result = + >::convert_origin( + relay, + OriginKind::Xcm, + ); + + assert!( + result.is_ok(), + "Relay + Xcm should convert via XcmPassthrough" + ); + }); +} + +#[test] +fn origin_converts_account_key20_with_native_kind() { + ExtBuilder::default().build().execute_with(|| { + // SignedAccountKey20AsNative converts AccountKey20 + Native kind into a + // Signed origin with the 20-byte account. + let account_location = Location::new( + 0, + [AccountKey20 { + network: Some(NetworkId::ByGenesis(xcm::v5::WESTEND_GENESIS_HASH)), + key: ALICE, + }], + ); + + let result = + >::convert_origin( + account_location, + OriginKind::Native, + ); + + assert!( + result.is_ok(), + "AccountKey20 + Native should convert via SignedAccountKey20AsNative" + ); + }); +} + +#[test] +fn origin_rejects_superuser_kind() { + ExtBuilder::default().build().execute_with(|| { + // No converter handles Superuser kind, so it should be rejected. + let relay = Location::parent(); + + let result = + >::convert_origin( + relay, + OriginKind::Superuser, + ); + + assert!(result.is_err(), "Superuser kind should not be convertible"); + }); +} + +#[test] +fn safe_call_filter_allows_all_calls() { + ExtBuilder::default().build().execute_with(|| { + use frame_support::traits::Contains; + + // Moonbase's SafeCallFilter currently allows all calls (returns true). + // This is intentional — filtering is handled at the EVM level. + // Verify with calls from two distinct pallets to confirm the filter + // is truly permissive, not a pallet-specific whitelist. + let remark_call = RuntimeCall::System(frame_system::Call::remark { remark: vec![] }); + assert!( + SafeCallFilter::contains(&remark_call), + "SafeCallFilter should allow System::remark" + ); + + let utility_call = RuntimeCall::Utility(pallet_utility::Call::batch { calls: vec![] }); + assert!( + SafeCallFilter::contains(&utility_call), + "SafeCallFilter should allow Utility::batch" + ); + }); +} diff --git a/runtime/moonbase/tests/xcm_config/reserves.rs b/runtime/moonbase/tests/xcm_config/reserves.rs new file mode 100644 index 00000000000..bddc4987f33 --- /dev/null +++ b/runtime/moonbase/tests/xcm_config/reserves.rs @@ -0,0 +1,267 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Tests for IsReserve (Reserves) configuration. +//! +//! The Reserves type determines which assets are recognized as reserve assets +//! and which origin is allowed to act as reserve for those assets. +//! +//! Moonbase's Reserves configuration allows: +//! - IsBridgedConcreteAssetFrom: Bridged assets from Asset Hub +//! - Case: DOT from Asset Hub +//! - MultiNativeAsset>: Self-reserve + +use crate::xcm_common::*; +use frame_support::traits::ContainsPair; +use moonbase_runtime::xcm_config::{AssetHubLocation, XcmExecutorConfig}; +use xcm::latest::prelude::*; +use xcm_primitives::IsBridgedConcreteAssetFrom; + +/// The actual `IsReserve` type wired into the XCM executor. +type IsReserve = ::IsReserve; + +const ASSET_HUB_PARA_ID: u32 = 1001; + +#[test] +fn reserves_accepts_dot_from_asset_hub() { + ExtBuilder::default().build().execute_with(|| { + // DOT asset coming from Asset Hub should be accepted + let dot_asset = Asset { + id: AssetId(Location::parent()), + fun: Fungible(ONE_DOT), + }; + let asset_hub_origin = Location::new(1, [Parachain(ASSET_HUB_PARA_ID)]); + + // RelayChainNativeAssetFromAssetHub case should match this + type RelayFromAssetHub = + xcm_builder::Case; + + assert!( + RelayFromAssetHub::contains(&dot_asset, &asset_hub_origin), + "DOT from Asset Hub should be accepted as reserve" + ); + assert!( + IsReserve::contains(&dot_asset, &asset_hub_origin), + "IsReserve must accept DOT from Asset Hub (runtime wiring)" + ); + }); +} + +#[test] +fn reserves_accepts_bridged_assets_from_asset_hub() { + ExtBuilder::default().build().execute_with(|| { + // Bridged asset from another consensus (parents: 2) + let bridged_asset = Asset { + id: AssetId(Location::new( + 2, + [GlobalConsensus(NetworkId::Kusama), Parachain(1000)], + )), + fun: Fungible(1_000_000), + }; + let asset_hub_origin = AssetHubLocation::get(); + + // IsBridgedConcreteAssetFrom should match + assert!( + IsBridgedConcreteAssetFrom::::contains( + &bridged_asset, + &asset_hub_origin + ), + "Bridged assets from Asset Hub should be accepted" + ); + assert!( + IsReserve::contains(&bridged_asset, &asset_hub_origin), + "IsReserve must accept bridged assets from Asset Hub (runtime wiring)" + ); + }); +} + +#[test] +fn reserves_rejects_bridged_assets_from_wrong_origin() { + ExtBuilder::default().build().execute_with(|| { + // Bridged asset from another consensus + let bridged_asset = Asset { + id: AssetId(Location::new( + 2, + [GlobalConsensus(NetworkId::Kusama), Parachain(1000)], + )), + fun: Fungible(1_000_000), + }; + // Wrong origin - not Asset Hub + let wrong_origin = Location::new(1, [Parachain(2000)]); + + assert!( + !IsBridgedConcreteAssetFrom::::contains( + &bridged_asset, + &wrong_origin + ), + "Bridged assets from wrong origin should be rejected" + ); + }); +} + +#[test] +fn reserves_rejects_non_bridged_assets_via_bridged_filter() { + ExtBuilder::default().build().execute_with(|| { + // Non-bridged asset (parents: 1, not 2) + let local_asset = Asset { + id: AssetId(Location::new(1, [Parachain(1000)])), + fun: Fungible(1_000_000), + }; + let asset_hub_origin = AssetHubLocation::get(); + + // IsBridgedConcreteAssetFrom requires parents > 1 + assert!( + !IsBridgedConcreteAssetFrom::::contains( + &local_asset, + &asset_hub_origin + ), + "Non-bridged assets should not match bridged asset filter" + ); + }); +} + +#[test] +fn reserves_accepts_self_reserve() { + ExtBuilder::default().build().execute_with(|| { + use frame_support::traits::PalletInfoAccess; + use moonbase_runtime::xcm_config::SelfLocationAbsolute; + use moonbase_runtime::Balances; + use xcm_primitives::{AbsoluteAndRelativeReserve, MultiNativeAsset}; + + let self_reserve = Location::new(0, [PalletInstance(Balances::index() as u8)]); + + let native_asset = Asset { + id: AssetId(self_reserve), + fun: Fungible(1_000_000_000_000_000_000), // 1 UNIT + }; + + // MultiNativeAsset accepts an asset when the origin matches the asset's + // reserve. For our own native token the reserve is ourselves + // (Location::here()), so origin = here() must pass. + let self_origin = Location::here(); + + assert!( + MultiNativeAsset::>::contains( + &native_asset, + &self_origin + ), + "Self reserve asset should be accepted when origin is here()" + ); + assert!( + IsReserve::contains(&native_asset, &self_origin), + "IsReserve must accept self-reserve (runtime wiring)" + ); + }); +} + +#[test] +fn reserves_accepts_sibling_native_asset() { + ExtBuilder::default().build().execute_with(|| { + // Native asset from a sibling parachain + let sibling_asset = Asset { + id: AssetId(Location::new(1, [Parachain(2000), PalletInstance(10)])), + fun: Fungible(1_000_000), + }; + let sibling_origin = Location::new(1, [Parachain(2000)]); + + // MultiNativeAsset should accept this - the origin matches the asset's reserve + // This is checked by matching asset's reserve location to the origin + use moonbase_runtime::xcm_config::SelfLocationAbsolute; + use xcm_primitives::{AbsoluteAndRelativeReserve, MultiNativeAsset}; + + // The reserve of sibling asset is the sibling chain itself + // MultiNativeAsset will check if origin matches reserve + assert!( + MultiNativeAsset::>::contains( + &sibling_asset, + &sibling_origin + ), + "Sibling native asset should be accepted when origin matches reserve" + ); + assert!( + IsReserve::contains(&sibling_asset, &sibling_origin), + "IsReserve must accept sibling native asset (runtime wiring)" + ); + }); +} + +#[test] +fn reserves_rejects_asset_with_mismatched_origin() { + ExtBuilder::default().build().execute_with(|| { + // Asset claims to be from parachain 2000 + let asset = Asset { + id: AssetId(Location::new(1, [Parachain(2000), PalletInstance(10)])), + fun: Fungible(1_000_000), + }; + // But origin is from parachain 3000 + let wrong_origin = Location::new(1, [Parachain(3000)]); + + use moonbase_runtime::xcm_config::SelfLocationAbsolute; + use xcm_primitives::{AbsoluteAndRelativeReserve, MultiNativeAsset}; + + assert!( + !MultiNativeAsset::>::contains( + &asset, + &wrong_origin + ), + "Asset from mismatched origin should be rejected" + ); + }); +} + +#[test] +fn reserves_accepts_dot_from_relay() { + ExtBuilder::default().build().execute_with(|| { + let dot_asset = Asset { + id: AssetId(Location::parent()), + fun: Fungible(ONE_DOT), + }; + let relay_origin = Location::parent(); + + use moonbase_runtime::xcm_config::SelfLocationAbsolute; + use xcm_primitives::{AbsoluteAndRelativeReserve, MultiNativeAsset}; + + assert!( + MultiNativeAsset::>::contains( + &dot_asset, + &relay_origin + ), + "DOT from relay should be accepted as reserve" + ); + assert!( + IsReserve::contains(&dot_asset, &relay_origin), + "IsReserve must accept DOT from relay (runtime wiring)" + ); + }); +} + +#[test] +fn teleport_always_rejected() { + ExtBuilder::default().build().execute_with(|| { + type IsTeleporter = ::IsTeleporter; + + let dot = Asset { + id: AssetId(Location::parent()), + fun: Fungible(ONE_DOT), + }; + let relay_origin = Location::parent(); + + assert!( + !IsTeleporter::contains(&dot, &relay_origin), + "IsTeleporter should reject every asset/origin pair" + ); + }); +} diff --git a/runtime/moonbase/tests/xcm_config/traders.rs b/runtime/moonbase/tests/xcm_config/traders.rs new file mode 100644 index 00000000000..248d3276aad --- /dev/null +++ b/runtime/moonbase/tests/xcm_config/traders.rs @@ -0,0 +1,286 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Tests for the XCM Trader (pallet_xcm_weight_trader::Trader) configuration. +//! +//! The trader is responsible for converting weight to fee and accepting payment +//! in different assets. Moonbase uses a custom trader that: +//! - Accepts the native token (UNIT) at standard rates +//! - Accepts registered foreign assets at configured relative prices + +use crate::xcm_common::*; +use frame_support::traits::PalletInfoAccess; +use moonbase_runtime::{Balances, Runtime, Treasury}; +use pallet_xcm_weight_trader::{Pallet as XcmWeightTrader, Trader}; +use sp_weights::Weight; +use xcm::latest::prelude::*; +use xcm::VersionedAssetId; +use xcm_executor::traits::WeightTrader; +use xcm_executor::AssetsInHolding; + +fn native_location() -> Location { + Location::new(0, [PalletInstance(Balances::index() as u8)]) +} + +#[test] +fn trader_accepts_native_token() { + ExtBuilder::default().build().execute_with(|| { + let mut trader = Trader::::new(); + let weight_to_buy = Weight::from_parts(1_000_000_000, 64 * 1024); + + // Create payment in native token + let mut payment = AssetsInHolding::new(); + payment.subsume(Asset { + id: AssetId(native_location()), + fun: Fungible(ONE_UNIT), + }); + + let context = XcmContext::with_message_id([0u8; 32]); + let result = trader.buy_weight(weight_to_buy, payment.clone(), &context); + + // Should succeed - native token is always accepted + assert!(result.is_ok(), "Native token should be accepted for fees"); + }); +} + +#[test] +fn trader_computes_native_fee_correctly() { + ExtBuilder::default().build().execute_with(|| { + let weight = Weight::from_parts(1_000_000_000, 64 * 1024); + let native_loc = native_location(); + + // Compute fee for native token using public API + let versioned_asset_id = VersionedAssetId::V5(AssetId(native_loc)); + let fee_result = + XcmWeightTrader::::query_weight_to_asset_fee(weight, versioned_asset_id); + + assert!(fee_result.is_ok(), "Should compute fee for native token"); + let fee = fee_result.unwrap(); + assert!(fee > 0, "Fee should be non-zero"); + }); +} + +#[test] +fn trader_rejects_unsupported_asset() { + ExtBuilder::default().build().execute_with(|| { + let mut trader = Trader::::new(); + let weight_to_buy = Weight::from_parts(1_000_000_000, 64 * 1024); + + // Try to pay with unsupported asset + let unsupported_asset_location = Location::new(1, [Parachain(9999), PalletInstance(99)]); + let mut payment = AssetsInHolding::new(); + payment.subsume(Asset { + id: AssetId(unsupported_asset_location.clone()), + fun: Fungible(1_000_000_000_000), + }); + + let context = XcmContext::with_message_id([0u8; 32]); + let result = trader.buy_weight(weight_to_buy, payment, &context); + + // Should fail - asset not registered + assert!(result.is_err(), "Unsupported asset should be rejected"); + assert_eq!(result.unwrap_err(), XcmError::AssetNotFound); + }); +} + +#[test] +fn trader_accepts_registered_foreign_asset() { + // Register DOT as supported asset + let dot_location = Location::parent(); + + ExtBuilder::default() + .with_xcm_assets(vec![XcmAssetInitialization { + asset_id: 1, + xcm_location: dot_location.clone(), + decimals: 10, + name: "Polkadot", + symbol: "DOT", + balances: vec![], + }]) + .build() + .execute_with(|| { + // First verify the asset is registered and queryable + let versioned_asset_id = VersionedAssetId::V5(AssetId(dot_location.clone())); + let weight = Weight::from_parts(1_000_000_000, 64 * 1024); + let fee_result = + XcmWeightTrader::::query_weight_to_asset_fee(weight, versioned_asset_id); + + // If the query succeeds, the asset is properly registered + assert!( + fee_result.is_ok(), + "Registered foreign asset should be queryable" + ); + + // Now test the trader directly + let mut trader = Trader::::new(); + let weight_to_buy = Weight::from_parts(1_000_000_000, 64 * 1024); + + // Pay with DOT - need sufficient amount to cover the computed fee + let fee = fee_result.unwrap(); + let mut payment = AssetsInHolding::new(); + payment.subsume(Asset { + id: AssetId(dot_location.clone()), + fun: Fungible(fee * 2), // Double to ensure enough + }); + + let context = XcmContext::with_message_id([0u8; 32]); + let result = trader.buy_weight(weight_to_buy, payment, &context); + + // Should succeed - DOT is registered in XcmWeightTrader + assert!( + result.is_ok(), + "Registered foreign asset should be accepted: {:?}", + result + ); + }); +} + +#[test] +fn trader_computes_foreign_asset_fee_with_relative_price() { + let dot_location = Location::parent(); + + ExtBuilder::default() + .with_xcm_assets(vec![XcmAssetInitialization { + asset_id: 1, + xcm_location: dot_location.clone(), + decimals: 10, + name: "Polkadot", + symbol: "DOT", + balances: vec![], + }]) + .build() + .execute_with(|| { + let weight = Weight::from_parts(1_000_000_000, 64 * 1024); + + // Compute fee for DOT using public API + let versioned_asset_id = VersionedAssetId::V5(AssetId(dot_location.clone())); + let fee_result = + XcmWeightTrader::::query_weight_to_asset_fee(weight, versioned_asset_id); + + assert!( + fee_result.is_ok(), + "Should compute fee for registered asset" + ); + let fee = fee_result.unwrap(); + // Fee should be computed based on relative price + assert!(fee > 0, "Fee should be non-zero"); + }); +} + +#[test] +fn trader_cannot_buy_weight_twice() { + ExtBuilder::default().build().execute_with(|| { + let mut trader = Trader::::new(); + let weight_to_buy = Weight::from_parts(1_000_000_000, 64 * 1024); + let context = XcmContext::with_message_id([0u8; 32]); + + // First purchase + let mut payment1 = AssetsInHolding::new(); + payment1.subsume(Asset { + id: AssetId(native_location()), + fun: Fungible(ONE_UNIT), + }); + let first_result = trader.buy_weight(weight_to_buy, payment1, &context); + assert!( + first_result.is_ok(), + "First buy_weight must succeed for the second-purchase test to be meaningful" + ); + + // Second purchase should fail + let mut payment2 = AssetsInHolding::new(); + payment2.subsume(Asset { + id: AssetId(native_location()), + fun: Fungible(ONE_UNIT), + }); + let result = trader.buy_weight(weight_to_buy, payment2, &context); + + assert!(result.is_err(), "Second buy_weight should fail"); + assert_eq!(result.unwrap_err(), XcmError::NotWithdrawable); + }); +} + +#[test] +fn trader_refunds_unused_weight() { + ExtBuilder::default().build().execute_with(|| { + let mut trader = Trader::::new(); + let weight_bought = Weight::from_parts(2_000_000_000, 128 * 1024); + let weight_used = Weight::from_parts(1_000_000_000, 64 * 1024); + let context = XcmContext::with_message_id([0u8; 32]); + + // Buy more weight than needed + let mut payment = AssetsInHolding::new(); + payment.subsume(Asset { + id: AssetId(native_location()), + fun: Fungible(ONE_UNIT * 10), // Plenty of funds + }); + + let buy_result = trader.buy_weight(weight_bought, payment, &context); + assert!(buy_result.is_ok()); + + // Refund unused weight + let unused_weight = weight_bought.saturating_sub(weight_used); + let refund = trader.refund_weight(unused_weight, &context); + + // Must get a refund + let refunded_asset = refund.expect("refund_weight must return Some for unused weight"); + assert_eq!( + refunded_asset.id, + AssetId(native_location()), + "Refunded asset must be the native token" + ); + match refunded_asset.fun { + Fungible(amount) => { + assert!(amount > 0, "Refunded amount must be non-zero"); + } + _ => panic!("Expected fungible refund"), + } + }); +} + +#[test] +fn trader_handles_insufficient_payment() { + ExtBuilder::default().build().execute_with(|| { + let mut trader = Trader::::new(); + let weight_to_buy = Weight::from_parts(1_000_000_000_000, 64 * 1024); // Very large weight + let context = XcmContext::with_message_id([0u8; 32]); + + // Try to pay with very small amount + let mut payment = AssetsInHolding::new(); + payment.subsume(Asset { + id: AssetId(native_location()), + fun: Fungible(1), // Tiny amount + }); + + let result = trader.buy_weight(weight_to_buy, payment, &context); + + // Should fail - insufficient payment + assert!(result.is_err(), "Insufficient payment should be rejected"); + assert_eq!(result.unwrap_err(), XcmError::TooExpensive); + }); +} + +#[test] +fn xcm_fees_go_to_treasury() { + ExtBuilder::default().build().execute_with(|| { + use moonbase_runtime::xcm_config::XcmFeesAccount; + + assert_eq!( + XcmFeesAccount::get(), + Treasury::account_id(), + "XCM fee destination should be the treasury account" + ); + }); +} diff --git a/runtime/moonbase/tests/xcm_config/transactors.rs b/runtime/moonbase/tests/xcm_config/transactors.rs new file mode 100644 index 00000000000..d68956395b6 --- /dev/null +++ b/runtime/moonbase/tests/xcm_config/transactors.rs @@ -0,0 +1,358 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Tests for AssetTransactors configuration. +//! +//! AssetTransactors handle the deposit and withdrawal of assets via XCM. +//! Moonbase uses a tuple of transactors: +//! - LocalAssetTransactor: Handles native token (UNIT) using pallet_balances +//! - EvmForeignAssets: Handles registered foreign assets +//! - Erc20XcmBridge: Handles ERC20 tokens via the bridge + +use crate::xcm_common::*; +use frame_support::traits::{Currency, PalletInfoAccess}; +use moonbase_runtime::{AccountId, Balances}; +use sp_core::U256; +use xcm::latest::prelude::*; +use xcm_executor::traits::TransactAsset; + +fn alice_account() -> AccountId { + AccountId::from(ALICE) +} + +fn bob_account() -> AccountId { + AccountId::from(BOB) +} + +fn native_asset_location() -> Location { + Location::new(0, [PalletInstance(Balances::index() as u8)]) +} + +#[test] +fn local_transactor_deposits_native_token() { + ExtBuilder::default() + .with_balances(vec![(alice_account(), ONE_UNIT * 100)]) + .build() + .execute_with(|| { + use moonbase_runtime::xcm_config::AssetTransactors; + + let initial_balance = Balances::free_balance(bob_account()); + + let asset = Asset { + id: AssetId(native_asset_location()), + fun: Fungible(ONE_UNIT), + }; + let destination = Location::new( + 0, + [AccountKey20 { + network: None, + key: BOB, + }], + ); + + // Deposit native asset to Bob + let result = + ::deposit_asset(&asset, &destination, None); + + assert!(result.is_ok(), "Deposit should succeed"); + let final_balance = Balances::free_balance(bob_account()); + assert_eq!( + final_balance, + initial_balance + ONE_UNIT, + "Balance should increase by deposited amount" + ); + }); +} + +#[test] +fn local_transactor_withdraws_native_token() { + ExtBuilder::default() + .with_balances(vec![(alice_account(), ONE_UNIT * 100)]) + .build() + .execute_with(|| { + use moonbase_runtime::xcm_config::AssetTransactors; + + let initial_balance = Balances::free_balance(alice_account()); + + let asset = Asset { + id: AssetId(native_asset_location()), + fun: Fungible(ONE_UNIT), + }; + let source = Location::new( + 0, + [AccountKey20 { + network: None, + key: ALICE, + }], + ); + + // Withdraw native asset from Alice + let result = ::withdraw_asset(&asset, &source, None); + + assert!(result.is_ok(), "Withdraw should succeed"); + let final_balance = Balances::free_balance(alice_account()); + assert_eq!( + final_balance, + initial_balance - ONE_UNIT, + "Balance should decrease by withdrawn amount" + ); + }); +} + +#[test] +fn local_transactor_fails_withdraw_insufficient_balance() { + ExtBuilder::default() + .with_balances(vec![(alice_account(), ONE_UNIT)]) // Only 1 UNIT + .build() + .execute_with(|| { + use moonbase_runtime::xcm_config::AssetTransactors; + + let asset = Asset { + id: AssetId(native_asset_location()), + fun: Fungible(ONE_UNIT * 100), // Try to withdraw 100 UNIT + }; + let source = Location::new( + 0, + [AccountKey20 { + network: None, + key: ALICE, + }], + ); + + let result = ::withdraw_asset(&asset, &source, None); + + assert!( + result.is_err(), + "Withdraw should fail with insufficient balance" + ); + }); +} + +#[test] +fn foreign_asset_transactor_deposits_registered_asset() { + let dot_location = Location::parent(); + + ExtBuilder::default() + .with_xcm_assets(vec![XcmAssetInitialization { + asset_id: 1, + xcm_location: dot_location.clone(), + decimals: 10, + name: "Polkadot", + symbol: "DOT", + balances: vec![], + }]) + .build() + .execute_with(|| { + use moonbase_runtime::xcm_config::AssetTransactors; + + let balance_before = moonbase_runtime::EvmForeignAssets::balance(1, bob_account()) + .unwrap_or(U256::zero()); + + let asset = Asset { + id: AssetId(dot_location.clone()), + fun: Fungible(ONE_DOT), + }; + let destination = Location::new( + 0, + [AccountKey20 { + network: None, + key: BOB, + }], + ); + + // Deposit DOT to Bob + let result = + ::deposit_asset(&asset, &destination, None); + + assert!( + result.is_ok(), + "Deposit of registered foreign asset should succeed" + ); + + let balance_after = moonbase_runtime::EvmForeignAssets::balance(1, bob_account()) + .expect("balance query should succeed after deposit"); + assert_eq!( + balance_after - balance_before, + U256::from(ONE_DOT), + "Bob's foreign asset balance should increase by the deposited amount" + ); + }); +} + +#[test] +fn transactor_fails_for_unregistered_asset() { + ExtBuilder::default().build().execute_with(|| { + use moonbase_runtime::xcm_config::AssetTransactors; + + // Unregistered asset location + let unknown_asset = Asset { + id: AssetId(Location::new(1, [Parachain(9999), PalletInstance(99)])), + fun: Fungible(1_000_000), + }; + let destination = Location::new( + 0, + [AccountKey20 { + network: None, + key: BOB, + }], + ); + + let result = + ::deposit_asset(&unknown_asset, &destination, None); + + // Should fail - asset not registered + assert!(result.is_err(), "Deposit of unregistered asset should fail"); + }); +} + +#[test] +fn transactor_withdraws_registered_foreign_asset() { + let dot_location = Location::parent(); + + ExtBuilder::default() + .with_xcm_assets(vec![XcmAssetInitialization { + asset_id: 1, + xcm_location: dot_location.clone(), + decimals: 10, + name: "Polkadot", + symbol: "DOT", + balances: vec![(bob_account(), ONE_DOT * 10)], + }]) + .build() + .execute_with(|| { + use moonbase_runtime::xcm_config::AssetTransactors; + + let balance_before = moonbase_runtime::EvmForeignAssets::balance(1, bob_account()) + .expect("balance query should succeed"); + + let asset = Asset { + id: AssetId(dot_location.clone()), + fun: Fungible(ONE_DOT), + }; + let source = Location::new( + 0, + [AccountKey20 { + network: None, + key: BOB, + }], + ); + + let result = ::withdraw_asset(&asset, &source, None); + + assert!( + result.is_ok(), + "Withdraw of registered foreign asset should succeed: {:?}", + result + ); + + let balance_after = moonbase_runtime::EvmForeignAssets::balance(1, bob_account()) + .expect("balance query should succeed after withdraw"); + assert_eq!( + balance_before - balance_after, + U256::from(ONE_DOT), + "Bob's foreign asset balance should decrease by the withdrawn amount" + ); + }); +} + +#[test] +fn transactor_handles_erc20_bridge_asset() { + ExtBuilder::default() + .with_balances(vec![(alice_account(), ONE_UNIT * 100)]) + .build() + .execute_with(|| { + use moonbase_runtime::xcm_config::AssetTransactors; + use moonbase_runtime::Erc20XcmBridge; + + let bridge_pallet_location = { + use frame_support::traits::PalletInfoAccess; + Location::new( + 0, + [PalletInstance( + ::index() as u8, + )], + ) + }; + + let fake_contract: [u8; 20] = [0xAA; 20]; + let erc20_asset_location = Location::new( + 0, + [ + PalletInstance(bridge_pallet_location.first_interior().map_or(0, |j| { + if let Junction::PalletInstance(idx) = j { + *idx + } else { + 0 + } + })), + AccountKey20 { + network: None, + key: fake_contract, + }, + ], + ); + + let asset = Asset { + id: AssetId(erc20_asset_location), + fun: Fungible(1_000), + }; + let destination = Location::new( + 0, + [AccountKey20 { + network: None, + key: BOB, + }], + ); + + let result = + ::deposit_asset(&asset, &destination, None); + + // Either Ok or Err — the important thing is no panic. + let _ = result; + }); +} + +#[test] +fn transactor_handles_relay_sovereign_account() { + ExtBuilder::default() + .with_balances(vec![(alice_account(), ONE_UNIT * 100)]) + .build() + .execute_with(|| { + use moonbase_runtime::xcm_config::{AssetTransactors, LocationToAccountId}; + use xcm_executor::traits::ConvertLocation; + + // The relay chain's sovereign account + let relay_location = Location::parent(); + let sovereign_account = LocationToAccountId::convert_location(&relay_location).unwrap(); + + // Give the sovereign account some funds + let _ = Balances::deposit_creating(&sovereign_account, ONE_UNIT * 10); + + let asset = Asset { + id: AssetId(native_asset_location()), + fun: Fungible(ONE_UNIT), + }; + + // Withdraw from relay sovereign account + let result = + ::withdraw_asset(&asset, &relay_location, None); + + assert!( + result.is_ok(), + "Should withdraw from relay sovereign account" + ); + }); +} diff --git a/runtime/moonbase/tests/xcm_config/weigher.rs b/runtime/moonbase/tests/xcm_config/weigher.rs new file mode 100644 index 00000000000..f07ccce88dd --- /dev/null +++ b/runtime/moonbase/tests/xcm_config/weigher.rs @@ -0,0 +1,154 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Tests for XcmWeigher configuration. +//! +//! The weigher calculates the weight of XCM messages based on the instructions +//! they contain. Moonbase uses WeightInfoBounds with custom XcmWeight implementation. + +use crate::xcm_common::*; +use moonbase_runtime::{xcm_config::XcmWeigher, RuntimeCall}; +use parity_scale_codec::Encode; +use sp_runtime::traits::Zero; +use sp_weights::Weight; +use xcm::latest::prelude::*; +use xcm_executor::traits::WeightBounds; + +#[test] +fn weigher_calculates_weight_for_simple_message() { + ExtBuilder::default().build().execute_with(|| { + let message: Xcm = Xcm(vec![ClearOrigin]); + + let weight = XcmWeigher::weight(&mut message.clone(), Weight::MAX); + + assert!(weight.is_ok(), "Should calculate weight for simple message"); + let w = weight.unwrap(); + assert!(!w.is_zero(), "Weight should be non-zero"); + }); +} + +#[test] +fn weigher_calculates_weight_for_transfer_message() { + ExtBuilder::default().build().execute_with(|| { + let message: Xcm = Xcm(vec![ + WithdrawAsset((Location::parent(), 1_000_000_000_000u128).into()), + BuyExecution { + fees: (Location::parent(), 1_000_000_000_000u128).into(), + weight_limit: WeightLimit::Unlimited, + }, + DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: ALICE, + }], + ), + }, + ]); + + let weight = XcmWeigher::weight(&mut message.clone(), Weight::MAX); + + assert!( + weight.is_ok(), + "Should calculate weight for transfer message" + ); + let w = weight.unwrap(); + assert!(w.ref_time() > 0, "Weight ref_time should be positive"); + }); +} + +#[test] +fn weigher_weight_increases_with_more_instructions() { + ExtBuilder::default().build().execute_with(|| { + let simple_message: Xcm = Xcm(vec![ClearOrigin]); + + let complex_message: Xcm = Xcm(vec![ + ClearOrigin, + ClearOrigin, + ClearOrigin, + ClearOrigin, + ClearOrigin, + ]); + + let simple_weight = XcmWeigher::weight(&mut simple_message.clone(), Weight::MAX).unwrap(); + let complex_weight = XcmWeigher::weight(&mut complex_message.clone(), Weight::MAX).unwrap(); + + assert!( + complex_weight.ref_time() > simple_weight.ref_time(), + "More instructions should result in higher weight" + ); + }); +} + +#[test] +fn weigher_respects_max_instructions() { + ExtBuilder::default().build().execute_with(|| { + // MaxInstructions is 100 in xcm_config + // Create a message with more than 100 instructions + let instructions: Vec> = (0..150).map(|_| ClearOrigin).collect(); + let message: Xcm = Xcm(instructions); + + let weight = XcmWeigher::weight(&mut message.clone(), Weight::MAX); + + // Should fail because it exceeds MaxInstructions + assert!( + weight.is_err(), + "Message exceeding MaxInstructions should fail weighing" + ); + }); +} + +#[test] +fn weigher_handles_transact_instruction() { + ExtBuilder::default().build().execute_with(|| { + // Transact instruction has variable weight based on the encoded call + let encoded_call = RuntimeCall::System(frame_system::Call::remark { + remark: vec![1, 2, 3], + }) + .encode(); + let message: Xcm = Xcm(vec![Transact { + origin_kind: OriginKind::Xcm, + call: encoded_call.into(), + fallback_max_weight: Some(Weight::from_parts(100_000_000, 10_000)), + }]); + + let weight = XcmWeigher::weight(&mut message.clone(), Weight::MAX); + + assert!( + weight.is_ok(), + "Should calculate weight for Transact instruction" + ); + }); +} + +#[test] +fn message_queue_heap_size_sufficient_for_xcm() { + ExtBuilder::default().build().execute_with(|| { + // MessageQueueHeapSize is used as MaxPageSize and HeapSize for pallet_message_queue. + // It must be large enough to hold any valid XCM message, including those + // received via HRMP. A minimum of 100KB ensures compatibility. + use moonbase_runtime::xcm_config::MessageQueueHeapSize; + + let heap_size = MessageQueueHeapSize::get(); + + assert!( + heap_size >= 100 * 1024, + "MessageQueueHeapSize ({heap_size}) should be at least 100KB for HRMP compatibility" + ); + }); +} diff --git a/runtime/moonbase/tests/xcm_config/xcm_common.rs b/runtime/moonbase/tests/xcm_config/xcm_common.rs new file mode 100644 index 00000000000..b3850f87853 --- /dev/null +++ b/runtime/moonbase/tests/xcm_config/xcm_common.rs @@ -0,0 +1,152 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Common test utilities for XCM configuration tests. + +#![allow(dead_code)] + +pub use crate::common::*; + +use moonbase_runtime::{currency::UNIT, xcm_config::XcmExecutorConfig, RuntimeCall}; +use parity_scale_codec::Encode; +use sp_weights::Weight; +use xcm::latest::prelude::*; +use xcm_executor::XcmExecutor; + +pub const ONE_DOT: u128 = 10_000_000_000; // DOT has 10 decimals +pub const ONE_UNIT: u128 = UNIT; + +/// Execute an XCM message and return the outcome. +/// +/// This uses the real Moonbase XcmExecutorConfig to test XCM behavior. +pub fn execute_xcm(origin: Location, message: Xcm) -> Outcome { + let hash = message.using_encoded(sp_io::hashing::blake2_256); + XcmExecutor::::prepare_and_execute( + origin, + message, + &mut hash.clone(), + Weight::MAX, + Weight::zero(), + ) +} + +/// Execute an XCM message with a weight limit and return the outcome. +pub fn execute_xcm_with_weight( + origin: Location, + message: Xcm, + weight_limit: Weight, +) -> Outcome { + let hash = message.using_encoded(sp_io::hashing::blake2_256); + XcmExecutor::::prepare_and_execute( + origin, + message, + &mut hash.clone(), + weight_limit, + Weight::zero(), + ) +} + +/// Execute an XCM message with pre-credited weight. +/// +/// `TakeWeightCredit` barrier passes when the message weight is within the +/// credited amount; use this to test that barrier path. +pub fn execute_xcm_with_credit( + origin: Location, + message: Xcm, + weight_credit: Weight, +) -> Outcome { + let hash = message.using_encoded(sp_io::hashing::blake2_256); + XcmExecutor::::prepare_and_execute( + origin, + message, + &mut hash.clone(), + Weight::MAX, + weight_credit, + ) +} + +/// Helper to check if an outcome is a barrier error. +/// +/// Barrier rejections can surface as either `Outcome::Error` or +/// `Outcome::Incomplete` (when the executor begins processing before the +/// barrier rejects at instruction index 0), so both variants are matched. +pub fn is_barrier_error(outcome: &Outcome) -> bool { + matches!( + outcome, + Outcome::Error(ref e) if e.error == XcmError::Barrier + ) || matches!( + outcome, + Outcome::Incomplete { ref error, .. } if error.error == XcmError::Barrier + ) +} + +/// Helper to check if execution completed successfully +pub fn is_complete(outcome: &Outcome) -> bool { + matches!(outcome, Outcome::Complete { .. }) +} + +/// Helper to check if execution is incomplete (partially executed) +pub fn is_incomplete(outcome: &Outcome) -> bool { + matches!(outcome, Outcome::Incomplete { .. }) +} + +/// Create a simple asset from location and amount +pub fn asset(location: Location, amount: u128) -> Asset { + Asset { + id: AssetId(location), + fun: Fungible(amount), + } +} + +/// Relay chain native asset (DOT) +pub fn relay_asset(amount: u128) -> Asset { + asset(Location::parent(), amount) +} + +/// Asset from a sibling parachain's native token +pub fn sibling_asset(para_id: u32, pallet_index: u8, amount: u128) -> Asset { + asset( + Location::new(1, [Parachain(para_id), PalletInstance(pallet_index)]), + amount, + ) +} + +/// Build a message with paid execution +pub fn paid_message(fees: Asset, instructions: Vec>) -> Xcm { + let mut all_instructions = vec![ + WithdrawAsset(fees.clone().into()), + BuyExecution { + fees, + weight_limit: WeightLimit::Unlimited, + }, + ]; + all_instructions.extend(instructions); + Xcm(all_instructions) +} + +/// Build a deposit asset instruction to an account +pub fn deposit_to_account(account: [u8; 20]) -> Instruction { + DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: account, + }], + ), + } +} diff --git a/runtime/moonbase/tests/xcm_emulator/asset_hub.rs b/runtime/moonbase/tests/xcm_emulator/asset_hub.rs new file mode 100644 index 00000000000..fa72f43a5d6 --- /dev/null +++ b/runtime/moonbase/tests/xcm_emulator/asset_hub.rs @@ -0,0 +1,306 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Asset Hub ↔ Moonbeam transfer tests using xcm-emulator. +//! +//! These tests exercise cross-chain transfers between the real +//! `asset-hub-westend-runtime` (para 1000) and the real `moonbase-runtime` +//! (para 2004), with Westend as relay. + +use crate::network::*; +use frame_support::{assert_ok, traits::fungible::Inspect}; +use sp_core::U256; +use xcm::latest::prelude::*; +use xcm_emulator::TestExt; + +// =========================================================================== +// Setup helpers +// =========================================================================== + +/// Register DOT on Moonbeam, open HRMP between Asset Hub and Moonbeam. +fn setup_asset_hub_and_moonbase() { + init_network(); + + moonbase_execute_with(|| register_dot_asset(DOT_ASSET_ID)); + + WestendRelay::::execute_with(|| { + open_hrmp_channels(ASSET_HUB_PARA_ID, MOONBASE_PARA_ID); + }); +} + +/// Fund ALITH on Moonbeam with DOT from the relay. +fn fund_moonbase_alith_with_dot(amount: u128) { + WestendRelay::::execute_with(|| { + assert_ok!( + westend_runtime::XcmPallet::transfer_assets_using_type_and_then( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE.clone()), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(MOONBASE_PARA_ID)] + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(amount), + }]))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedAssetId::from(AssetId(Location::here()))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: ALITH, + }], + ), + }]))), + WeightLimit::Unlimited, + ) + ); + }); +} + +// =========================================================================== +// Tests +// =========================================================================== + +/// Transfer DOT from the relay to Asset Hub, confirming the real +/// asset-hub-westend-runtime processes DMP correctly. +#[test] +fn transfer_dot_from_relay_to_asset_hub() { + init_network(); + + let recipient = sp_runtime::AccountId32::new([2u8; 32]); + + let balance_before = asset_hub_execute_with(|| { + >::balance(&recipient) + }); + + // Send DOT from relay to Asset Hub (DOT is the native token on both). + WestendRelay::::execute_with(|| { + assert_ok!(westend_runtime::XcmPallet::limited_teleport_assets( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE.clone()), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(ASSET_HUB_PARA_ID)], + ))), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [AccountId32 { + network: None, + id: recipient.clone().into(), + }], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(ONE_DOT * 10), + }]))), + 0, + WeightLimit::Unlimited, + )); + }); + + let balance_after = asset_hub_execute_with(|| { + >::balance(&recipient) + }); + assert!( + balance_after > balance_before, + "Asset Hub account should have received DOT: before={balance_before}, after={balance_after}" + ); +} + +/// Transfer DOT from the relay to both Asset Hub (teleport) and Moonbeam +/// (reserve), confirming both chains can hold DOT originated from the +/// same relay in the same network. +#[test] +fn relay_funds_both_asset_hub_and_moonbase() { + setup_asset_hub_and_moonbase(); + + let ah_recipient = sp_runtime::AccountId32::new([2u8; 32]); + + // Fund Asset Hub via teleport. + WestendRelay::::execute_with(|| { + assert_ok!(westend_runtime::XcmPallet::limited_teleport_assets( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE.clone()), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(ASSET_HUB_PARA_ID)], + ))), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [AccountId32 { + network: None, + id: ah_recipient.clone().into(), + }], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(ONE_DOT * 10), + }]))), + 0, + WeightLimit::Unlimited, + )); + }); + + // Fund Moonbeam via reserve. + fund_moonbase_alith_with_dot(ONE_DOT * 10); + + // Both chains should have DOT. + let ah_balance = asset_hub_execute_with(|| { + >::balance(&ah_recipient) + }); + assert!( + ah_balance > 0, + "Asset Hub should have DOT (got {ah_balance})" + ); + + let moonbase_balance = moonbase_execute_with(|| { + moonbase_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonbase_runtime::AccountId::from(ALITH), + ) + .unwrap_or_default() + }); + assert!( + moonbase_balance > U256::zero(), + "Moonbase should have DOT (got {moonbase_balance})" + ); +} + +/// Transfer a trust-backed asset (e.g. USDT) from Asset Hub to Moonbeam. +/// Asset Hub is the reserve for trust-backed assets, so this is a +/// reserve-backed transfer. +#[test] +fn transfer_trust_backed_asset_from_asset_hub_to_moonbase() { + setup_asset_hub_and_moonbase(); + + // Create and mint a trust-backed asset (id=1984, "USDT") on Asset Hub. + let asset_id: u32 = 1984; + let asset_owner = sp_runtime::AccountId32::new([1u8; 32]); + let mint_amount: u128 = 1_000_000_000; // 1000 USDT (6 decimals) + + asset_hub_execute_with(|| { + assert_ok!(asset_hub_westend_runtime::Assets::force_create( + asset_hub_westend_runtime::RuntimeOrigin::root(), + asset_id.into(), + asset_owner.clone().into(), + true, + 1_000, // min_balance + )); + assert_ok!(asset_hub_westend_runtime::Assets::mint( + asset_hub_westend_runtime::RuntimeOrigin::signed(asset_owner.clone()), + asset_id.into(), + asset_owner.clone().into(), + mint_amount, + )); + }); + + // Register this asset on Moonbeam as a foreign asset. + // From Moonbeam's perspective: ../Parachain(1000)/PalletInstance(50)/GeneralIndex(1984) + const USDT_FOREIGN_ID: u128 = 10; + moonbase_execute_with(|| { + let usdt_location = xcm::latest::Location::new( + 1, + [ + Parachain(ASSET_HUB_PARA_ID), + PalletInstance(50u8), // pallet_assets instance 1 + GeneralIndex(asset_id as u128), + ], + ); + + frame_support::assert_ok!(moonbase_runtime::EvmForeignAssets::create_foreign_asset( + moonbase_runtime::RuntimeOrigin::root(), + USDT_FOREIGN_ID, + usdt_location.clone(), + 6, // USDT decimals + b"USDT".to_vec().try_into().unwrap(), + b"Tether USD".to_vec().try_into().unwrap(), + )); + + frame_support::assert_ok!(moonbase_runtime::XcmWeightTrader::add_asset( + moonbase_runtime::RuntimeOrigin::root(), + usdt_location, + 10_000_000_000_000_000_000_000_000_000u128, + )); + }); + + // Also need DOT on Asset Hub sender for fees. Fund via relay teleport. + WestendRelay::::execute_with(|| { + assert_ok!(westend_runtime::XcmPallet::limited_teleport_assets( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE.clone()), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(ASSET_HUB_PARA_ID)], + ))), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [AccountId32 { + network: None, + id: asset_owner.clone().into(), + }], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(ONE_DOT * 100), + }]))), + 0, + WeightLimit::Unlimited, + )); + }); + + // Transfer USDT from Asset Hub to Moonbeam. + // Asset Hub is the reserve for this trust-backed asset. + let transfer_amount: u128 = 500_000_000; // 500 USDT + + asset_hub_execute_with(|| { + let usdt_on_ah = Location::new(0, [PalletInstance(50u8), GeneralIndex(asset_id as u128)]); + + assert_ok!(asset_hub_westend_runtime::PolkadotXcm::transfer_assets( + asset_hub_westend_runtime::RuntimeOrigin::signed(asset_owner.clone()), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(MOONBASE_PARA_ID)], + ))), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [AccountKey20 { + network: None, + key: ALITH, + }], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(usdt_on_ah), + fun: Fungible(transfer_amount), + }]))), + 0, + WeightLimit::Unlimited, + )); + }); + + // ALITH on Moonbeam should have received USDT as a foreign asset. + let alith_usdt = moonbase_execute_with(|| { + moonbase_runtime::EvmForeignAssets::balance( + USDT_FOREIGN_ID, + moonbase_runtime::AccountId::from(ALITH), + ) + .unwrap_or_default() + }); + assert!( + alith_usdt > U256::zero(), + "ALITH should have received USDT on Moonbeam (got {alith_usdt})" + ); +} diff --git a/runtime/moonbase/tests/xcm_emulator/main.rs b/runtime/moonbase/tests/xcm_emulator/main.rs new file mode 100644 index 00000000000..be1b1190245 --- /dev/null +++ b/runtime/moonbase/tests/xcm_emulator/main.rs @@ -0,0 +1,35 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! XCM Emulator Integration Tests (Level 2A) +//! +//! Uses the real `moonbase_runtime` connected to `westend_runtime` as relay +//! and a sibling `moonbase_runtime` instance. Tests exercise: +//! +//! - Transfers: relay→para, para→relay, para→para (DMP/UMP/XCMP) +//! - Fee collection: treasury receives execution fees, insufficient fees fail +//! - Transact: sovereign transact to relay +//! - HRMP: open, accept, close channels via `pallet_xcm_transactor` +//! - Account sufficiency: fresh accounts receive foreign assets + +#![cfg(test)] + +mod asset_hub; +mod network; +mod relay; +mod transact; +mod transfers; +mod versioning; diff --git a/runtime/moonbase/tests/xcm_emulator/network.rs b/runtime/moonbase/tests/xcm_emulator/network.rs new file mode 100644 index 00000000000..0706c027f1a --- /dev/null +++ b/runtime/moonbase/tests/xcm_emulator/network.rs @@ -0,0 +1,457 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Network declaration for xcm-emulator. +//! +//! Wires a Westend relay chain, the real Moonbase runtime (para 2004), +//! and a sibling Moonbase instance (para 2005) into a single test network. + +use crate::relay; + +use frame_support::traits::OnInitialize; +use nimbus_primitives::{NimbusId, NIMBUS_ENGINE_ID}; +use parity_scale_codec::Encode; +use sp_runtime::{Digest, DigestItem}; +use xcm_emulator::decl_test_networks; +use xcm_emulator::decl_test_parachains; +use xcm_emulator::decl_test_relay_chains; +use xcm_emulator::BlockProducer; +use xcm_emulator::TestExt; + +/// `BlockProducer` for Moonbase's Nimbus-based consensus. +/// +/// xcm-emulator defaults to an Aura-backed producer; Moonbase does not use +/// pallet-aura, so we plug in a Nimbus pre-runtime digest with a 6s block +/// interval (matching `moonbase_runtime::MILLISECS_PER_BLOCK`). +pub struct MoonbaseBlockProducer; + +impl BlockProducer for MoonbaseBlockProducer { + fn slot_duration() -> u64 { + moonbase_runtime::MILLISECS_PER_BLOCK + } + + fn pre_runtime_digest(_relay_block_number: u32) -> Digest { + let author = NimbusId::from(sp_core::sr25519::Public::from_raw([1u8; 32])); + Digest { + logs: vec![DigestItem::PreRuntime(NIMBUS_ENGINE_ID, author.encode())], + } + } +} + +pub const ASSET_HUB_PARA_ID: u32 = 1000; +pub const MOONBASE_PARA_ID: u32 = 2004; +pub const SIBLING_PARA_ID: u32 = 2005; + +// ---- Well-known test accounts (20-byte) ------------------------------------ +pub const ALITH: [u8; 20] = [1u8; 20]; +pub const BALTATHAR: [u8; 20] = [2u8; 20]; +// ---- Well-known relay accounts (32-byte) ----------------------------------- +pub const RELAY_ALICE: sp_runtime::AccountId32 = sp_runtime::AccountId32::new([1u8; 32]); + +// ---- Asset ID constants ---------------------------------------------------- +pub const DOT_ASSET_ID: u128 = 1; +pub const UNIT_ASSET_ID: u128 = 2; + +// ---- DOT constants --------------------------------------------------------- +pub const ONE_DOT: u128 = 10_000_000_000; // 10 decimals + +// --------------------------------------------------------------------------- +// Relay chain declaration (Westend runtime) +// --------------------------------------------------------------------------- +decl_test_relay_chains! { + #[api_version(15)] + pub struct WestendRelay { + genesis = relay::relay_genesis(), + on_init = (), + runtime = westend_runtime, + core = { + SovereignAccountOf: westend_runtime::xcm_config::LocationConverter, + }, + pallets = { + XcmPallet: westend_runtime::XcmPallet, + Balances: westend_runtime::Balances, + Hrmp: westend_runtime::Hrmp, + Utility: westend_runtime::Utility, + } + } +} + +// --------------------------------------------------------------------------- +// Moonbase parachain declaration (para 2004) +// --------------------------------------------------------------------------- +decl_test_parachains! { + pub struct MoonbasePara { + genesis = moonbase_genesis(MOONBASE_PARA_ID), + on_init = { + crate::network::satisfy_moonbase_inherents(); + }, + runtime = moonbase_runtime, + core = { + XcmpMessageHandler: moonbase_runtime::XcmpQueue, + LocationToAccountId: moonbase_runtime::xcm_config::LocationToAccountId, + ParachainInfo: moonbase_runtime::ParachainInfo, + MessageOrigin: cumulus_primitives_core::AggregateMessageOrigin, + BlockProducer: crate::network::MoonbaseBlockProducer, + }, + pallets = { + PolkadotXcm: moonbase_runtime::PolkadotXcm, + Balances: moonbase_runtime::Balances, + EvmForeignAssets: moonbase_runtime::EvmForeignAssets, + XcmWeightTrader: moonbase_runtime::XcmWeightTrader, + XcmTransactor: moonbase_runtime::XcmTransactor, + Treasury: moonbase_runtime::Treasury, + EthereumXcm: moonbase_runtime::EthereumXcm, + Proxy: moonbase_runtime::Proxy, + EVM: moonbase_runtime::EVM, + } + } +} + +// --------------------------------------------------------------------------- +// Sibling parachain declaration (para 2005) — another Moonbase instance +// --------------------------------------------------------------------------- +decl_test_parachains! { + pub struct SiblingPara { + genesis = moonbase_genesis(SIBLING_PARA_ID), + on_init = { + crate::network::satisfy_moonbase_inherents(); + }, + runtime = moonbase_runtime, + core = { + XcmpMessageHandler: moonbase_runtime::XcmpQueue, + LocationToAccountId: moonbase_runtime::xcm_config::LocationToAccountId, + ParachainInfo: moonbase_runtime::ParachainInfo, + MessageOrigin: cumulus_primitives_core::AggregateMessageOrigin, + BlockProducer: crate::network::MoonbaseBlockProducer, + }, + pallets = { + PolkadotXcm: moonbase_runtime::PolkadotXcm, + Balances: moonbase_runtime::Balances, + EvmForeignAssets: moonbase_runtime::EvmForeignAssets, + XcmWeightTrader: moonbase_runtime::XcmWeightTrader, + XcmTransactor: moonbase_runtime::XcmTransactor, + Treasury: moonbase_runtime::Treasury, + EthereumXcm: moonbase_runtime::EthereumXcm, + Proxy: moonbase_runtime::Proxy, + EVM: moonbase_runtime::EVM, + } + } +} + +// --------------------------------------------------------------------------- +// Asset Hub Westend declaration (para 1000, real asset-hub-westend-runtime) +// --------------------------------------------------------------------------- +decl_test_parachains! { + pub struct AssetHubPara { + genesis = asset_hub_genesis(), + on_init = { + asset_hub_westend_runtime::AuraExt::on_initialize(1); + }, + runtime = asset_hub_westend_runtime, + core = { + XcmpMessageHandler: asset_hub_westend_runtime::XcmpQueue, + LocationToAccountId: asset_hub_westend_runtime::xcm_config::LocationToAccountId, + ParachainInfo: asset_hub_westend_runtime::ParachainInfo, + MessageOrigin: cumulus_primitives_core::AggregateMessageOrigin, + }, + pallets = { + PolkadotXcm: asset_hub_westend_runtime::PolkadotXcm, + Balances: asset_hub_westend_runtime::Balances, + Assets: asset_hub_westend_runtime::Assets, + ForeignAssets: asset_hub_westend_runtime::ForeignAssets, + } + } +} + +// --------------------------------------------------------------------------- +// Network declaration +// --------------------------------------------------------------------------- +decl_test_networks! { + pub struct PolkadotMoonbeamNet { + relay_chain = WestendRelay, + parachains = vec![ + AssetHubPara, + MoonbasePara, + SiblingPara, + ], + bridge = () + } +} + +// =========================================================================== +// Helpers +// =========================================================================== + +/// Execute a closure on the Moonbase parachain (para 2004), automatically +/// satisfying mandatory inherent checks. +pub fn moonbase_execute_with(f: impl FnOnce() -> R) -> R { + MoonbasePara::::execute_with(|| { + satisfy_moonbase_inherents(); + f() + }) +} + +/// Execute a closure on the Sibling parachain (para 2005), automatically +/// satisfying mandatory inherent checks. +pub fn sibling_execute_with(f: impl FnOnce() -> R) -> R { + SiblingPara::::execute_with(|| { + satisfy_moonbase_inherents(); + f() + }) +} + +/// Execute a closure on Asset Hub (para 1000). +pub fn asset_hub_execute_with(f: impl FnOnce() -> R) -> R { + AssetHubPara::::execute_with(f) +} + +/// Patch storage to satisfy Moonbase's mandatory inherent checks. +/// Called automatically by [`moonbase_execute_with`] / [`sibling_execute_with`]. +pub(crate) fn satisfy_moonbase_inherents() { + pallet_author_inherent::Author::::put( + moonbase_runtime::AccountId::from([1u8; 20]), + ); + pallet_author_inherent::InherentIncluded::::put(true); + + frame_support::storage::unhashed::put( + &frame_support::storage::storage_prefix(b"Randomness", b"InherentIncluded"), + &(), + ); + frame_support::storage::unhashed::kill(&frame_support::storage::storage_prefix( + b"Randomness", + b"NotFirstBlock", + )); +} + +/// Initialise network and clear `NotFirstBlock` on all parachains. +pub fn init_network() { + // Trigger `Parachain::init()` on every chain by executing on relay. + WestendRelay::::execute_with(|| {}); + + // Clear NotFirstBlock so VRF verification is skipped in subsequent blocks. + MoonbasePara::::ext_wrapper(|| { + frame_support::storage::unhashed::kill(&frame_support::storage::storage_prefix( + b"Randomness", + b"NotFirstBlock", + )); + }); + SiblingPara::::ext_wrapper(|| { + frame_support::storage::unhashed::kill(&frame_support::storage::storage_prefix( + b"Randomness", + b"NotFirstBlock", + )); + }); +} + +/// Register DOT as a foreign asset on a Moonbeam-runtime chain and configure +/// its price in the XCM weight trader. Call inside `moonbase_execute_with` or +/// `sibling_execute_with`. +pub fn register_dot_asset(asset_id: u128) { + let dot_location = xcm::latest::Location::parent(); + + frame_support::assert_ok!(moonbase_runtime::EvmForeignAssets::create_foreign_asset( + moonbase_runtime::RuntimeOrigin::root(), + asset_id, + dot_location.clone(), + 10, + b"DOT".to_vec().try_into().unwrap(), + b"Polkadot".to_vec().try_into().unwrap(), + )); + + // relative_price large enough so that 10 DOT covers XCM execution fees. + frame_support::assert_ok!(moonbase_runtime::XcmWeightTrader::add_asset( + moonbase_runtime::RuntimeOrigin::root(), + dot_location, + 10_000_000_000_000_000_000_000_000_000u128, // 10^28 + )); +} + +/// Configure `pallet_xcm_transactor` relay indices for Westend. +/// Call inside `moonbase_execute_with` or `sibling_execute_with`. +pub fn set_westend_relay_indices() { + use frame_support::traits::PalletInfoAccess; + use pallet_xcm_transactor::relay_indices::RelayChainIndices; + + // Validate pallet indices against the Westend runtime so we fail fast if + // they drift after a relay upgrade. + let staking_idx = westend_runtime::Staking::index() as u8; + let utility_idx = westend_runtime::Utility::index() as u8; + let hrmp_idx = westend_runtime::Hrmp::index() as u8; + + assert_eq!(staking_idx, 6u8, "Westend Staking pallet index has changed"); + assert_eq!( + utility_idx, 16u8, + "Westend Utility pallet index has changed" + ); + assert_eq!(hrmp_idx, 51u8, "Westend Hrmp pallet index has changed"); + + let indices = RelayChainIndices { + staking: staking_idx, + utility: utility_idx, + hrmp: hrmp_idx, + // Call indices within staking pallet: + bond: 0u8, + bond_extra: 1u8, + unbond: 2u8, + withdraw_unbonded: 3u8, + validate: 4u8, + nominate: 5u8, + chill: 6u8, + set_payee: 7u8, + set_controller: 8u8, + rebond: 19u8, + // Utility::as_derivative + as_derivative: 1u8, + // HRMP call indices: + init_open_channel: 0u8, + accept_open_channel: 1u8, + close_channel: 2u8, + cancel_open_request: 6u8, + }; + + pallet_xcm_transactor::RelayIndices::::put(indices); +} + +/// Open HRMP channels between two parachains on the relay. +/// Must be called inside `WestendRelay::execute_with`. +pub fn open_hrmp_channels(sender: u32, recipient: u32) { + use frame_support::assert_ok; + + assert_ok!(westend_runtime::Hrmp::force_open_hrmp_channel( + westend_runtime::RuntimeOrigin::root(), + sender.into(), + recipient.into(), + 8, // max_capacity + 1024, // max_message_size + )); + assert_ok!(westend_runtime::Hrmp::force_open_hrmp_channel( + westend_runtime::RuntimeOrigin::root(), + recipient.into(), + sender.into(), + 8, + 1024, + )); + assert_ok!(westend_runtime::Hrmp::force_process_hrmp_open( + westend_runtime::RuntimeOrigin::root(), + 2, + )); +} + +// --------------------------------------------------------------------------- +// Moonbeam genesis helper +// --------------------------------------------------------------------------- + +fn asset_hub_genesis() -> sp_core::storage::Storage { + use sp_runtime::BuildStorage; + + let endowment: u128 = 1_000_000_000_000_000; // 1 000 WND (12 decimals) + + let mut t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + + parachain_info::GenesisConfig:: { + parachain_id: ASSET_HUB_PARA_ID.into(), + _config: Default::default(), + } + .assimilate_storage(&mut t) + .unwrap(); + + // Pre-fund Asset Hub's XCM checking account so relay→AH teleports of the + // native (WND) asset don't fail with `NotWithdrawable`. Asset Hub tracks + // teleported-out WND in this account; in a fresh test genesis it starts + // at zero, so an incoming teleport cannot withdraw against it. + let checking_account: sp_runtime::AccountId32 = + sp_runtime::traits::AccountIdConversion::into_account_truncating(&frame_support::PalletId( + *b"py/xcmch", + )); + + pallet_balances::GenesisConfig:: { + balances: vec![ + (sp_runtime::AccountId32::new([1u8; 32]), endowment), + (sp_runtime::AccountId32::new([2u8; 32]), endowment), + (checking_account, endowment), + ], + ..Default::default() + } + .assimilate_storage(&mut t) + .unwrap(); + + // Populate at least one Aura authority. Asset Hub runs on pallet-aura + // and `FindAuthor` panics on `slot % authorities_len()` if it's empty. + xcm_emulator::pallet_aura::GenesisConfig:: { + authorities: vec![parachains_common::AuraId::from( + sp_core::sr25519::Public::from_raw([1u8; 32]), + )], + } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_xcm::GenesisConfig:: { + safe_xcm_version: Some(xcm::latest::VERSION), + ..Default::default() + } + .assimilate_storage(&mut t) + .unwrap(); + + t +} + +fn moonbase_genesis(para_id: u32) -> sp_core::storage::Storage { + use moonbase_runtime::{currency::UNIT, AccountId, Runtime}; + use sp_runtime::BuildStorage; + + let mut t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + + parachain_info::GenesisConfig:: { + parachain_id: para_id.into(), + _config: Default::default(), + } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_balances::GenesisConfig:: { + balances: vec![ + (AccountId::from(ALITH), UNIT * 10_000), + (AccountId::from(BALTATHAR), UNIT * 10_000), + ], + dev_accounts: None, + } + .assimilate_storage(&mut t) + .unwrap(); + + // Map the NimbusId emitted by `MoonbaseBlockProducer` to ALITH so + // `pallet_author_inherent::FindAuthor` can resolve a block author. + pallet_author_mapping::GenesisConfig:: { + mappings: vec![( + NimbusId::from(sp_core::sr25519::Public::from_raw([1u8; 32])), + AccountId::from(ALITH), + )], + } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_xcm::GenesisConfig:: { + safe_xcm_version: Some(xcm::latest::VERSION), + ..Default::default() + } + .assimilate_storage(&mut t) + .unwrap(); + + t +} diff --git a/runtime/moonbase/tests/xcm_emulator/relay.rs b/runtime/moonbase/tests/xcm_emulator/relay.rs new file mode 100644 index 00000000000..4da15fed146 --- /dev/null +++ b/runtime/moonbase/tests/xcm_emulator/relay.rs @@ -0,0 +1,94 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Relay chain genesis for xcm-emulator tests. +//! +//! Uses the full `westend_runtime` so we get real DMP routing, HRMP, and +//! the `ParachainHost` runtime API the emulator requires. + +pub use westend_runtime; + +use parity_scale_codec::Encode; +use sp_core::storage::Storage; +use sp_runtime::{traits::AccountIdConversion, AccountId32, BuildStorage}; + +use crate::network::{ASSET_HUB_PARA_ID, MOONBASE_PARA_ID, SIBLING_PARA_ID}; + +/// Build relay `Storage` with both parachains registered and funded. +pub fn relay_genesis() -> Storage { + let asset_hub_sovereign: AccountId32 = + polkadot_parachain::primitives::Id::from(ASSET_HUB_PARA_ID).into_account_truncating(); + let moonbase_sovereign: AccountId32 = + polkadot_parachain::primitives::Id::from(MOONBASE_PARA_ID).into_account_truncating(); + let sibling_sovereign: AccountId32 = + polkadot_parachain::primitives::Id::from(SIBLING_PARA_ID).into_account_truncating(); + let endowment: u128 = 1_000_000_000_000_000; // 1 000 WND (12 decimals) + + let mut host_config = polkadot_runtime_parachains::configuration::HostConfiguration::default(); + host_config.max_downward_message_size = 1 << 20; + host_config.max_upward_message_size = 1 << 16; + host_config.max_upward_queue_count = 100; + host_config.max_upward_message_num_per_candidate = 10; + host_config.hrmp_max_message_num_per_candidate = 10; + host_config.hrmp_channel_max_capacity = 8; + host_config.hrmp_channel_max_total_size = 8 * 1024; + host_config.hrmp_channel_max_message_size = 1024; + host_config.hrmp_max_parachain_outbound_channels = 10; + host_config.hrmp_max_parachain_inbound_channels = 10; + + let genesis_config = westend_runtime::RuntimeGenesisConfig { + balances: westend_runtime::BalancesConfig { + balances: vec![ + (AccountId32::new([1u8; 32]), endowment), + (AccountId32::new([2u8; 32]), endowment), + (asset_hub_sovereign, endowment), + (moonbase_sovereign, endowment), + (sibling_sovereign, endowment), + ], + ..Default::default() + }, + configuration: westend_runtime::ConfigurationConfig { + config: host_config, + }, + ..Default::default() + }; + + let mut storage = genesis_config + .build_storage() + .expect("Should build relay genesis storage"); + + // Register both parachains so DMP and HRMP consider them valid. + use frame_support::storage::generator::StorageMap; + for para_id in [ASSET_HUB_PARA_ID, MOONBASE_PARA_ID, SIBLING_PARA_ID] { + let pid = polkadot_parachain::primitives::Id::from(para_id); + let head_data = polkadot_parachain::primitives::HeadData(vec![0u8; 32]); + let key = polkadot_runtime_parachains::paras::Heads::::storage_map_final_key(pid); + storage.top.insert(key, head_data.encode()); + + // Also register the ParaLifecycle as Parachain so HRMP considers them valid. + // ParaLifecycles is pub(super) so we cannot use storage_map_final_key(); + // reconstruct the key manually (Twox64Concat hasher on ParaId). + let prefix = frame_support::storage::storage_prefix(b"Paras", b"ParaLifecycles"); + let encoded_id = pid.encode(); + let mut full_key = prefix.to_vec(); + full_key.extend(&sp_io::hashing::twox_64(&encoded_id)); + full_key.extend(&encoded_id); + let lifecycle = polkadot_runtime_parachains::paras::ParaLifecycle::Parachain; + storage.top.insert(full_key, lifecycle.encode()); + } + + storage +} diff --git a/runtime/moonbase/tests/xcm_emulator/transact.rs b/runtime/moonbase/tests/xcm_emulator/transact.rs new file mode 100644 index 00000000000..2f043fc4426 --- /dev/null +++ b/runtime/moonbase/tests/xcm_emulator/transact.rs @@ -0,0 +1,1201 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbase. + +// Moonbase 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. + +// Moonbase 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 Moonbase. If not, see . + +//! XcmTransactor tests using the **real** Moonbase runtime against Westend relay. +//! +//! Covers: +//! - transact_through_sovereign (relay) — basic, fee_payer=None, custom fee/weight, refund +//! - transact_through_derivative (relay) — basic, custom fee/weight, refund +//! - transact_through_signed (relay) — basic, custom fee/weight, refund +//! - HRMP channel management (init/accept/close) + +use crate::network::*; +use frame_support::{ + assert_ok, + traits::fungible::{Inspect, Mutate}, +}; +use pallet_xcm_transactor::{Currency, CurrencyPayment, HrmpOperation, TransactWeights}; +use parity_scale_codec::Encode; +use sp_core::U256; +use xcm::latest::prelude::*; +use xcm_emulator::{RelayChain, TestExt}; +use xcm_executor::traits::ConvertLocation; + +// =========================================================================== +// Setup +// =========================================================================== + +fn setup_transactor() { + init_network(); + + moonbase_execute_with(|| { + register_dot_asset(DOT_ASSET_ID); + set_westend_relay_indices(); + + // Configure transact info for the relay destination. + assert_ok!(moonbase_runtime::XcmTransactor::set_transact_info( + moonbase_runtime::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::from(Location::parent())), + 3_000u64.into(), // extra_weight (relay charges per instruction) + 20_000_000_000u64.into(), // max_weight + // 4 instructions in transact_through_signed + Some(4_000u64.into()), + )); + }); + + // Fund Moonbase's sovereign on relay so it can pay fees for UMP transacts. + WestendRelay::::execute_with(|| { + // The sovereign is already funded via relay genesis (endowment). + }); +} + +/// Encode a `system::remark_with_event` call for the Westend relay. +fn relay_remark_call() -> Vec { + westend_runtime::RuntimeCall::System( + frame_system::Call::::remark_with_event { + remark: b"hello from Moonbase".to_vec(), + }, + ) + .encode() +} + +/// Derive the relay account for a signed XCM origin from a parachain user. +/// The XCM `DescendOrigin(AccountKey20(key))` shifts the origin to +/// `Parachain(para_id)/AccountKey20(key)`, which the relay's `LocationConverter` +/// hashes into a 32-byte account. +fn relay_derived_account(para_id: u32, key: [u8; 20]) -> sp_runtime::AccountId32 { + let location = Location::new(0, [Parachain(para_id), AccountKey20 { network: None, key }]); + westend_runtime::xcm_config::LocationConverter::convert_location(&location) + .expect("Should derive relay account from parachain signed origin") +} + +/// Assert that the relay processed a UMP message and emitted a Remarked event. +fn assert_relay_remark_executed() { + WestendRelay::::execute_with(|| { + let events = westend_runtime::System::events(); + + let was_processed = events.iter().any(|e| { + matches!( + &e.event, + westend_runtime::RuntimeEvent::MessageQueue( + pallet_message_queue::Event::Processed { success: true, .. } + ) + ) + }); + assert!( + was_processed, + "Relay should have successfully processed the UMP transact" + ); + + let has_remark = events.iter().any(|e| { + matches!( + &e.event, + westend_runtime::RuntimeEvent::System(frame_system::Event::Remarked { .. }) + ) + }); + assert!(has_remark, "Relay should have emitted a Remarked event"); + }); +} + +/// Send DOT from relay to Moonbase ALITH. +fn fund_moonbase_alith_with_dot(amount: u128) { + WestendRelay::::execute_with(|| { + assert_ok!( + westend_runtime::XcmPallet::transfer_assets_using_type_and_then( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(MOONBASE_PARA_ID)] + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(amount), + }]))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedAssetId::from(AssetId(Location::here()))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: ALITH + }], + ), + }]))), + WeightLimit::Unlimited, + ) + ); + }); +} + +// =========================================================================== +// Transact through sovereign (para → relay) +// =========================================================================== + +#[test] +fn transact_through_sovereign_to_relay() { + setup_transactor(); + fund_moonbase_alith_with_dot(ONE_DOT * 1000); + + // Check the sovereign account balance on relay before transact. + let sovereign = WestendRelay::::execute_with(|| { + WestendRelay::::sovereign_account_id_of(Location::new( + 0, + [Parachain(MOONBASE_PARA_ID)], + )) + }); + let sovereign_before = WestendRelay::::execute_with(|| { + >::balance(&sovereign) + }); + assert!( + sovereign_before > 0, + "Sovereign should be funded from genesis" + ); + + moonbase_execute_with(|| { + assert_ok!(moonbase_runtime::XcmTransactor::transact_through_sovereign( + moonbase_runtime::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::from(Location::parent())), + Some(moonbase_runtime::AccountId::from(ALITH)), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent() + ))), + fee_amount: Some(ONE_DOT), // explicit fee + }, + relay_remark_call(), + OriginKind::SovereignAccount, + TransactWeights { + transact_required_weight_at_most: 1_000_000_000u64.into(), + overall_weight: Some(Limited(2_000_000_000u64.into())), + }, + false, + )); + }); + + assert_relay_remark_executed(); + + // Verify the sovereign paid fees for the XCM execution. + let sovereign_after = WestendRelay::::execute_with(|| { + >::balance(&sovereign) + }); + assert!( + sovereign_after <= sovereign_before, + "Sovereign should have spent DOT: before={sovereign_before}, after={sovereign_after}" + ); +} + +// =========================================================================== +// HRMP: open and close channels via XcmTransactor +// =========================================================================== + +#[test] +fn hrmp_init_accept_close_via_xcm_transactor() { + init_network(); + + moonbase_execute_with(|| { + register_dot_asset(DOT_ASSET_ID); + set_westend_relay_indices(); + }); + sibling_execute_with(|| { + register_dot_asset(DOT_ASSET_ID); + set_westend_relay_indices(); + }); + + use pallet_xcm_transactor::{HrmpInitParams, HrmpOperation}; + + // Step 1: Moonbase requests to open channel to sibling. + moonbase_execute_with(|| { + assert_ok!(moonbase_runtime::XcmTransactor::hrmp_manage( + moonbase_runtime::RuntimeOrigin::root(), + HrmpOperation::InitOpen(HrmpInitParams { + para_id: SIBLING_PARA_ID.into(), + proposed_max_capacity: 8, + proposed_max_message_size: 1024, + }), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent() + ))), + fee_amount: Some(ONE_DOT * 100), + }, + TransactWeights { + transact_required_weight_at_most: 5_000_000_000u64.into(), + overall_weight: Some(Limited(10_000_000_000u64.into())), + }, + )); + }); + + // Verify the open-channel request arrived on relay. + WestendRelay::::execute_with(|| { + let events = westend_runtime::System::events(); + let has_open_request = events.iter().any(|e| { + matches!( + &e.event, + westend_runtime::RuntimeEvent::Hrmp( + polkadot_runtime_parachains::hrmp::Event::OpenChannelRequested { .. } + ) + ) + }); + assert!( + has_open_request, + "Relay should have emitted OpenChannelRequested" + ); + }); + + // Step 2: Sibling accepts the channel. + sibling_execute_with(|| { + assert_ok!(moonbase_runtime::XcmTransactor::hrmp_manage( + moonbase_runtime::RuntimeOrigin::root(), + HrmpOperation::Accept { + para_id: MOONBASE_PARA_ID.into(), + }, + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent() + ))), + fee_amount: Some(ONE_DOT * 100), + }, + TransactWeights { + transact_required_weight_at_most: 5_000_000_000u64.into(), + overall_weight: Some(Limited(10_000_000_000u64.into())), + }, + )); + }); + + WestendRelay::::execute_with(|| { + let events = westend_runtime::System::events(); + let has_accept = events.iter().any(|e| { + matches!( + &e.event, + westend_runtime::RuntimeEvent::Hrmp( + polkadot_runtime_parachains::hrmp::Event::OpenChannelAccepted { .. } + ) + ) + }); + assert!(has_accept, "Relay should have emitted OpenChannelAccepted"); + }); + + // Step 3: Process the pending open requests and verify the channel is established. + WestendRelay::::execute_with(|| { + assert_ok!(westend_runtime::Hrmp::force_process_hrmp_open( + westend_runtime::RuntimeOrigin::root(), + 1, + )); + + use polkadot_runtime_parachains::hrmp; + let channel = + hrmp::HrmpChannels::::get(xcm_emulator::HrmpChannelId { + sender: MOONBASE_PARA_ID.into(), + recipient: SIBLING_PARA_ID.into(), + }); + assert!( + channel.is_some(), + "HRMP channel Moonbase → Sibling should be established" + ); + }); +} + +// =========================================================================== +// HRMP: close channel via XcmTransactor +// =========================================================================== + +#[test] +fn hrmp_close_via_xcm_transactor() { + init_network(); + + moonbase_execute_with(|| { + register_dot_asset(DOT_ASSET_ID); + set_westend_relay_indices(); + }); + + // Force-open a channel so we can close it. + WestendRelay::::execute_with(|| { + open_hrmp_channels(MOONBASE_PARA_ID, SIBLING_PARA_ID); + }); + + // Close the channel from Moonbase side via XcmTransactor. + moonbase_execute_with(|| { + assert_ok!(moonbase_runtime::XcmTransactor::hrmp_manage( + moonbase_runtime::RuntimeOrigin::root(), + HrmpOperation::Close(xcm_emulator::HrmpChannelId { + sender: MOONBASE_PARA_ID.into(), + recipient: SIBLING_PARA_ID.into(), + }), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent(), + ))), + fee_amount: Some(ONE_DOT * 100), + }, + TransactWeights { + transact_required_weight_at_most: 5_000_000_000u64.into(), + overall_weight: Some(Limited(10_000_000_000u64.into())), + }, + )); + }); + + // Verify the close event on relay. + WestendRelay::::execute_with(|| { + let events = westend_runtime::System::events(); + let has_close = events.iter().any(|e| { + matches!( + &e.event, + westend_runtime::RuntimeEvent::Hrmp( + polkadot_runtime_parachains::hrmp::Event::ChannelClosed { .. } + ) + ) + }); + assert!(has_close, "Relay should have emitted ChannelClosed"); + }); +} + +// =========================================================================== +// Transact through sovereign: fee_payer = None +// =========================================================================== + +#[test] +fn transact_through_sovereign_fee_payer_none() { + setup_transactor(); + + // With fee_payer = None, no local withdraw happens — only the sovereign on + // relay pays. The sovereign must be funded from genesis. + moonbase_execute_with(|| { + assert_ok!(moonbase_runtime::XcmTransactor::transact_through_sovereign( + moonbase_runtime::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::from(Location::parent())), + None, // no fee payer — no local withdraw + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent(), + ))), + fee_amount: Some(ONE_DOT), + }, + relay_remark_call(), + OriginKind::SovereignAccount, + TransactWeights { + transact_required_weight_at_most: 1_000_000_000u64.into(), + overall_weight: Some(Limited(2_000_000_000u64.into())), + }, + false, + )); + }); + + assert_relay_remark_executed(); +} + +// =========================================================================== +// Transact through sovereign: custom fee & weight (no refund) +// =========================================================================== + +#[test] +fn transact_through_sovereign_custom_fee_weight() { + setup_transactor(); + fund_moonbase_alith_with_dot(ONE_DOT * 1000); + + moonbase_execute_with(|| { + assert_ok!(moonbase_runtime::XcmTransactor::transact_through_sovereign( + moonbase_runtime::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::from(Location::parent())), + Some(moonbase_runtime::AccountId::from(ALITH)), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent(), + ))), + fee_amount: Some(ONE_DOT * 5), // explicit larger fee + }, + relay_remark_call(), + OriginKind::SovereignAccount, + TransactWeights { + transact_required_weight_at_most: 2_000_000_000u64.into(), + overall_weight: Some(Limited(4_000_000_000u64.into())), + }, + false, + )); + }); + + assert_relay_remark_executed(); +} + +// =========================================================================== +// Transact through sovereign: custom fee, weight & refund +// =========================================================================== + +#[test] +fn transact_through_sovereign_custom_fee_weight_refund() { + setup_transactor(); + fund_moonbase_alith_with_dot(ONE_DOT * 1000); + + let sovereign_before = WestendRelay::::execute_with(|| { + let sovereign = WestendRelay::::sovereign_account_id_of( + Location::new(0, [Parachain(MOONBASE_PARA_ID)]), + ); + >::balance(&sovereign) + }); + + moonbase_execute_with(|| { + assert_ok!(moonbase_runtime::XcmTransactor::transact_through_sovereign( + moonbase_runtime::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::from(Location::parent())), + Some(moonbase_runtime::AccountId::from(ALITH)), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent(), + ))), + fee_amount: Some(ONE_DOT * 10), // overpay to test refund + }, + relay_remark_call(), + OriginKind::SovereignAccount, + TransactWeights { + transact_required_weight_at_most: 2_000_000_000u64.into(), + overall_weight: Some(Limited(4_000_000_000u64.into())), + }, + true, // refund = true + )); + }); + + assert_relay_remark_executed(); + + // With refund=true, leftover fees are deposited back to the sovereign. + // The sovereign should have lost less than the full 10 DOT fee. + let sovereign_after = WestendRelay::::execute_with(|| { + let sovereign = WestendRelay::::sovereign_account_id_of( + Location::new(0, [Parachain(MOONBASE_PARA_ID)]), + ); + >::balance(&sovereign) + }); + let fee_spent = sovereign_before.saturating_sub(sovereign_after); + assert!( + fee_spent < ONE_DOT * 10, + "With refund, sovereign should spend less than the full fee: spent={fee_spent}" + ); +} + +// =========================================================================== +// Transact through signed (para → relay) +// =========================================================================== + +#[test] +fn transact_through_signed_to_relay() { + setup_transactor(); + fund_moonbase_alith_with_dot(ONE_DOT * 1000); + + let derived_account = relay_derived_account(MOONBASE_PARA_ID, ALITH); + + // Fund the derived account on relay so it can pay XCM fees. + WestendRelay::::execute_with(|| { + assert_ok!(westend_runtime::Balances::transfer_allow_death( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE), + derived_account.clone().into(), + ONE_DOT * 100, + )); + }); + + moonbase_execute_with(|| { + assert_ok!(moonbase_runtime::XcmTransactor::transact_through_signed( + moonbase_runtime::RuntimeOrigin::signed(moonbase_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::parent())), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent(), + ))), + fee_amount: Some(ONE_DOT * 10), + }, + relay_remark_call(), + TransactWeights { + transact_required_weight_at_most: 1_000_000_000u64.into(), + overall_weight: Some(Limited(4_000_000_000u64.into())), + }, + false, + )); + }); + + assert_relay_remark_executed(); +} + +// =========================================================================== +// Transact through signed: custom fee & weight +// =========================================================================== + +#[test] +fn transact_through_signed_custom_fee_weight() { + setup_transactor(); + fund_moonbase_alith_with_dot(ONE_DOT * 1000); + + let derived_account = relay_derived_account(MOONBASE_PARA_ID, ALITH); + + WestendRelay::::execute_with(|| { + assert_ok!(westend_runtime::Balances::transfer_allow_death( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE), + derived_account.clone().into(), + ONE_DOT * 100, + )); + }); + + moonbase_execute_with(|| { + assert_ok!(moonbase_runtime::XcmTransactor::transact_through_signed( + moonbase_runtime::RuntimeOrigin::signed(moonbase_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::parent())), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent(), + ))), + fee_amount: Some(ONE_DOT * 5), + }, + relay_remark_call(), + TransactWeights { + transact_required_weight_at_most: 2_000_000_000u64.into(), + overall_weight: Some(Limited(6_000_000_000u64.into())), + }, + false, + )); + }); + + assert_relay_remark_executed(); +} + +// =========================================================================== +// Transact through signed: custom fee, weight & refund +// =========================================================================== + +#[test] +fn transact_through_signed_custom_fee_weight_refund() { + setup_transactor(); + fund_moonbase_alith_with_dot(ONE_DOT * 1000); + + let derived_account = relay_derived_account(MOONBASE_PARA_ID, ALITH); + + WestendRelay::::execute_with(|| { + assert_ok!(westend_runtime::Balances::transfer_allow_death( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE), + derived_account.clone().into(), + ONE_DOT * 100, + )); + }); + + let derived_before = WestendRelay::::execute_with(|| { + >::balance(&derived_account) + }); + + moonbase_execute_with(|| { + assert_ok!(moonbase_runtime::XcmTransactor::transact_through_signed( + moonbase_runtime::RuntimeOrigin::signed(moonbase_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::parent())), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent(), + ))), + fee_amount: Some(ONE_DOT * 20), // overpay + }, + relay_remark_call(), + TransactWeights { + transact_required_weight_at_most: 2_000_000_000u64.into(), + overall_weight: Some(Limited(6_000_000_000u64.into())), + }, + true, // refund = true + )); + }); + + assert_relay_remark_executed(); + + // With refund, the derived account should get surplus back. + let derived_after = WestendRelay::::execute_with(|| { + >::balance(&derived_account) + }); + let fee_spent = derived_before.saturating_sub(derived_after); + assert!( + fee_spent < ONE_DOT * 20, + "With refund, derived account should spend less than the full fee: spent={fee_spent}" + ); +} + +// =========================================================================== +// Transact through derivative +// =========================================================================== + +/// Setup for derivative transact tests. +/// Registers ALITH as the owner of derivative index 0 and funds the derivative +/// sub-account on the relay. +fn setup_derivative() { + setup_transactor(); + fund_moonbase_alith_with_dot(ONE_DOT * 1000); + + let derivative_index: u16 = 0; + + // Register ALITH as the owner of index 0. + moonbase_execute_with(|| { + assert_ok!(moonbase_runtime::XcmTransactor::register( + moonbase_runtime::RuntimeOrigin::root(), + moonbase_runtime::AccountId::from(ALITH), + derivative_index, + )); + }); + + // Fund the derivative account on relay. + // The derivative is computed from the sovereign account of Moonbase parachain. + WestendRelay::::execute_with(|| { + let sovereign = WestendRelay::::sovereign_account_id_of( + Location::new(0, [Parachain(MOONBASE_PARA_ID)]), + ); + let derivative = pallet_utility::derivative_account_id(sovereign, derivative_index); + assert_ok!(westend_runtime::Balances::transfer_allow_death( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE), + derivative.into(), + ONE_DOT * 100, + )); + }); +} + +#[test] +fn transact_through_derivative_to_relay() { + setup_derivative(); + + moonbase_execute_with(|| { + assert_ok!( + moonbase_runtime::XcmTransactor::transact_through_derivative( + moonbase_runtime::RuntimeOrigin::signed(moonbase_runtime::AccountId::from(ALITH),), + moonbase_runtime::xcm_config::Transactors::Relay, + 0u16, // derivative index + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent() + ),)), + fee_amount: Some(ONE_DOT * 10), + }, + // Inner call (unwrapped — the pallet wraps it in as_derivative). + relay_remark_call(), + TransactWeights { + transact_required_weight_at_most: 2_000_000_000u64.into(), + overall_weight: Some(Limited(4_000_000_000u64.into())), + }, + false, + ) + ); + }); + + assert_relay_remark_executed(); +} + +#[test] +fn transact_through_derivative_custom_fee_weight() { + setup_derivative(); + + moonbase_execute_with(|| { + assert_ok!( + moonbase_runtime::XcmTransactor::transact_through_derivative( + moonbase_runtime::RuntimeOrigin::signed(moonbase_runtime::AccountId::from(ALITH),), + moonbase_runtime::xcm_config::Transactors::Relay, + 0u16, + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent() + ),)), + fee_amount: Some(ONE_DOT * 5), + }, + relay_remark_call(), + TransactWeights { + transact_required_weight_at_most: 3_000_000_000u64.into(), + overall_weight: Some(Limited(6_000_000_000u64.into())), + }, + false, + ) + ); + }); + + assert_relay_remark_executed(); +} + +#[test] +fn transact_through_derivative_custom_fee_weight_refund() { + setup_derivative(); + + let sovereign_before = WestendRelay::::execute_with(|| { + let sovereign = WestendRelay::::sovereign_account_id_of( + Location::new(0, [Parachain(MOONBASE_PARA_ID)]), + ); + >::balance(&sovereign) + }); + + moonbase_execute_with(|| { + assert_ok!( + moonbase_runtime::XcmTransactor::transact_through_derivative( + moonbase_runtime::RuntimeOrigin::signed(moonbase_runtime::AccountId::from(ALITH),), + moonbase_runtime::xcm_config::Transactors::Relay, + 0u16, + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent() + ),)), + fee_amount: Some(ONE_DOT * 20), // overpay + }, + relay_remark_call(), + TransactWeights { + transact_required_weight_at_most: 2_000_000_000u64.into(), + overall_weight: Some(Limited(4_000_000_000u64.into())), + }, + true, // refund + ) + ); + }); + + assert_relay_remark_executed(); + + // With refund, surplus should be deposited back to the sovereign (SelfLocation). + let sovereign_after = WestendRelay::::execute_with(|| { + let sovereign = WestendRelay::::sovereign_account_id_of( + Location::new(0, [Parachain(MOONBASE_PARA_ID)]), + ); + >::balance(&sovereign) + }); + let fee_spent = sovereign_before.saturating_sub(sovereign_after); + assert!( + fee_spent < ONE_DOT * 20, + "With refund, sovereign should spend less than the full fee: spent={fee_spent}" + ); +} + +// =========================================================================== +// Transact through signed: para → para +// =========================================================================== + +/// Setup for para-to-para transact tests via signed origin. +/// Opens HRMP channels between Moonbase and Sibling, registers DOT on both, +/// and funds the derived account on the sibling. +fn setup_para_to_para_signed() -> moonbase_runtime::AccountId { + init_network(); + + // Register DOT + relay indices on Moonbase. + moonbase_execute_with(|| { + register_dot_asset(DOT_ASSET_ID); + set_westend_relay_indices(); + }); + + // Open HRMP channels. + WestendRelay::::execute_with(|| { + open_hrmp_channels(MOONBASE_PARA_ID, SIBLING_PARA_ID); + }); + + // Register DOT on sibling so it can accept DOT as XCM fee. + sibling_execute_with(|| { + register_dot_asset(DOT_ASSET_ID); + }); + + // Compute the derived account on the sibling for ALITH's signed origin from Moonbase. + // After DescendOrigin(AccountKey20(ALITH)), the sibling sees origin + // Location::new(1, [Parachain(2004), AccountKey20(ALITH)]). + let derived_on_sibling: moonbase_runtime::AccountId = sibling_execute_with(|| { + >::convert_location(&Location::new( + 1, + [ + Parachain(MOONBASE_PARA_ID), + AccountKey20 { + network: None, + key: ALITH, + }, + ], + )) + .expect("Should derive sibling account for Moonbase ALITH") + }); + + // Fund the derived account on sibling with DOT (relay → sibling DMP). + WestendRelay::::execute_with(|| { + assert_ok!( + westend_runtime::XcmPallet::transfer_assets_using_type_and_then( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(SIBLING_PARA_ID)], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(ONE_DOT * 100), + }]))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedAssetId::from(AssetId(Location::here()))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: derived_on_sibling.into(), + }], + ), + }]))), + WeightLimit::Unlimited, + ) + ); + }); + + // Verify the derived account received DOT. + sibling_execute_with(|| { + let balance = + moonbase_runtime::EvmForeignAssets::balance(DOT_ASSET_ID, derived_on_sibling).unwrap(); + assert!( + balance > sp_core::U256::zero(), + "Derived account on sibling should have DOT" + ); + }); + + derived_on_sibling +} + +/// Encode a `system::remark_with_event` call for the sibling (Moonbase runtime). +fn sibling_remark_call() -> Vec { + moonbase_runtime::RuntimeCall::System( + frame_system::Call::::remark_with_event { + remark: b"hello from Moonbase to sibling".to_vec(), + }, + ) + .encode() +} + +/// Assert that the sibling processed the HRMP transact and emitted a Remarked event. +fn assert_sibling_remark_executed() { + sibling_execute_with(|| { + let events = moonbase_runtime::System::events(); + + let has_remark = events.iter().any(|e| { + matches!( + &e.event, + moonbase_runtime::RuntimeEvent::System(frame_system::Event::Remarked { .. }) + ) + }); + assert!( + has_remark, + "Sibling should have emitted Remarked event from transact" + ); + }); +} + +#[test] +fn transact_through_signed_para_to_para() { + setup_para_to_para_signed(); + + moonbase_execute_with(|| { + assert_ok!(moonbase_runtime::XcmTransactor::transact_through_signed( + moonbase_runtime::RuntimeOrigin::signed(moonbase_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(SIBLING_PARA_ID)], + ))), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent(), + ))), + fee_amount: Some(ONE_DOT * 10), + }, + sibling_remark_call(), + TransactWeights { + transact_required_weight_at_most: 1_000_000_000u64.into(), + overall_weight: Some(Limited(4_000_000_000u64.into())), + }, + false, + )); + }); + + assert_sibling_remark_executed(); +} + +#[test] +fn transact_through_signed_para_to_para_refund() { + let derived_on_sibling = setup_para_to_para_signed(); + + let dot_before = sibling_execute_with(|| { + moonbase_runtime::EvmForeignAssets::balance(DOT_ASSET_ID, derived_on_sibling).unwrap() + }); + + moonbase_execute_with(|| { + assert_ok!(moonbase_runtime::XcmTransactor::transact_through_signed( + moonbase_runtime::RuntimeOrigin::signed(moonbase_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(SIBLING_PARA_ID)], + ))), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent(), + ))), + fee_amount: Some(ONE_DOT * 20), // overpay + }, + sibling_remark_call(), + TransactWeights { + transact_required_weight_at_most: 1_000_000_000u64.into(), + // Refund appendix (RefundSurplus + DepositAsset) needs extra weight. + overall_weight: Some(Limited(8_000_000_000u64.into())), + }, + true, // refund = true + )); + }); + + assert_sibling_remark_executed(); + + // With refund, the derived account should get surplus back. + let dot_after = sibling_execute_with(|| { + moonbase_runtime::EvmForeignAssets::balance(DOT_ASSET_ID, derived_on_sibling).unwrap() + }); + let spent = dot_before.saturating_sub(dot_after); + assert!( + spent < sp_core::U256::from(ONE_DOT * 20), + "With refund, derived account should spend less than the full 20 DOT fee: spent={spent}" + ); +} + +// =========================================================================== +// Transact through signed: para → para (EthereumXcm) +// =========================================================================== + +/// Common setup for Ethereum XCM transact tests. +/// Returns the derived account on the sibling. +fn setup_para_to_para_ethereum() -> moonbase_runtime::AccountId { + let derived_on_sibling = setup_para_to_para_signed(); + + // The derived account needs GLMR on the sibling for EVM value transfers. + sibling_execute_with(|| { + >::mint_into( + &derived_on_sibling, + moonbase_runtime::currency::UNIT * 10, + ) + .expect("Should mint GLMR for derived account on sibling"); + }); + + derived_on_sibling +} + +/// Encode an `EthereumXcm::transact` call that does an EVM value transfer. +fn ethereum_xcm_transfer_call(recipient: sp_core::H160, value: u128) -> Vec { + use sp_runtime::BoundedVec; + + let eth_tx = + xcm_primitives::EthereumXcmTransaction::V1(xcm_primitives::EthereumXcmTransactionV1 { + gas_limit: U256::from(21000), + fee_payment: xcm_primitives::EthereumXcmFee::Auto, + action: pallet_ethereum::TransactionAction::Call(recipient), + value: U256::from(value), + input: BoundedVec::< + u8, + sp_core::ConstU32<{ xcm_primitives::MAX_ETHEREUM_XCM_INPUT_SIZE }>, + >::try_from(vec![]) + .unwrap(), + access_list: None, + }); + + moonbase_runtime::RuntimeCall::EthereumXcm(pallet_ethereum_xcm::Call::< + moonbase_runtime::Runtime, + >::transact { + xcm_transaction: eth_tx, + }) + .encode() +} + +/// EVM transfer to ALITH on sibling via EthereumXcm::transact. +#[test] +fn transact_through_signed_para_to_para_ethereum() { + let _derived = setup_para_to_para_ethereum(); + + let transfer_value = 100u128; + let alith_h160 = sp_core::H160::from(ALITH); + + let alith_balance_before = sibling_execute_with(|| { + >::balance(&moonbase_runtime::AccountId::from( + ALITH, + )) + }); + + moonbase_execute_with(|| { + assert_ok!(moonbase_runtime::XcmTransactor::transact_through_signed( + moonbase_runtime::RuntimeOrigin::signed(moonbase_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(SIBLING_PARA_ID)], + ))), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent(), + ))), + fee_amount: Some(ONE_DOT * 10), + }, + ethereum_xcm_transfer_call(alith_h160, transfer_value), + TransactWeights { + transact_required_weight_at_most: 4_000_000_000u64.into(), + overall_weight: Some(Limited(8_000_000_000u64.into())), + }, + false, + )); + }); + + let alith_balance_after = sibling_execute_with(|| { + >::balance(&moonbase_runtime::AccountId::from( + ALITH, + )) + }); + assert_eq!( + alith_balance_after - alith_balance_before, + transfer_value, + "ALITH should receive {transfer_value} WEI on sibling via EthereumXcm transact" + ); +} + +/// EthereumXcm::transact_through_proxy fails without a proxy set up. +#[test] +fn transact_through_signed_para_to_para_ethereum_no_proxy_fails() { + let _derived = setup_para_to_para_ethereum(); + + let alith_h160 = sp_core::H160::from(ALITH); + // Use a distinct recipient so a self-transfer cannot mask proxy rejection. + let recipient: [u8; 20] = [42u8; 20]; + let recipient_h160 = sp_core::H160::from(recipient); + let transfer_value = 100u128; + + // Encode a transact_through_proxy call without any proxy being set. + let eth_tx = + xcm_primitives::EthereumXcmTransaction::V2(xcm_primitives::EthereumXcmTransactionV2 { + gas_limit: U256::from(21000), + action: pallet_ethereum::TransactionAction::Call(recipient_h160), + value: U256::from(transfer_value), + input: sp_runtime::BoundedVec::try_from(vec![]).unwrap(), + access_list: None, + }); + + let proxy_call = moonbase_runtime::RuntimeCall::EthereumXcm(pallet_ethereum_xcm::Call::< + moonbase_runtime::Runtime, + >::transact_through_proxy { + transact_as: alith_h160, + xcm_transaction: eth_tx, + }) + .encode(); + + let recipient_balance_before = sibling_execute_with(|| { + >::balance(&moonbase_runtime::AccountId::from( + recipient, + )) + }); + + moonbase_execute_with(|| { + assert_ok!(moonbase_runtime::XcmTransactor::transact_through_signed( + moonbase_runtime::RuntimeOrigin::signed(moonbase_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(SIBLING_PARA_ID)], + ))), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent(), + ))), + fee_amount: Some(ONE_DOT * 10), + }, + proxy_call, + TransactWeights { + transact_required_weight_at_most: 4_000_000_000u64.into(), + overall_weight: Some(Limited(8_000_000_000u64.into())), + }, + false, + )); + }); + + // The EVM transfer should NOT have happened (proxy not set). + let recipient_balance_after = sibling_execute_with(|| { + >::balance(&moonbase_runtime::AccountId::from( + recipient, + )) + }); + assert_eq!( + recipient_balance_after, recipient_balance_before, + "Recipient balance should be unchanged — transact_through_proxy should fail without proxy" + ); +} + +/// EthereumXcm::transact_through_proxy succeeds with a proxy set up. +#[test] +fn transact_through_signed_para_to_para_ethereum_proxy_succeeds() { + let derived = setup_para_to_para_ethereum(); + + let recipient: [u8; 20] = [42u8; 20]; + let transfer_value = 100u128; + + // Set up proxy: ALITH delegates to the derived account on the sibling. + sibling_execute_with(|| { + assert_ok!(moonbase_runtime::Proxy::add_proxy( + moonbase_runtime::RuntimeOrigin::signed(moonbase_runtime::AccountId::from(ALITH)), + derived, + moonbase_runtime::ProxyType::Any, + 0, + )); + }); + + let recipient_balance_before = sibling_execute_with(|| { + >::balance(&moonbase_runtime::AccountId::from( + recipient, + )) + }); + + // Encode a transact_through_proxy call targeting ALITH as proxy principal, + // EVM transfer to `recipient`. + let eth_tx = + xcm_primitives::EthereumXcmTransaction::V2(xcm_primitives::EthereumXcmTransactionV2 { + gas_limit: U256::from(21000), + action: pallet_ethereum::TransactionAction::Call(sp_core::H160::from(recipient)), + value: U256::from(transfer_value), + input: sp_runtime::BoundedVec::try_from(vec![]).unwrap(), + access_list: None, + }); + + let proxy_call = moonbase_runtime::RuntimeCall::EthereumXcm(pallet_ethereum_xcm::Call::< + moonbase_runtime::Runtime, + >::transact_through_proxy { + transact_as: sp_core::H160::from(ALITH), + xcm_transaction: eth_tx, + }) + .encode(); + + moonbase_execute_with(|| { + assert_ok!(moonbase_runtime::XcmTransactor::transact_through_signed( + moonbase_runtime::RuntimeOrigin::signed(moonbase_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(SIBLING_PARA_ID)], + ))), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent(), + ))), + fee_amount: Some(ONE_DOT * 10), + }, + proxy_call, + TransactWeights { + transact_required_weight_at_most: 4_000_000_000u64.into(), + overall_weight: Some(Limited(8_000_000_000u64.into())), + }, + false, + )); + }); + + let recipient_balance_after = sibling_execute_with(|| { + >::balance(&moonbase_runtime::AccountId::from( + recipient, + )) + }); + assert_eq!( + recipient_balance_after - recipient_balance_before, + transfer_value, + "Recipient should receive {transfer_value} WEI via EthereumXcm proxy transact" + ); +} diff --git a/runtime/moonbase/tests/xcm_emulator/transfers.rs b/runtime/moonbase/tests/xcm_emulator/transfers.rs new file mode 100644 index 00000000000..c93338a28c5 --- /dev/null +++ b/runtime/moonbase/tests/xcm_emulator/transfers.rs @@ -0,0 +1,1275 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbase. + +// Moonbase 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. + +// Moonbase 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 Moonbase. If not, see . + +//! Transfer tests using xcm-emulator with the **real** Moonbase runtime. +//! +//! Covers: relay→para, para→relay, para→para transfers, fee behaviour, +//! account sufficiency, and error cases. + +use crate::network::*; +use frame_support::{ + assert_ok, + traits::{fungible::Inspect, tokens::fungible::Mutate}, +}; +use sp_core::U256; +use xcm::latest::prelude::*; +use xcm_emulator::TestExt; + +// =========================================================================== +// Setup helper +// =========================================================================== + +/// Full network init: register DOT on Moonbase, configure weight trader. +fn setup_relay_to_moonbase() { + init_network(); + moonbase_execute_with(|| { + register_dot_asset(DOT_ASSET_ID); + }); +} + +/// Full network init with sibling: register DOT on both paras, open HRMP channels. +fn setup_with_sibling() { + init_network(); + + moonbase_execute_with(|| { + register_dot_asset(DOT_ASSET_ID); + }); + sibling_execute_with(|| { + register_dot_asset(DOT_ASSET_ID); + }); + + // Open bi-directional HRMP channels between Moonbase (2004) and Sibling (2005). + WestendRelay::::execute_with(|| { + open_hrmp_channels(MOONBASE_PARA_ID, SIBLING_PARA_ID); + }); +} + +// =========================================================================== +// Transfer: Relay → Moonbase (DMP) +// =========================================================================== + +#[test] +fn transfer_dot_from_relay_to_moonbase() { + setup_relay_to_moonbase(); + + let sender = RELAY_ALICE; + let beneficiary_key = ALITH; + + WestendRelay::::execute_with(|| { + let balance_before = >::balance(&sender); + + assert_ok!( + westend_runtime::XcmPallet::transfer_assets_using_type_and_then( + westend_runtime::RuntimeOrigin::signed(sender.clone()), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(MOONBASE_PARA_ID)] + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(ONE_DOT * 10), + }]))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedAssetId::from(AssetId(Location::here()))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: beneficiary_key, + }], + ), + }]))), + WeightLimit::Unlimited, + ) + ); + + let balance_after = >::balance(&sender); + assert!( + balance_after < balance_before, + "Sender balance should decrease" + ); + }); + + moonbase_execute_with(|| { + let beneficiary = moonbase_runtime::AccountId::from(beneficiary_key); + let balance = moonbase_runtime::EvmForeignAssets::balance(DOT_ASSET_ID, beneficiary) + .expect("balance query should succeed"); + assert!( + balance > U256::zero(), + "Beneficiary should have DOT on Moonbase, got {balance}" + ); + }); +} + +// =========================================================================== +// Transfer: Moonbase → Relay (UMP) +// =========================================================================== + +#[test] +fn transfer_dot_from_moonbase_to_relay() { + setup_relay_to_moonbase(); + + // First: send DOT from relay to Moonbase so ALITH has some DOT. + WestendRelay::::execute_with(|| { + assert_ok!( + westend_runtime::XcmPallet::transfer_assets_using_type_and_then( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(MOONBASE_PARA_ID)] + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(ONE_DOT * 100), + }]))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedAssetId::from(AssetId(Location::here()))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: ALITH + }], + ), + }]))), + WeightLimit::Unlimited, + ) + ); + }); + + let alith_dot_before = moonbase_execute_with(|| { + moonbase_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonbase_runtime::AccountId::from(ALITH), + ) + .unwrap() + }); + assert!(alith_dot_before > U256::zero(), "ALITH should have DOT"); + + // Record relay-side balance of a relay account before the return transfer. + let relay_bob = sp_runtime::AccountId32::new([2u8; 32]); + let relay_bob_before = WestendRelay::::execute_with(|| { + >::balance(&relay_bob) + }); + + // Now send DOT back from Moonbase to relay via PolkadotXcm. + // DOT's reserve is the relay, so we use DestinationReserve transfer type. + moonbase_execute_with(|| { + let dot_location = Location::parent(); + let dest = Location::parent(); + let beneficiary = Location::new( + 0, + [AccountId32 { + network: None, + id: relay_bob.clone().into(), + }], + ); + let amount = ONE_DOT * 5; + + assert_ok!( + moonbase_runtime::PolkadotXcm::transfer_assets_using_type_and_then( + moonbase_runtime::RuntimeOrigin::signed(moonbase_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(dest)), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(dot_location.clone()), + fun: Fungible(amount), + }]))), + Box::new(xcm_executor::traits::TransferType::DestinationReserve), + Box::new(xcm::VersionedAssetId::from(AssetId(dot_location))), + Box::new(xcm_executor::traits::TransferType::DestinationReserve), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary, + }]))), + WeightLimit::Unlimited, + ) + ); + }); + + // Verify relay account received DOT (minus fees). + let relay_bob_after = WestendRelay::::execute_with(|| { + >::balance(&relay_bob) + }); + assert!( + relay_bob_after > relay_bob_before, + "Relay Bob should have more DOT: before={relay_bob_before}, after={relay_bob_after}" + ); +} + +// =========================================================================== +// Fee behaviour: insufficient fees +// =========================================================================== + +#[test] +fn error_when_not_paying_enough_fees() { + setup_relay_to_moonbase(); + + // Send a tiny amount (1 unit) from relay — should fail to pay Moonbase execution fees. + WestendRelay::::execute_with(|| { + assert_ok!( + westend_runtime::XcmPallet::transfer_assets_using_type_and_then( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(MOONBASE_PARA_ID)] + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(1), // way too little for fees + }]))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedAssetId::from(AssetId(Location::here()))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: ALITH + }], + ), + }]))), + WeightLimit::Unlimited, + ) + ); + }); + + // ALITH should NOT have received the token (execution failed). + moonbase_execute_with(|| { + let balance = moonbase_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonbase_runtime::AccountId::from(ALITH), + ) + .unwrap(); + assert_eq!( + balance, + U256::zero(), + "Should not receive DOT when fees are insufficient" + ); + }); +} + +// =========================================================================== +// Fee behaviour: fees go to treasury +// =========================================================================== + +#[test] +fn fees_collected_by_treasury() { + setup_relay_to_moonbase(); + + let treasury_dot_before = moonbase_execute_with(|| { + let treasury = moonbase_runtime::Treasury::account_id(); + moonbase_runtime::EvmForeignAssets::balance(DOT_ASSET_ID, treasury).unwrap_or(U256::zero()) + }); + + // Send DOT from relay to Moonbase (fees will be charged). + WestendRelay::::execute_with(|| { + assert_ok!( + westend_runtime::XcmPallet::transfer_assets_using_type_and_then( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(MOONBASE_PARA_ID)] + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(ONE_DOT * 10), + }]))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedAssetId::from(AssetId(Location::here()))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: BALTATHAR + }], + ), + }]))), + WeightLimit::Unlimited, + ) + ); + }); + + moonbase_execute_with(|| { + let treasury = moonbase_runtime::Treasury::account_id(); + let treasury_dot_after = + moonbase_runtime::EvmForeignAssets::balance(DOT_ASSET_ID, treasury) + .unwrap_or(U256::zero()); + assert!( + treasury_dot_after > treasury_dot_before, + "Treasury should collect fees: before={treasury_dot_before}, after={treasury_dot_after}" + ); + + // And beneficiary should have gotten the rest (not the full amount). + let beneficiary_balance = moonbase_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonbase_runtime::AccountId::from(BALTATHAR), + ) + .unwrap(); + assert!( + beneficiary_balance > U256::zero(), + "Beneficiary received DOT" + ); + assert!( + beneficiary_balance < U256::from(ONE_DOT * 10), + "Beneficiary received less than sent (fees deducted)" + ); + }); +} + +// =========================================================================== +// Account sufficiency: non-existent account receives foreign asset +// =========================================================================== + +#[test] +fn receive_asset_for_non_existent_account() { + setup_relay_to_moonbase(); + + let fresh_account: [u8; 20] = [42u8; 20]; + + WestendRelay::::execute_with(|| { + assert_ok!( + westend_runtime::XcmPallet::transfer_assets_using_type_and_then( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(MOONBASE_PARA_ID)] + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(ONE_DOT * 10), + }]))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedAssetId::from(AssetId(Location::here()))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: fresh_account, + }], + ), + }]))), + WeightLimit::Unlimited, + ) + ); + }); + + moonbase_execute_with(|| { + let balance = moonbase_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonbase_runtime::AccountId::from(fresh_account), + ) + .unwrap(); + assert!( + balance > U256::zero(), + "Fresh (non-existent) account should receive DOT via XCM" + ); + }); +} + +// =========================================================================== +// Transfer: Para → Para via relay (XCMP/HRMP) +// =========================================================================== + +#[test] +fn transfer_dot_from_moonbase_to_sibling() { + setup_with_sibling(); + + // First fund Moonbase ALITH with DOT from relay. + WestendRelay::::execute_with(|| { + assert_ok!( + westend_runtime::XcmPallet::transfer_assets_using_type_and_then( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(MOONBASE_PARA_ID)] + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(ONE_DOT * 100), + }]))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedAssetId::from(AssetId(Location::here()))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: ALITH + }], + ), + }]))), + WeightLimit::Unlimited, + ) + ); + }); + + // Verify ALITH got DOT on Moonbase. + let alith_dot = moonbase_execute_with(|| { + moonbase_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonbase_runtime::AccountId::from(ALITH), + ) + .unwrap() + }); + assert!(alith_dot > U256::zero(), "ALITH should have DOT"); + + // Now send DOT from Moonbase to Sibling via reserve transfer through relay. + // DOT's reserve is the relay (parent), so we use RemoteReserve. + // The custom_xcm_on_dest must include BuyExecution since the sibling's + // barrier requires paid execution. + moonbase_execute_with(|| { + let dest = Location::new(1, [Parachain(SIBLING_PARA_ID)]); + let beneficiary = Location::new( + 0, + [AccountKey20 { + network: None, + key: BALTATHAR, + }], + ); + let dot_location = Location::parent(); + // Send a large amount so enough survives relay fees for the sibling. + let amount = ONE_DOT * 50; + + assert_ok!( + moonbase_runtime::PolkadotXcm::transfer_assets_using_type_and_then( + moonbase_runtime::RuntimeOrigin::signed(moonbase_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(dest)), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(dot_location.clone()), + fun: Fungible(amount), + }]))), + Box::new(xcm_executor::traits::TransferType::RemoteReserve( + xcm::VersionedLocation::from(Location::parent()) + )), + Box::new(xcm::VersionedAssetId::from(AssetId(dot_location.clone()))), + Box::new(xcm_executor::traits::TransferType::RemoteReserve( + xcm::VersionedLocation::from(Location::parent()) + )), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![ + BuyExecution { + // Use a small fee amount that will definitely be in holding + // after the relay takes its share. + fees: Asset { + id: AssetId(dot_location), + fun: Fungible(ONE_DOT / 10), + }, + weight_limit: WeightLimit::Unlimited, + }, + DepositAsset { + assets: Wild(All), + beneficiary, + }, + ]))), + WeightLimit::Unlimited, + ) + ); + }); + + // Trigger message routing on the relay so the DMP is delivered to sibling. + WestendRelay::::execute_with(|| {}); + + // Verify BALTATHAR received DOT on the sibling. + sibling_execute_with(|| { + let balance = moonbase_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonbase_runtime::AccountId::from(BALTATHAR), + ) + .unwrap(); + assert!( + balance > U256::zero(), + "BALTATHAR should have DOT on sibling, got {balance}" + ); + }); +} + +// =========================================================================== +// EVM account with native balance receives foreign assets +// =========================================================================== + +#[test] +fn evm_account_receives_foreign_asset() { + setup_relay_to_moonbase(); + + // ALITH has GLMR from genesis. Send DOT and verify both balances coexist. + WestendRelay::::execute_with(|| { + assert_ok!( + westend_runtime::XcmPallet::transfer_assets_using_type_and_then( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(MOONBASE_PARA_ID)], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(ONE_DOT * 10), + }]))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedAssetId::from(AssetId(Location::here()))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: ALITH + }], + ), + }]))), + WeightLimit::Unlimited, + ) + ); + }); + + moonbase_execute_with(|| { + // ALITH should have both native GLMR and foreign DOT. + let glmr = >::balance( + &moonbase_runtime::AccountId::from(ALITH), + ); + assert!(glmr > 0, "ALITH should still have GLMR"); + + let dot = moonbase_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonbase_runtime::AccountId::from(ALITH), + ) + .unwrap(); + assert!(dot > U256::zero(), "ALITH should also have DOT"); + }); +} + +// =========================================================================== +// Foreign assets survive native balance drainage +// =========================================================================== + +#[test] +fn foreign_assets_survive_native_balance_drain() { + setup_relay_to_moonbase(); + + let test_account: [u8; 20] = [77u8; 20]; + + // Give the test account some GLMR. + moonbase_execute_with(|| { + >::mint_into( + &moonbase_runtime::AccountId::from(test_account), + moonbase_runtime::currency::UNIT, + ) + .expect("Should mint GLMR"); + }); + + // Send DOT to the test account. + WestendRelay::::execute_with(|| { + assert_ok!( + westend_runtime::XcmPallet::transfer_assets_using_type_and_then( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(MOONBASE_PARA_ID)], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(ONE_DOT * 10), + }]))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedAssetId::from(AssetId(Location::here()))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: test_account + }], + ), + }]))), + WeightLimit::Unlimited, + ) + ); + }); + + // Drain all GLMR, then verify foreign asset is still accessible. + moonbase_execute_with(|| { + let balance = >::balance( + &moonbase_runtime::AccountId::from(test_account), + ); + let _ = >::burn_from( + &moonbase_runtime::AccountId::from(test_account), + balance, + frame_support::traits::tokens::Preservation::Expendable, + frame_support::traits::tokens::Precision::BestEffort, + frame_support::traits::tokens::Fortitude::Force, + ); + + let remaining = >::balance( + &moonbase_runtime::AccountId::from(test_account), + ); + assert_eq!(remaining, 0, "Native balance should be zero after drain"); + + // Foreign asset balance should still be accessible. + let dot = moonbase_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonbase_runtime::AccountId::from(test_account), + ) + .unwrap(); + assert!( + dot > U256::zero(), + "Foreign asset should survive native balance drain" + ); + }); +} + +// =========================================================================== +// Native asset (GLMR) para → para transfers +// =========================================================================== + +/// Register Moonbase's native GLMR as a foreign asset on the sibling and +/// configure the XCM weight trader price. +fn register_unit_on_sibling() { + sibling_execute_with(|| { + // From the sibling's perspective, Moonbase's native token lives at: + // ../Parachain(2004)/PalletInstance(3) (pallet_balances = index 3) + let glmr_location = + xcm::latest::Location::new(1, [Parachain(MOONBASE_PARA_ID), PalletInstance(3u8)]); + + frame_support::assert_ok!(moonbase_runtime::EvmForeignAssets::create_foreign_asset( + moonbase_runtime::RuntimeOrigin::root(), + UNIT_ASSET_ID, + glmr_location.clone(), + 18, // GLMR has 18 decimals + b"UNIT".to_vec().try_into().unwrap(), + b"Moonbase".to_vec().try_into().unwrap(), + )); + + frame_support::assert_ok!(moonbase_runtime::XcmWeightTrader::add_asset( + moonbase_runtime::RuntimeOrigin::root(), + glmr_location, + 10_000_000_000_000_000_000_000_000_000u128, // 10^28 (generous relative price) + )); + }); +} + +/// Setup for GLMR para→para transfers: open HRMP, register DOT on Moonbase, +/// register GLMR on sibling. +fn setup_unit_para_to_para() { + setup_with_sibling(); + register_unit_on_sibling(); +} + +/// Transfer GLMR from Moonbase to Sibling (reserve-backed). +#[test] +fn transfer_unit_from_moonbase_to_sibling() { + setup_unit_para_to_para(); + + let alith_before = moonbase_execute_with(|| { + >::balance(&moonbase_runtime::AccountId::from( + ALITH, + )) + }); + + let amount = moonbase_runtime::currency::UNIT; // 1 GLMR + + moonbase_execute_with(|| { + assert_ok!(moonbase_runtime::PolkadotXcm::transfer_assets( + moonbase_runtime::RuntimeOrigin::signed(moonbase_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(SIBLING_PARA_ID)], + ))), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [AccountKey20 { + network: None, + key: BALTATHAR + }], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::new(0, [PalletInstance(3)])), + fun: Fungible(amount), + }]))), + 0, + WeightLimit::Unlimited, + )); + }); + + // ALITH should have less GLMR after the transfer. + let alith_after = moonbase_execute_with(|| { + >::balance(&moonbase_runtime::AccountId::from( + ALITH, + )) + }); + assert!( + alith_after < alith_before, + "ALITH should have less GLMR after transfer" + ); + assert!( + alith_before - alith_after >= amount, + "ALITH should have spent at least {amount}" + ); + + // BALTATHAR should have GLMR on sibling (as foreign asset). + sibling_execute_with(|| { + let balance = moonbase_runtime::EvmForeignAssets::balance( + UNIT_ASSET_ID, + moonbase_runtime::AccountId::from(BALTATHAR), + ) + .unwrap(); + assert!( + balance > U256::zero(), + "BALTATHAR should have GLMR on sibling" + ); + }); +} + +/// Roundtrip: GLMR from Moonbase → Sibling → back to Moonbase. +#[test] +fn transfer_unit_roundtrip_moonbase_sibling() { + setup_unit_para_to_para(); + + let alith_initial = moonbase_execute_with(|| { + >::balance(&moonbase_runtime::AccountId::from( + ALITH, + )) + }); + + let amount = moonbase_runtime::currency::UNIT; // 1 GLMR + + // Step 1: Send GLMR from Moonbase to Sibling (BALTATHAR). + moonbase_execute_with(|| { + assert_ok!(moonbase_runtime::PolkadotXcm::transfer_assets( + moonbase_runtime::RuntimeOrigin::signed(moonbase_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(SIBLING_PARA_ID)], + ))), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [AccountKey20 { + network: None, + key: BALTATHAR + }], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::new(0, [PalletInstance(3)])), + fun: Fungible(amount), + }]))), + 0, + WeightLimit::Unlimited, + )); + }); + + // Verify BALTATHAR got GLMR on sibling. + let glmr_on_sibling = sibling_execute_with(|| { + moonbase_runtime::EvmForeignAssets::balance( + UNIT_ASSET_ID, + moonbase_runtime::AccountId::from(BALTATHAR), + ) + .unwrap() + }); + assert!( + glmr_on_sibling > U256::zero(), + "BALTATHAR should have GLMR on sibling: {glmr_on_sibling}" + ); + + // Step 2: Send GLMR back from Sibling to Moonbase (ALITH). + // From the sibling's perspective, GLMR is at ../Parachain(2004)/PalletInstance(3). + sibling_execute_with(|| { + let glmr_location = Location::new(1, [Parachain(MOONBASE_PARA_ID), PalletInstance(3)]); + + assert_ok!(moonbase_runtime::PolkadotXcm::transfer_assets( + moonbase_runtime::RuntimeOrigin::signed(moonbase_runtime::AccountId::from(BALTATHAR),), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(MOONBASE_PARA_ID)], + ))), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [AccountKey20 { + network: None, + key: ALITH + }], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(glmr_location), + fun: Fungible(glmr_on_sibling.try_into().unwrap()), + }]))), + 0, + WeightLimit::Unlimited, + )); + }); + + // ALITH should have recovered most of the GLMR (minus fees on both hops). + let alith_final = moonbase_execute_with(|| { + >::balance(&moonbase_runtime::AccountId::from( + ALITH, + )) + }); + // After roundtrip, ALITH loses some to fees but should still have most. + let total_lost = alith_initial.saturating_sub(alith_final); + assert!( + total_lost < amount, + "Roundtrip should only lose fees, not the full amount: lost={total_lost}, sent={amount}" + ); +} + +/// GLMR transfer with trader: fees are deducted from GLMR on the sibling. +#[test] +fn transfer_unit_to_sibling_with_trader_fees() { + setup_unit_para_to_para(); + + let amount = moonbase_runtime::currency::UNIT * 100; // 100 GLMR + + moonbase_execute_with(|| { + assert_ok!(moonbase_runtime::PolkadotXcm::transfer_assets( + moonbase_runtime::RuntimeOrigin::signed(moonbase_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(SIBLING_PARA_ID)], + ))), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [AccountKey20 { + network: None, + key: BALTATHAR + }], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::new(0, [PalletInstance(3)])), + fun: Fungible(amount), + }]))), + 0, + WeightLimit::Unlimited, + )); + }); + + sibling_execute_with(|| { + let received = moonbase_runtime::EvmForeignAssets::balance( + UNIT_ASSET_ID, + moonbase_runtime::AccountId::from(BALTATHAR), + ) + .unwrap(); + + // BALTATHAR should receive less than the full amount (fees deducted). + assert!( + received > U256::zero() && received < U256::from(amount), + "Should receive less than full amount due to fees: received={received}, sent={amount}" + ); + + // Treasury should have received some GLMR as fees. + let treasury = moonbase_runtime::Treasury::account_id(); + let treasury_fee = + moonbase_runtime::EvmForeignAssets::balance(UNIT_ASSET_ID, treasury).unwrap(); + assert!( + treasury_fee > U256::zero(), + "Treasury should have collected GLMR fees" + ); + }); +} + +// =========================================================================== +// DOT transfers via RemoteReserve (relay as reserve) +// =========================================================================== + +/// Fund ALITH with DOT via relay DMP. +fn fund_moonbase_alith_with_dot(amount: u128) { + WestendRelay::::execute_with(|| { + let beneficiary = Location::new( + 0, + [AccountKey20 { + network: None, + key: ALITH, + }], + ); + let assets: xcm::VersionedAssets = (Location::here(), amount).into(); + let fees_id: xcm::VersionedAssetId = AssetId(Location::here()).into(); + let xcm_on_dest = Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary, + }]); + + assert_ok!( + westend_runtime::XcmPallet::transfer_assets_using_type_and_then( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE.clone()), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(MOONBASE_PARA_ID)] + ))), + Box::new(assets), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(fees_id), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedXcm::V5(xcm_on_dest)), + WeightLimit::Unlimited, + ) + ); + }); +} + +/// Send DOT from Moonbase to a sibling using `RemoteReserve` through the +/// relay. DOT's reserve is the relay (parent), so a direct +/// `DestinationReserve` is invalid — the relay must mediate. +#[test] +fn transfer_dot_to_sibling_via_remote_reserve() { + setup_with_sibling(); + + let send_amount = ONE_DOT * 100; + fund_moonbase_alith_with_dot(send_amount); + + let alith_dot_before = moonbase_execute_with(|| { + moonbase_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonbase_runtime::AccountId::from(ALITH), + ) + .unwrap_or_default() + }); + assert!( + alith_dot_before > U256::zero(), + "ALITH should have DOT before transfer" + ); + + let transfer = ONE_DOT * 50; + + moonbase_execute_with(|| { + let dot_location = Location::parent(); + + assert_ok!( + moonbase_runtime::PolkadotXcm::transfer_assets_using_type_and_then( + moonbase_runtime::RuntimeOrigin::signed(moonbase_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(SIBLING_PARA_ID)], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(dot_location.clone()), + fun: Fungible(transfer), + }]))), + Box::new(xcm_executor::traits::TransferType::RemoteReserve( + xcm::VersionedLocation::from(Location::parent()), + )), + Box::new(xcm::VersionedAssetId::from(AssetId(dot_location.clone()))), + Box::new(xcm_executor::traits::TransferType::RemoteReserve( + xcm::VersionedLocation::from(Location::parent()), + )), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![ + BuyExecution { + fees: Asset { + id: AssetId(dot_location), + fun: Fungible(ONE_DOT / 10), + }, + weight_limit: WeightLimit::Unlimited, + }, + DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: BALTATHAR, + }], + ), + }, + ]))), + WeightLimit::Unlimited, + ) + ); + }); + + WestendRelay::::execute_with(|| {}); + + let alith_dot_after = moonbase_execute_with(|| { + moonbase_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonbase_runtime::AccountId::from(ALITH), + ) + .unwrap_or_default() + }); + assert!( + alith_dot_after < alith_dot_before, + "ALITH DOT should decrease after transfer" + ); + + let baltathar_dot = sibling_execute_with(|| { + moonbase_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonbase_runtime::AccountId::from(BALTATHAR), + ) + .unwrap_or_default() + }); + assert!( + baltathar_dot > U256::zero(), + "BALTATHAR should have DOT on sibling (got {baltathar_dot})" + ); +} + +/// Roundtrip: DOT from Moonbase → Sibling → back to Moonbase, both legs +/// using RemoteReserve through the relay. +#[test] +fn transfer_dot_roundtrip_via_remote_reserve() { + setup_with_sibling(); + + let send_amount = ONE_DOT * 100; + fund_moonbase_alith_with_dot(send_amount); + + let outbound = ONE_DOT * 50; + let dot_location = Location::parent(); + + // ── Moonbase → Sibling ──────────────────────────────────────────────── + moonbase_execute_with(|| { + assert_ok!( + moonbase_runtime::PolkadotXcm::transfer_assets_using_type_and_then( + moonbase_runtime::RuntimeOrigin::signed(moonbase_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(SIBLING_PARA_ID)], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(dot_location.clone()), + fun: Fungible(outbound), + }]))), + Box::new(xcm_executor::traits::TransferType::RemoteReserve( + xcm::VersionedLocation::from(Location::parent()), + )), + Box::new(xcm::VersionedAssetId::from(AssetId(dot_location.clone()))), + Box::new(xcm_executor::traits::TransferType::RemoteReserve( + xcm::VersionedLocation::from(Location::parent()), + )), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![ + BuyExecution { + fees: Asset { + id: AssetId(dot_location.clone()), + fun: Fungible(ONE_DOT / 10), + }, + weight_limit: WeightLimit::Unlimited, + }, + DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: BALTATHAR, + }], + ), + }, + ]))), + WeightLimit::Unlimited, + ) + ); + }); + + WestendRelay::::execute_with(|| {}); + + let baltathar_dot = sibling_execute_with(|| { + moonbase_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonbase_runtime::AccountId::from(BALTATHAR), + ) + .unwrap_or_default() + }); + assert!(baltathar_dot > U256::zero(), "Sibling should have DOT"); + + // ── Sibling → Moonbase ──────────────────────────────────────────────── + let return_amount_raw: u128 = baltathar_dot.try_into().unwrap(); + let return_half = return_amount_raw / 2; + + sibling_execute_with(|| { + assert_ok!( + moonbase_runtime::PolkadotXcm::transfer_assets_using_type_and_then( + moonbase_runtime::RuntimeOrigin::signed(moonbase_runtime::AccountId::from( + BALTATHAR, + )), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(MOONBASE_PARA_ID)], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(dot_location.clone()), + fun: Fungible(return_half), + }]))), + Box::new(xcm_executor::traits::TransferType::RemoteReserve( + xcm::VersionedLocation::from(Location::parent()), + )), + Box::new(xcm::VersionedAssetId::from(AssetId(dot_location.clone()))), + Box::new(xcm_executor::traits::TransferType::RemoteReserve( + xcm::VersionedLocation::from(Location::parent()), + )), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![ + BuyExecution { + fees: Asset { + id: AssetId(dot_location), + fun: Fungible(ONE_DOT / 10), + }, + weight_limit: WeightLimit::Unlimited, + }, + DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: ALITH, + }], + ), + }, + ]))), + WeightLimit::Unlimited, + ) + ); + }); + + WestendRelay::::execute_with(|| {}); + + let alith_dot_final = moonbase_execute_with(|| { + moonbase_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonbase_runtime::AccountId::from(ALITH), + ) + .unwrap_or_default() + }); + assert!( + alith_dot_final > U256::from(send_amount - outbound), + "ALITH should have more DOT than after the outbound leg (got {alith_dot_final})" + ); +} + +/// Transfer GLMR to a sibling as a self-reserve asset (GLMR pays its own +/// fees). Exercises `transfer_assets` with a single asset where the fee +/// asset and the transfer asset are the same. +#[test] +fn transfer_unit_self_reserve_to_sibling() { + setup_with_sibling(); + register_unit_on_sibling(); + + let glmr_amount = moonbase_runtime::currency::UNIT; + + moonbase_execute_with(|| { + assert_ok!(moonbase_runtime::PolkadotXcm::transfer_assets( + moonbase_runtime::RuntimeOrigin::signed(moonbase_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(SIBLING_PARA_ID)], + ))), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [AccountKey20 { + network: None, + key: BALTATHAR, + }], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::new(0, [PalletInstance(3)])), + fun: Fungible(glmr_amount), + }]))), + 0, + WeightLimit::Unlimited, + )); + }); + + let bal_glmr = sibling_execute_with(|| { + moonbase_runtime::EvmForeignAssets::balance( + UNIT_ASSET_ID, + moonbase_runtime::AccountId::from(BALTATHAR), + ) + .unwrap_or_default() + }); + assert!( + bal_glmr > U256::zero(), + "BALTATHAR should have received GLMR on sibling (got {bal_glmr})" + ); +} + +/// Receive a sibling-native foreign asset on Moonbase. +/// A sibling sends its own native token (another Moonbase instance's GLMR) +/// to Moonbase, which receives it as an EVM foreign asset. +#[test] +fn receive_sibling_native_asset() { + setup_with_sibling(); + + // On Moonbase, register the sibling's GLMR (PalletInstance(3) on para 2005) + // as a foreign asset with id=3. + const SIBLING_UNIT_ASSET_ID: u128 = 3; + moonbase_execute_with(|| { + let sibling_glmr_location = + xcm::latest::Location::new(1, [Parachain(SIBLING_PARA_ID), PalletInstance(3u8)]); + + frame_support::assert_ok!(moonbase_runtime::EvmForeignAssets::create_foreign_asset( + moonbase_runtime::RuntimeOrigin::root(), + SIBLING_UNIT_ASSET_ID, + sibling_glmr_location.clone(), + 18, + b"sGLMR".to_vec().try_into().unwrap(), + b"Sibling Glimmer".to_vec().try_into().unwrap(), + )); + + frame_support::assert_ok!(moonbase_runtime::XcmWeightTrader::add_asset( + moonbase_runtime::RuntimeOrigin::root(), + sibling_glmr_location, + 10_000_000_000_000_000_000_000_000_000u128, + )); + }); + + let amount = moonbase_runtime::currency::UNIT; + + sibling_execute_with(|| { + assert_ok!(moonbase_runtime::PolkadotXcm::transfer_assets( + moonbase_runtime::RuntimeOrigin::signed(moonbase_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(MOONBASE_PARA_ID)], + ))), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [AccountKey20 { + network: None, + key: BALTATHAR, + }], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::new(0, [PalletInstance(3)])), + fun: Fungible(amount), + }]))), + 0, + WeightLimit::Unlimited, + )); + }); + + let bal = moonbase_execute_with(|| { + moonbase_runtime::EvmForeignAssets::balance( + SIBLING_UNIT_ASSET_ID, + moonbase_runtime::AccountId::from(BALTATHAR), + ) + .unwrap_or_default() + }); + assert!( + bal > U256::zero(), + "BALTATHAR should have sibling GLMR on Moonbase (got {bal})" + ); +} diff --git a/runtime/moonbase/tests/xcm_emulator/versioning.rs b/runtime/moonbase/tests/xcm_emulator/versioning.rs new file mode 100644 index 00000000000..bdf8461d9cb --- /dev/null +++ b/runtime/moonbase/tests/xcm_emulator/versioning.rs @@ -0,0 +1,207 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! XCM version discovery / negotiation tests. +//! +//! Verifies that `SafeXcmVersion` is configured from genesis and that +//! Moonbeam discovers the XCM version of remote chains (relay and siblings) +//! after the first cross-chain interaction. +//! +//! Full runtime-upgrade version negotiation (as in the legacy mock tests) +//! is not feasible with xcm-emulator because there is no mock version +//! switcher. These tests cover the subset that works with the real runtime. + +use crate::network::*; +use frame_support::assert_ok; +use xcm::latest::prelude::*; +use xcm_emulator::TestExt; + +// =========================================================================== +// Helpers +// =========================================================================== + +/// Register Moonbeam GLMR as foreign asset on the current chain context. +fn register_glmr_foreign_asset(source_para_id: u32) { + let glmr_location = + xcm::latest::Location::new(1, [Parachain(source_para_id), PalletInstance(3u8)]); + + frame_support::assert_ok!(moonbase_runtime::EvmForeignAssets::create_foreign_asset( + moonbase_runtime::RuntimeOrigin::root(), + UNIT_ASSET_ID, + glmr_location.clone(), + 18, + b"UNIT".to_vec().try_into().unwrap(), + b"Moonbase".to_vec().try_into().unwrap(), + )); + + frame_support::assert_ok!(moonbase_runtime::XcmWeightTrader::add_asset( + moonbase_runtime::RuntimeOrigin::root(), + glmr_location, + 10_000_000_000_000_000_000_000_000_000u128, + )); +} + +// =========================================================================== +// Tests +// =========================================================================== + +/// Verify that Moonbeam subscribes to the relay's XCM version on first +/// interaction. After a DMP transfer the relay should know Moonbeam's +/// supported XCM version. +#[test] +fn xcm_version_discovery_with_relay() { + init_network(); + + moonbase_execute_with(|| { + register_dot_asset(DOT_ASSET_ID); + }); + + // Send DOT from relay to Moonbeam to trigger version discovery. + WestendRelay::::execute_with(|| { + let beneficiary = Location::new( + 0, + [AccountKey20 { + network: None, + key: ALITH, + }], + ); + let assets: xcm::VersionedAssets = (Location::here(), ONE_DOT * 5).into(); + let fees_id: xcm::VersionedAssetId = AssetId(Location::here()).into(); + let xcm_on_dest = Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary, + }]); + + assert_ok!( + westend_runtime::XcmPallet::transfer_assets_using_type_and_then( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE.clone()), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(MOONBASE_PARA_ID)] + ))), + Box::new(assets), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(fees_id), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedXcm::V5(xcm_on_dest)), + WeightLimit::Unlimited, + ) + ); + }); + + // Verify the relay can version-wrap an XCM destined for Moonbase. + // query_delivery_fees calls validate_send → ChildParachainRouter::validate → + // wrap_version, which requires SupportedVersion or SafeXcmVersion to be set. + WestendRelay::::execute_with(|| { + use xcm_runtime_apis::fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV2; + let fees = westend_runtime::Runtime::query_delivery_fees( + xcm::VersionedLocation::from(Location::new(0, [Parachain(MOONBASE_PARA_ID)])), + xcm::VersionedXcm::from(Xcm::<()>(vec![ClearOrigin])), + xcm::VersionedAssetId::from(AssetId(Location::here())), + ); + assert!( + fees.is_ok(), + "Relay should resolve XCM version for Moonbase destination" + ); + }); + + // Verify Moonbase can version-wrap an XCM destined for the relay. + // SafeXcmVersion is set from genesis, which wrap_version uses as fallback. + moonbase_execute_with(|| { + use xcm_runtime_apis::fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV2; + let fees = moonbase_runtime::Runtime::query_delivery_fees( + xcm::VersionedLocation::from(Location::parent()), + xcm::VersionedXcm::from(Xcm::<()>(vec![ClearOrigin])), + xcm::VersionedAssetId::from(AssetId(Location::here())), + ); + assert!( + fees.is_ok(), + "Moonbase should resolve XCM version for relay destination" + ); + }); +} + +/// Verify that Moonbeam and a sibling negotiate XCM versions via HRMP. +#[test] +fn xcm_version_discovery_with_sibling() { + init_network(); + + moonbase_execute_with(|| register_dot_asset(DOT_ASSET_ID)); + sibling_execute_with(|| register_dot_asset(DOT_ASSET_ID)); + + WestendRelay::::execute_with(|| { + open_hrmp_channels(MOONBASE_PARA_ID, SIBLING_PARA_ID); + }); + + // Register GLMR on sibling so we can do a transfer. + sibling_execute_with(|| register_glmr_foreign_asset(MOONBASE_PARA_ID)); + + let amount = moonbase_runtime::currency::UNIT; + + // Transfer triggers version negotiation between the two parachains. + moonbase_execute_with(|| { + assert_ok!(moonbase_runtime::PolkadotXcm::transfer_assets( + moonbase_runtime::RuntimeOrigin::signed(moonbase_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(SIBLING_PARA_ID)], + ))), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [AccountKey20 { + network: None, + key: BALTATHAR, + }], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::new(0, [PalletInstance(3)])), + fun: Fungible(amount), + }]))), + 0, + WeightLimit::Unlimited, + )); + }); + + // Verify sibling can version-wrap an XCM destined for Moonbase. + // query_delivery_fees calls validate_send → XcmpQueue::validate → + // wrap_version, which requires SupportedVersion or SafeXcmVersion. + sibling_execute_with(|| { + use xcm_runtime_apis::fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV2; + let fees = moonbase_runtime::Runtime::query_delivery_fees( + xcm::VersionedLocation::from(Location::new(1, [Parachain(MOONBASE_PARA_ID)])), + xcm::VersionedXcm::from(Xcm::<()>(vec![ClearOrigin])), + xcm::VersionedAssetId::from(AssetId(Location::here())), + ); + assert!( + fees.is_ok(), + "Sibling should resolve XCM version for Moonbase destination" + ); + }); + + // Verify Moonbase can version-wrap an XCM destined for the sibling. + moonbase_execute_with(|| { + use xcm_runtime_apis::fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV2; + let fees = moonbase_runtime::Runtime::query_delivery_fees( + xcm::VersionedLocation::from(Location::new(1, [Parachain(SIBLING_PARA_ID)])), + xcm::VersionedXcm::from(Xcm::<()>(vec![ClearOrigin])), + xcm::VersionedAssetId::from(AssetId(Location::here())), + ); + assert!( + fees.is_ok(), + "Moonbase should resolve XCM version for sibling destination" + ); + }); +} diff --git a/runtime/moonbase/tests/xcm_mock/mod.rs b/runtime/moonbase/tests/xcm_mock/mod.rs deleted file mode 100644 index c339bc671e0..00000000000 --- a/runtime/moonbase/tests/xcm_mock/mod.rs +++ /dev/null @@ -1,287 +0,0 @@ -// Copyright 2021 Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -pub mod parachain; -pub mod relay_chain; -pub mod statemint_like; -use cumulus_primitives_core::ParaId; -use pallet_xcm_transactor::relay_indices::*; -use sp_runtime::traits::AccountIdConversion; -use sp_runtime::{AccountId32, BuildStorage}; -use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain, TestExt}; - -use polkadot_runtime_parachains::configuration::{ - GenesisConfig as ConfigurationGenesisConfig, HostConfiguration, -}; -use polkadot_runtime_parachains::paras::{ - GenesisConfig as ParasGenesisConfig, ParaGenesisArgs, ParaKind, -}; - -use sp_core::{H160, U256}; -use std::{collections::BTreeMap, str::FromStr}; - -pub const PARAALICE: [u8; 20] = [1u8; 20]; -pub const PARABOB: [u8; 20] = [2u8; 20]; -pub const RELAYALICE: AccountId32 = AccountId32::new([0u8; 32]); -pub const RELAYBOB: AccountId32 = AccountId32::new([2u8; 32]); - -pub fn para_a_account() -> AccountId32 { - ParaId::from(1).into_account_truncating() -} - -pub fn para_b_account() -> AccountId32 { - ParaId::from(2).into_account_truncating() -} - -pub fn para_a_account_20() -> parachain::AccountId { - ParaId::from(1).into_account_truncating() -} - -pub fn evm_account() -> H160 { - H160::from_str("1000000000000000000000000000000000000001").unwrap() -} - -pub fn mock_para_genesis_info() -> ParaGenesisArgs { - ParaGenesisArgs { - genesis_head: vec![1u8].into(), - validation_code: vec![1u8].into(), - para_kind: ParaKind::Parachain, - } -} - -pub fn mock_relay_config() -> HostConfiguration { - HostConfiguration:: { - hrmp_channel_max_capacity: u32::MAX, - hrmp_channel_max_total_size: u32::MAX, - hrmp_max_parachain_inbound_channels: 10, - hrmp_max_parachain_outbound_channels: 10, - hrmp_channel_max_message_size: u32::MAX, - // Changed to avoid arithmetic errors within hrmp_close - max_downward_message_size: 100_000u32, - ..Default::default() - } -} - -pub fn mock_xcm_transactor_storage() -> RelayChainIndices { - RelayChainIndices { - staking: 0u8, - utility: 5u8, - hrmp: 6u8, - bond: 0u8, - bond_extra: 1u8, - unbond: 2u8, - withdraw_unbonded: 3u8, - validate: 4u8, - nominate: 5u8, - chill: 6u8, - set_payee: 7u8, - set_controller: 8u8, - rebond: 19u8, - as_derivative: 1u8, - init_open_channel: 0u8, - accept_open_channel: 1u8, - close_channel: 2u8, - cancel_open_request: 6u8, - } -} - -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_parachain! { - pub struct Statemint { - Runtime = statemint_like::Runtime, - XcmpMessageHandler = statemint_like::MsgQueue, - DmpMessageHandler = statemint_like::MsgQueue, - new_ext = statemint_ext(1000), - } -} - -decl_test_relay_chain! { - pub struct Relay { - Runtime = relay_chain::Runtime, - RuntimeCall = relay_chain::RuntimeCall, - RuntimeEvent = relay_chain::RuntimeEvent, - XcmConfig = relay_chain::XcmConfig, - MessageQueue = relay_chain::MessageQueue, - System = relay_chain::System, - new_ext = relay_ext(vec![1, 2, 3, 1000]), - } -} - -decl_test_network! { - pub struct MockNet { - relay_chain = Relay, - parachains = vec![ - (1, ParaA), - (2, ParaB), - (3, ParaC), - (1000, Statemint), - ], - } -} - -pub const INITIAL_BALANCE: u128 = 10_000_000_000_000_000; - -pub const INITIAL_EVM_BALANCE: u128 = 0; -pub const INITIAL_EVM_NONCE: u32 = 1; - -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![(PARAALICE.into(), INITIAL_BALANCE)], - dev_accounts: None, - } - .assimilate_storage(&mut t) - .unwrap(); - - pallet_xcm_transactor::GenesisConfig:: { - relay_indices: mock_xcm_transactor_storage(), - ..Default::default() - } - .assimilate_storage(&mut t) - .unwrap(); - - // EVM accounts are self-sufficient. - let mut evm_accounts = BTreeMap::new(); - evm_accounts.insert( - evm_account(), - fp_evm::GenesisAccount { - nonce: U256::from(INITIAL_EVM_NONCE), - balance: U256::from(INITIAL_EVM_BALANCE), - storage: Default::default(), - code: vec![ - 0x00, // STOP - ], - }, - ); - - let genesis_config = pallet_evm::GenesisConfig:: { - accounts: evm_accounts, - ..Default::default() - }; - genesis_config.assimilate_storage(&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 statemint_ext(para_id: u32) -> sp_io::TestExternalities { - use statemint_like::{MsgQueue, Runtime, System}; - - let mut t = frame_system::GenesisConfig::::default() - .build_storage() - .unwrap(); - - pallet_balances::GenesisConfig:: { - balances: vec![ - (RELAYALICE.into(), INITIAL_BALANCE), - (RELAYBOB.into(), INITIAL_BALANCE), - ], - dev_accounts: None, - } - .assimilate_storage(&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(paras: Vec) -> sp_io::TestExternalities { - use relay_chain::{Runtime, System}; - - let mut t = frame_system::GenesisConfig::::default() - .build_storage() - .unwrap(); - - pallet_balances::GenesisConfig:: { - balances: vec![(RELAYALICE, INITIAL_BALANCE)], - dev_accounts: None, - } - .assimilate_storage(&mut t) - .unwrap(); - - let para_genesis: Vec<(ParaId, ParaGenesisArgs)> = paras - .iter() - .map(|¶_id| (para_id.into(), mock_para_genesis_info())) - .collect(); - - let genesis_config = ConfigurationGenesisConfig:: { - config: mock_relay_config(), - }; - genesis_config.assimilate_storage(&mut t).unwrap(); - - let genesis_config = ParasGenesisConfig:: { - paras: para_genesis, - ..Default::default() - }; - genesis_config.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 Hrmp = polkadot_runtime_parachains::hrmp::Pallet; - -pub type StatemintBalances = pallet_balances::Pallet; -pub type StatemintChainPalletXcm = pallet_xcm::Pallet; -pub type StatemintAssets = pallet_assets::Pallet; - -pub type ParachainPalletXcm = pallet_xcm::Pallet; - -pub type RelayBalances = pallet_balances::Pallet; -pub type ParaBalances = pallet_balances::Pallet; -pub type XcmTransactor = pallet_xcm_transactor::Pallet; diff --git a/runtime/moonbase/tests/xcm_mock/parachain.rs b/runtime/moonbase/tests/xcm_mock/parachain.rs deleted file mode 100644 index ff3588a564c..00000000000 --- a/runtime/moonbase/tests/xcm_mock/parachain.rs +++ /dev/null @@ -1,1039 +0,0 @@ -// Copyright 2021 Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -//! Parachain runtime mock. - -use frame_support::{ - construct_runtime, ensure, parameter_types, - traits::{ - fungible::NativeOrWithId, ConstU32, EitherOf, Everything, Get, InstanceFilter, Nothing, - PalletInfoAccess, - }, - weights::Weight, - PalletId, -}; - -use frame_system::{pallet_prelude::BlockNumberFor, EnsureRoot}; -use moonbeam_runtime_common::{ - impl_asset_conversion::AssetRateConverter, impl_multiasset_paymaster::MultiAssetPaymaster, - xcm_origins::AllowSiblingParachains, -}; -use pallet_moonbeam_foreign_assets::{MapSuccessToGovernance, MapSuccessToXcm}; -use pallet_xcm::{migration::v1::VersionUncheckedMigrateToV1, EnsureXcm}; -use parity_scale_codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen}; -use sp_core::{H160, H256}; -use sp_runtime::{ - traits::{BlakeTwo256, Hash, IdentityLookup, MaybeEquivalence, Zero}, - Permill, -}; -use sp_std::{convert::TryFrom, prelude::*}; -use xcm::{latest::prelude::*, Version as XcmVersion, VersionedXcm}; - -use cumulus_primitives_core::relay_chain::HrmpChannelId; -use pallet_ethereum::PostLogContent; -use polkadot_core_primitives::BlockNumber as RelayBlockNumber; -use polkadot_parachain::primitives::{Id as ParaId, Sibling}; -use xcm::latest::{ - Error as XcmError, ExecuteXcm, - Junction::{PalletInstance, Parachain}, - Location, NetworkId, Outcome, Xcm, -}; -use xcm_builder::{ - AccountKey20Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, - AllowTopLevelPaidExecutionFrom, Case, EnsureXcmOrigin, FixedWeightBounds, FungibleAdapter, - IsConcrete, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, - SiblingParachainConvertsVia, SignedAccountKey20AsNative, SovereignSignedViaLocation, - TakeWeightCredit, WithComputedOrigin, -}; -use xcm_executor::{Config, XcmExecutor}; - -pub use moonbase_runtime::xcm_config::AssetType; -#[cfg(feature = "runtime-benchmarks")] -use moonbeam_runtime_common::benchmarking::BenchmarkHelper as ArgumentsBenchmarkHelper; -use scale_info::TypeInfo; -use xcm_simulator::{ - DmpMessageHandlerT as DmpMessageHandler, XcmpMessageFormat, - XcmpMessageHandlerT as XcmpMessageHandler, -}; - -pub type AccountId = moonbeam_core_primitives::AccountId; -pub type Balance = u128; -pub type AssetId = u128; -pub type BlockNumber = BlockNumberFor; - -parameter_types! { - pub const BlockHashCount: u32 = 250; -} - -impl frame_system::Config for Runtime { - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type RuntimeTask = RuntimeTask; - type Nonce = u64; - type Block = Block; - type Hash = H256; - type Hashing = ::sp_runtime::traits::BlakeTwo256; - type AccountId = AccountId; - type Lookup = IdentityLookup; - type RuntimeEvent = RuntimeEvent; - 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 = frame_support::traits::ConstU32<16>; - type SingleBlockMigrations = (); - type MultiBlockMigrator = (); - type PreInherents = (); - type PostInherents = (); - type PostTransactions = (); - type ExtensionsWeightInfo = (); -} - -parameter_types! { - pub ExistentialDeposit: Balance = 0; - pub const MaxLocks: u32 = 50; - pub const MaxReserves: u32 = 50; -} - -impl pallet_balances::Config for Runtime { - type MaxLocks = MaxLocks; - type Balance = Balance; - type RuntimeEvent = RuntimeEvent; - type DustRemoval = (); - type ExistentialDeposit = ExistentialDeposit; - type AccountStore = System; - type WeightInfo = (); - type MaxReserves = MaxReserves; - type ReserveIdentifier = [u8; 8]; - type RuntimeHoldReason = (); - type FreezeIdentifier = (); - type MaxFreezes = (); - type RuntimeFreezeReason = (); - type DoneSlashHandler = (); -} - -parameter_types! { - pub const AssetDeposit: Balance = 10; // Does not really matter as this will be only called by root - pub const ApprovalDeposit: Balance = 0; - pub const AssetsStringLimit: u32 = 50; - pub const MetadataDepositBase: Balance = 0; - pub const MetadataDepositPerByte: Balance = 0; - pub const AssetAccountDeposit: Balance = 0; -} - -/// Type for specifying how a `Location` can be converted into an `AccountId`. This is used -/// when determining ownership of accounts for asset transacting and when attempting to use XCM -/// `Transact` in order to determine the dispatch Origin. -pub type LocationToAccountId = ( - // The parent (Relay-chain) origin converts to the default `AccountId`. - ParentIsPreset, - // Sibling parachain origins convert to AccountId via the `ParaId::into`. - SiblingParachainConvertsVia, - AccountKey20Aliases, - // The rest of multilocations convert via hashing it - xcm_builder::HashedDescription< - AccountId, - xcm_builder::DescribeFamily, - >, -); - -/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, -/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can -/// biases the kind of local `Origin` it will become. -pub type XcmOriginToTransactDispatchOrigin = ( - // Sovereign account converter; this attempts to derive an `AccountId` from the origin location - // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for - // foreign chains who want to have a local sovereign account on this chain which they control. - SovereignSignedViaLocation, - // Native converter for Relay-chain (Parent) location; will converts to a `Relay` origin when - // recognised. - RelayChainAsNative, - // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when - // recognised. - SiblingParachainAsNative, - // Superuser converter for the Relay-chain (Parent) location. This will allow it to issue a - // transaction from the Root origin. - ParentAsSuperuser, - // Xcm origins can be represented natively under the Xcm pallet's Xcm origin. - pallet_xcm::XcmPassthrough, - SignedAccountKey20AsNative, -); - -parameter_types! { - pub const UnitWeightCost: Weight = Weight::from_parts(1u64, 1u64); - pub MaxInstructions: u32 = 100; -} - -/// The transactor for our own chain currency. -pub type LocalAssetTransactor = FungibleAdapter< - // Use this currency: - Balances, - // Use this currency when it is a fungible asset matching any of the locations in - // SelfReserveRepresentations - IsConcrete, - // We can convert the Locations with our converter above: - LocationToAccountId, - // Our chain's account ID type (we can't get away without mentioning it explicitly): - AccountId, - // We dont allow teleport - (), ->; - -// These will be our transactors -// We use both transactors -pub type AssetTransactors = (LocalAssetTransactor, EvmForeignAssets); - -pub type XcmRouter = super::ParachainXcmRouter; - -pub type XcmBarrier = ( - // Weight that is paid for may be consumed. - TakeWeightCredit, - // Expected responses are OK. - AllowKnownQueryResponses, - WithComputedOrigin< - ( - // If the message is one that immediately attempts to pay for execution, then allow it. - AllowTopLevelPaidExecutionFrom, - // Subscriptions for version tracking are OK. - AllowSubscriptionsFrom, - ), - UniversalLocation, - ConstU32<8>, - >, -); - -parameter_types! { - /// Xcm fees will go to the treasury account - pub XcmFeesAccount: AccountId = Treasury::account_id(); - /// Parachain token units per second of execution - pub ParaTokensPerSecond: u128 = 1000000000000; -} - -pub struct WeightToFee; -impl sp_weights::WeightToFee for WeightToFee { - type Balance = Balance; - - fn weight_to_fee(weight: &Weight) -> Self::Balance { - use sp_runtime::SaturatedConversion as _; - Self::Balance::saturated_from(weight.ref_time()) - .saturating_mul(ParaTokensPerSecond::get()) - .saturating_div(frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND as u128) - } -} - -parameter_types! { - pub RelayNetwork: NetworkId = moonbase_runtime::xcm_config::RelayNetwork::get(); - pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); - pub UniversalLocation: InteriorLocation = - [GlobalConsensus(RelayNetwork::get()), Parachain(MsgQueue::parachain_id().into())].into(); - - // New Self Reserve location, defines the multilocation identifying the self-reserve currency - // This is used to match it also against our Balances pallet when we receive such - // a Location: (Self Balances pallet index) - pub SelfReserve: Location = Location { - parents:0, - interior: [ - PalletInstance(::index() as u8) - ].into() - }; - pub const MaxAssetsIntoHolding: u32 = 64; - - pub AssetHubLocation: Location = Location::new(1, [Parachain(1000)]); - pub RelayLocationFilter: AssetFilter = Wild(AllOf { - fun: WildFungible, - id: xcm::prelude::AssetId(Location::parent()), - }); - - pub RelayChainNativeAssetFromAssetHub: (AssetFilter, Location) = ( - RelayLocationFilter::get(), - AssetHubLocation::get() - ); -} - -use frame_system::RawOrigin; -use sp_runtime::traits::PostDispatchInfoOf; -use sp_runtime::DispatchErrorWithPostInfo; -use xcm_executor::traits::CallDispatcher; -moonbeam_runtime_common::impl_moonbeam_xcm_call!(); - -type Reserves = ( - // Relaychain (DOT) from Asset Hub - Case, - // Assets which the reserve is the same as the origin. - xcm_primitives::MultiNativeAsset< - xcm_primitives::AbsoluteAndRelativeReserve, - >, -); - -pub struct XcmConfig; -impl Config for XcmConfig { - type RuntimeCall = RuntimeCall; - type XcmSender = XcmRouter; - type AssetTransactor = AssetTransactors; - type OriginConverter = XcmOriginToTransactDispatchOrigin; - type IsReserve = Reserves; - type IsTeleporter = (); - type UniversalLocation = UniversalLocation; - type Barrier = XcmBarrier; - type Weigher = FixedWeightBounds; - type Trader = pallet_xcm_weight_trader::Trader; - - type ResponseHandler = PolkadotXcm; - type SubscriptionService = PolkadotXcm; - type AssetTrap = PolkadotXcm; - type AssetClaims = PolkadotXcm; - type CallDispatcher = MoonbeamCall; - type AssetLocker = (); - type AssetExchanger = (); - type PalletInstancesInfo = (); - type MaxAssetsIntoHolding = MaxAssetsIntoHolding; - type FeeManager = (); - type MessageExporter = (); - type UniversalAliases = Nothing; - type SafeCallFilter = Everything; - type Aliasers = Nothing; - type TransactionalProcessor = (); - type HrmpNewChannelOpenRequestHandler = (); - type HrmpChannelAcceptedHandler = (); - type HrmpChannelClosingHandler = (); - type XcmRecorder = PolkadotXcm; - type XcmEventEmitter = (); -} - -impl cumulus_pallet_xcm::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type XcmExecutor = XcmExecutor; -} - -// Our currencyId. We distinguish for now between SelfReserve, and Others, defined by their Id. -#[derive( - Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo, DecodeWithMemTracking, -)] -pub enum CurrencyId { - SelfReserve, - ForeignAsset(AssetId), -} - -// How to convert from CurrencyId to Location -pub struct CurrencyIdToLocation(sp_std::marker::PhantomData); -impl sp_runtime::traits::Convert> - for CurrencyIdToLocation -where - AssetXConverter: MaybeEquivalence, -{ - fn convert(currency: CurrencyId) -> Option { - match currency { - CurrencyId::SelfReserve => { - // For now and until Xtokens is adapted to handle 0.9.16 version we use - // the old anchoring here - // This is not a problem in either cases, since the view of the destination - // chain does not change - // TODO! change this to NewAnchoringSelfReserve once xtokens is adapted for it - let multi: Location = SelfReserve::get(); - Some(multi) - } - CurrencyId::ForeignAsset(asset) => AssetXConverter::convert_back(&asset), - } - } -} - -parameter_types! { - pub const BaseXcmWeight: Weight = Weight::from_parts(100u64, 100u64); - pub const MaxAssetsForTransfer: usize = 2; - pub SelfLocation: Location = Location::here(); - pub SelfLocationAbsolute: Location = Location { - parents:1, - interior: [ - Parachain(MsgQueue::parachain_id().into()) - ].into() - }; -} - -parameter_types! { - pub const ProposalBond: Permill = Permill::from_percent(5); - pub const ProposalBondMinimum: Balance = 0; - pub const SpendPeriod: u32 = 0; - pub const TreasuryId: PalletId = PalletId(*b"pc/trsry"); - pub const MaxApprovals: u32 = 100; - pub TreasuryAccount: AccountId = Treasury::account_id(); -} - -impl pallet_treasury::Config for Runtime { - type PalletId = TreasuryId; - type Currency = Balances; - type RejectOrigin = EnsureRoot; - type RuntimeEvent = RuntimeEvent; - type SpendPeriod = SpendPeriod; - type Burn = (); - type BurnDestination = (); - type MaxApprovals = MaxApprovals; - type WeightInfo = (); - type SpendFunds = (); - type SpendOrigin = frame_support::traits::NeverEnsureOrigin; // Same as Polkadot - type AssetKind = NativeOrWithId; - type Beneficiary = AccountId; - type BeneficiaryLookup = IdentityLookup; - type Paymaster = MultiAssetPaymaster; - type BalanceConverter = AssetRateConverter; - type PayoutPeriod = ConstU32<0>; - #[cfg(feature = "runtime-benchmarks")] - type BenchmarkHelper = ArgumentsBenchmarkHelper; - type BlockNumberProvider = System; -} - -#[frame_support::pallet] -pub mod mock_msg_queue { - use super::*; - use frame_support::pallet_prelude::*; - - #[pallet::config] - pub trait Config: frame_system::Config { - type XcmExecutor: ExecuteXcm; - } - - #[pallet::call] - impl Pallet {} - - #[pallet::pallet] - pub struct Pallet(_); - - #[pallet::storage] - #[pallet::getter(fn parachain_id)] - pub(super) type ParachainId = StorageValue<_, ParaId, 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, InstructionError), - /// 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 = Location::new(1, [Parachain(sender.into())]); - let mut id = [0u8; 32]; - id.copy_from_slice(hash.as_ref()); - match T::XcmExecutor::prepare_and_execute( - location, - xcm, - &mut id, - max_weight, - Weight::zero(), - ) { - Outcome::Error(error) => { - (Err(error.clone()), Event::Fail(Some(hash), error)) - } - Outcome::Complete { used } => (Ok(used), 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 { used, error } => { - (Ok(used), Event::Fail(Some(hash), error)) - } - } - } - Err(()) => ( - Err(InstructionError { - error: XcmError::UnhandledXcmVersion, - index: 0, - }), - 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 mut 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::prepare_and_execute( - Parent, - x, - &mut id, - limit, - Weight::zero(), - ); - - Self::deposit_event(Event::ExecutedDownward(id, outcome)); - } - } - } - limit - } - } -} - -// 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 {} - - #[pallet::call] - impl Pallet {} - - #[pallet::pallet] - 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)); - } - } -} - -impl mock_msg_queue::Config for Runtime { - type XcmExecutor = XcmExecutor; -} - -impl mock_version_changer::Config for Runtime {} - -pub type LocalOriginToLocation = - xcm_primitives::SignedToAccountId20; - -parameter_types! { - pub MatcherLocation: Location = Location::here(); -} - -impl pallet_xcm::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type SendXcmOrigin = EnsureXcmOrigin; - type XcmRouter = XcmRouter; - type ExecuteXcmOrigin = EnsureXcmOrigin; - type XcmExecuteFilter = frame_support::traits::Nothing; - type XcmExecutor = XcmExecutor; - // Do not allow teleports - type XcmTeleportFilter = Nothing; - type XcmReserveTransferFilter = Everything; - type Weigher = FixedWeightBounds; - type UniversalLocation = UniversalLocation; - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; - // We use a custom one to test runtime upgrades - type AdvertisedXcmVersion = XcmVersioner; - type Currency = Balances; - type CurrencyMatcher = IsConcrete; - type TrustedLockers = (); - type SovereignAccountOf = (); - type MaxLockers = ConstU32<8>; - type WeightInfo = pallet_xcm::TestWeightInfo; - type MaxRemoteLockConsumers = ConstU32<0>; - type RemoteLockConsumerIdentifier = (); - type AdminOrigin = frame_system::EnsureRoot; - type AuthorizedAliasConsideration = Disabled; -} - -#[derive( - Clone, - Default, - Eq, - Debug, - PartialEq, - Ord, - PartialOrd, - Encode, - Decode, - TypeInfo, - DecodeWithMemTracking, -)] -pub struct AssetMetadata { - pub name: Vec, - pub symbol: Vec, - pub decimals: u8, -} - -pub struct AccountIdToH160; -impl sp_runtime::traits::Convert for AccountIdToH160 { - fn convert(account_id: AccountId) -> H160 { - account_id.into() - } -} - -pub type ForeignAssetManagerOrigin = EitherOf< - MapSuccessToXcm>, - MapSuccessToGovernance>, ->; - -moonbeam_runtime_common::impl_evm_runner_precompile_or_eth_xcm!(); - -parameter_types! { - pub ForeignAssetCreationDeposit: u128 = 100 * currency::UNIT; -} - -impl pallet_moonbeam_foreign_assets::Config for Runtime { - type AccountIdToH160 = AccountIdToH160; - type AssetIdFilter = Everything; - type EvmRunner = EvmRunnerPrecompileOrEthXcm; - type ConvertLocation = - SiblingParachainConvertsVia; - type ForeignAssetCreatorOrigin = ForeignAssetManagerOrigin; - type ForeignAssetFreezerOrigin = ForeignAssetManagerOrigin; - type ForeignAssetModifierOrigin = ForeignAssetManagerOrigin; - type ForeignAssetUnfreezerOrigin = ForeignAssetManagerOrigin; - type OnForeignAssetCreated = (); - type MaxForeignAssets = ConstU32<256>; - type WeightInfo = (); - type XcmLocationToH160 = LocationToH160; - type ForeignAssetCreationDeposit = ForeignAssetCreationDeposit; - type Balance = Balance; - type Currency = Balances; -} - -// 1 ROC/WND should be enough -parameter_types! { - pub MaxHrmpRelayFee: Asset = (Location::parent(), 1_000_000_000_000u128).into(); -} - -impl pallet_xcm_transactor::Config for Runtime { - type Balance = Balance; - type Transactor = MockTransactors; - type DerivativeAddressRegistrationOrigin = EnsureRoot; - type SovereignAccountDispatcherOrigin = frame_system::EnsureRoot; - type CurrencyId = CurrencyId; - type AccountIdToLocation = xcm_primitives::AccountIdToLocation; - type CurrencyIdToLocation = CurrencyIdToLocation; - type SelfLocation = SelfLocation; - type Weigher = xcm_builder::FixedWeightBounds; - type UniversalLocation = UniversalLocation; - type XcmSender = XcmRouter; - type BaseXcmWeight = BaseXcmWeight; - type AssetTransactor = AssetTransactors; - type ReserveProvider = xcm_primitives::AbsoluteAndRelativeReserve; - type WeightInfo = (); - type HrmpManipulatorOrigin = EnsureRoot; - type HrmpOpenOrigin = EnsureRoot; - type MaxHrmpFee = xcm_builder::Case; - type FeeTrader = moonbeam_tests_primitives::MemoryFeeTrader; -} - -parameter_types! { - pub RelayLocation: Location = Location::parent(); -} - -impl pallet_xcm_weight_trader::Config for Runtime { - type AccountIdToLocation = xcm_primitives::AccountIdToLocation; - type AddSupportedAssetOrigin = EnsureRoot; - type AssetLocationFilter = Everything; - type AssetTransactor = AssetTransactors; - type Balance = Balance; - type EditSupportedAssetOrigin = EnsureRoot; - type NativeLocation = SelfReserve; - type PauseSupportedAssetOrigin = EnsureRoot; - type RemoveSupportedAssetOrigin = EnsureRoot; - type ResumeSupportedAssetOrigin = EnsureRoot; - type WeightInfo = (); - type WeightToFee = WeightToFee; - type XcmFeesAccount = XcmFeesAccount; - #[cfg(feature = "runtime-benchmarks")] - type NotFilteredLocation = RelayLocation; -} - -parameter_types! { - pub const MinimumPeriod: u64 = 1000; -} -impl pallet_timestamp::Config for Runtime { - type Moment = u64; - type OnTimestampSet = (); - type MinimumPeriod = MinimumPeriod; - type WeightInfo = (); -} - -parameter_types! { - pub BlockGasLimit: U256 = moonbase_runtime::BlockGasLimit::get(); - pub WeightPerGas: Weight = moonbase_runtime::WeightPerGas::get(); - pub const GasLimitPovSizeRatio: u64 = moonbase_runtime::GasLimitPovSizeRatio::get(); - pub GasLimitStorageGrowthRatio: u64 = moonbase_runtime::GasLimitStorageGrowthRatio::get(); -} - -impl pallet_evm::Config for Runtime { - type FeeCalculator = (); - type GasWeightMapping = pallet_evm::FixedGasWeightMapping; - type WeightPerGas = WeightPerGas; - - type CallOrigin = pallet_evm::EnsureAddressRoot; - type WithdrawOrigin = pallet_evm::EnsureAddressNever; - - type AddressMapping = pallet_evm::IdentityAddressMapping; - type Currency = Balances; - type Runner = pallet_evm::runner::stack::Runner; - - type PrecompilesType = (); - type PrecompilesValue = (); - type ChainId = (); - type BlockGasLimit = BlockGasLimit; - type TransactionGasLimit = moonbase_runtime::TransactionGasLimit; - type OnChargeTransaction = (); - type BlockHashMapping = pallet_evm::SubstrateBlockHashMapping; - type FindAuthor = (); - type OnCreate = (); - type GasLimitPovSizeRatio = GasLimitPovSizeRatio; - type GasLimitStorageGrowthRatio = GasLimitStorageGrowthRatio; - type Timestamp = Timestamp; - type WeightInfo = pallet_evm::weights::SubstrateWeight; - type AccountProvider = FrameSystemAccountProvider; - type CreateOriginFilter = (); - type CreateInnerOriginFilter = (); -} - -#[allow(dead_code)] -pub struct NormalFilter; - -impl frame_support::traits::Contains for NormalFilter { - fn contains(c: &RuntimeCall) -> bool { - match c { - _ => true, - } - } -} - -// We need to use the encoding from the relay mock runtime -#[derive(Encode, Decode)] -pub enum RelayCall { - #[codec(index = 5u8)] - // the index should match the position of the module in `construct_runtime!` - Utility(UtilityCall), - #[codec(index = 6u8)] - // the index should match the position of the module in `construct_runtime!` - Hrmp(HrmpCall), -} - -#[derive(Encode, Decode)] -pub enum UtilityCall { - #[codec(index = 1u8)] - AsDerivative(u16), -} - -// HRMP call encoding, needed for xcm transactor pallet -#[derive(Encode, Decode)] -pub enum HrmpCall { - #[codec(index = 0u8)] - InitOpenChannel(ParaId, u32, u32), - #[codec(index = 1u8)] - AcceptOpenChannel(ParaId), - #[codec(index = 2u8)] - CloseChannel(HrmpChannelId), - #[codec(index = 6u8)] - CancelOpenRequest(HrmpChannelId, u32), -} - -#[derive( - Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo, DecodeWithMemTracking, -)] -pub enum MockTransactors { - Relay, -} - -impl xcm_primitives::XcmTransact for MockTransactors { - fn destination(self) -> Location { - match self { - MockTransactors::Relay => Location::parent(), - } - } - - fn utility_pallet_index(&self) -> u8 { - XcmTransactor::relay_indices().utility - } - - fn staking_pallet_index(&self) -> u8 { - XcmTransactor::relay_indices().staking - } -} - -#[allow(dead_code)] -pub struct MockHrmpEncoder; - -impl xcm_primitives::HrmpEncodeCall for MockHrmpEncoder { - fn hrmp_encode_call( - call: xcm_primitives::HrmpAvailableCalls, - ) -> Result, xcm::latest::Error> { - match call { - xcm_primitives::HrmpAvailableCalls::InitOpenChannel(a, b, c) => Ok(RelayCall::Hrmp( - HrmpCall::InitOpenChannel(a.clone(), b.clone(), c.clone()), - ) - .encode()), - xcm_primitives::HrmpAvailableCalls::AcceptOpenChannel(a) => { - Ok(RelayCall::Hrmp(HrmpCall::AcceptOpenChannel(a.clone())).encode()) - } - xcm_primitives::HrmpAvailableCalls::CloseChannel(a) => { - Ok(RelayCall::Hrmp(HrmpCall::CloseChannel(a.clone())).encode()) - } - xcm_primitives::HrmpAvailableCalls::CancelOpenRequest(a, b) => { - Ok(RelayCall::Hrmp(HrmpCall::CancelOpenRequest(a.clone(), b.clone())).encode()) - } - } - } -} - -parameter_types! { - pub const PostBlockAndTxnHashes: PostLogContent = PostLogContent::BlockAndTxnHashes; - pub const AllowUnprotectedTxs: bool = false; -} - -impl pallet_ethereum::Config for Runtime { - type StateRoot = - pallet_ethereum::IntermediateStateRoot<::Version>; - type PostLogContent = PostBlockAndTxnHashes; - type ExtraDataLength = ConstU32<30>; - type AllowUnprotectedTxs = AllowUnprotectedTxs; -} -parameter_types! { - pub ReservedXcmpWeight: Weight = Weight::from_parts(u64::max_value(), 0); -} - -#[derive( - Copy, - Clone, - Eq, - PartialEq, - Ord, - PartialOrd, - Encode, - Decode, - Debug, - MaxEncodedLen, - TypeInfo, - DecodeWithMemTracking, -)] -pub enum ProxyType { - NotAllowed = 0, - Any = 1, -} - -impl pallet_evm_precompile_proxy::EvmProxyCallFilter for ProxyType {} - -impl InstanceFilter for ProxyType { - fn filter(&self, _c: &RuntimeCall) -> bool { - match self { - ProxyType::NotAllowed => false, - ProxyType::Any => true, - } - } - fn is_superset(&self, _o: &Self) -> bool { - false - } -} - -impl Default for ProxyType { - fn default() -> Self { - Self::NotAllowed - } -} - -parameter_types! { - pub const ProxyCost: u64 = 1; -} - -impl pallet_proxy::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type RuntimeCall = RuntimeCall; - type Currency = Balances; - type ProxyType = ProxyType; - type ProxyDepositBase = ProxyCost; - type ProxyDepositFactor = ProxyCost; - type MaxProxies = ConstU32<32>; - type WeightInfo = pallet_proxy::weights::SubstrateWeight; - type MaxPending = ConstU32<32>; - type CallHasher = BlakeTwo256; - type AnnouncementDepositBase = ProxyCost; - type AnnouncementDepositFactor = ProxyCost; - type BlockNumberProvider = (); -} - -pub struct EthereumXcmEnsureProxy; -impl xcm_primitives::EnsureProxy for EthereumXcmEnsureProxy { - fn ensure_ok(delegator: AccountId, delegatee: AccountId) -> Result<(), &'static str> { - // The EVM implicitly contains an Any proxy, so we only allow for "Any" proxies - let def: pallet_proxy::ProxyDefinition = - pallet_proxy::Pallet::::find_proxy( - &delegator, - &delegatee, - Some(ProxyType::Any), - ) - .map_err(|_| "proxy error: expected `ProxyType::Any`")?; - // We only allow to use it for delay zero proxies, as the call will iMmediatly be executed - ensure!(def.delay.is_zero(), "proxy delay is Non-zero`"); - Ok(()) - } -} - -impl pallet_ethereum_xcm::Config for Runtime { - type InvalidEvmTransactionError = pallet_ethereum::InvalidTransactionWrapper; - type ValidatedTransaction = pallet_ethereum::ValidatedTransaction; - type XcmEthereumOrigin = pallet_ethereum_xcm::EnsureXcmEthereumTransaction; - type ReservedXcmpWeight = ReservedXcmpWeight; - type EnsureProxy = EthereumXcmEnsureProxy; - type ControllerOrigin = EnsureRoot; - type ForceOrigin = EnsureRoot; -} - -type Block = frame_system::mocking::MockBlockU32; - -construct_runtime!( - pub enum Runtime { - System: frame_system, - Balances: pallet_balances, - MsgQueue: mock_msg_queue, - XcmVersioner: mock_version_changer, - - PolkadotXcm: pallet_xcm, - CumulusXcm: cumulus_pallet_xcm, - XcmTransactor: pallet_xcm_transactor, - XcmWeightTrader: pallet_xcm_weight_trader, - Treasury: pallet_treasury, - Proxy: pallet_proxy, - - Timestamp: pallet_timestamp, - EVM: pallet_evm, - Ethereum: pallet_ethereum, - EthereumXcm: pallet_ethereum_xcm, - EvmForeignAssets: pallet_moonbeam_foreign_assets, - } -); - -pub(crate) fn para_events() -> Vec { - System::events() - .into_iter() - .map(|r| r.event) - .filter_map(|e| Some(e)) - .collect::>() -} - -use frame_support::traits::{Disabled, OnFinalize, OnInitialize, UncheckedOnRuntimeUpgrade}; -use moonbase_runtime::{currency, xcm_config::LocationToH160}; -use pallet_evm::FrameSystemAccountProvider; - -pub(crate) fn on_runtime_upgrade() { - VersionUncheckedMigrateToV1::::on_runtime_upgrade(); -} - -pub(crate) fn para_roll_to(n: BlockNumber) { - 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/moonbase/tests/xcm_mock/relay_chain.rs b/runtime/moonbase/tests/xcm_mock/relay_chain.rs deleted file mode 100644 index 0a3404fa4ed..00000000000 --- a/runtime/moonbase/tests/xcm_mock/relay_chain.rs +++ /dev/null @@ -1,453 +0,0 @@ -// Copyright 2021 Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -//! Relay chain runtime mock. - -use frame_support::{ - construct_runtime, parameter_types, - traits::{Everything, Nothing, ProcessMessage, ProcessMessageError}, -}; -use frame_system::pallet_prelude::BlockNumberFor; -use sp_core::H256; -use sp_runtime::{ - traits::{ConstU32, IdentityLookup}, - AccountId32, -}; - -use frame_support::weights::{Weight, WeightMeter}; -use polkadot_parachain::primitives::Id as ParaId; -use polkadot_runtime_parachains::{ - configuration, dmp, hrmp, - inclusion::{AggregateMessageOrigin, UmpQueueId}, - origin, paras, shared, -}; -use sp_runtime::transaction_validity::TransactionPriority; -use sp_runtime::Permill; -use xcm::latest::prelude::*; -use xcm_builder::{ - Account32Hash, AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, - AllowTopLevelPaidExecutionFrom, ChildParachainAsNative, ChildParachainConvertsVia, - ChildSystemParachainAsSuperuser, FixedRateOfFungible, FixedWeightBounds, - FungibleAdapter as XcmCurrencyAdapter, IsConcrete, ProcessXcmMessage, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, - WithComputedOrigin, -}; -use xcm_executor::{Config, XcmExecutor}; -pub type AccountId = AccountId32; -pub type Balance = u128; -pub type BlockNumber = BlockNumberFor; - -parameter_types! { - pub const BlockHashCount: u32 = 250; -} - -impl frame_system::Config for Runtime { - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type RuntimeTask = RuntimeTask; - type Nonce = u64; - type Block = Block; - type Hash = H256; - type Hashing = ::sp_runtime::traits::BlakeTwo256; - type AccountId = AccountId; - type Lookup = IdentityLookup; - type RuntimeEvent = RuntimeEvent; - 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>; - type SingleBlockMigrations = (); - type MultiBlockMigrator = (); - type PreInherents = (); - type PostInherents = (); - type PostTransactions = (); - type ExtensionsWeightInfo = (); -} - -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 RuntimeEvent = RuntimeEvent; - type DustRemoval = (); - type ExistentialDeposit = ExistentialDeposit; - type AccountStore = System; - type WeightInfo = (); - type MaxReserves = MaxReserves; - type ReserveIdentifier = [u8; 8]; - type RuntimeHoldReason = (); - type FreezeIdentifier = (); - type MaxFreezes = (); - type RuntimeFreezeReason = (); - type DoneSlashHandler = (); -} - -impl pallet_utility::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type RuntimeCall = RuntimeCall; - type WeightInfo = (); - type PalletsOrigin = OriginCaller; -} - -impl shared::Config for Runtime { - type DisabledValidators = (); -} - -impl configuration::Config for Runtime { - type WeightInfo = configuration::TestWeightInfo; -} - -parameter_types! { - pub KsmLocation: Location = Here.into(); - pub const KusamaNetwork: NetworkId = NetworkId::Kusama; - pub const AnyNetwork: Option = None; - pub UniversalLocation: InteriorLocation = Here; -} - -pub type SovereignAccountOf = ( - ChildParachainConvertsVia, - AccountId32Aliases, - // Not enabled in the relay per se, but we enable it to test - // the transact_through_signed extrinsic - Account32Hash, -); - -pub type LocalAssetTransactor = - XcmCurrencyAdapter, SovereignAccountOf, AccountId, ()>; - -type LocalOriginConverter = ( - SovereignSignedViaLocation, - ChildParachainAsNative, - SignedAccountId32AsNative, - ChildSystemParachainAsSuperuser, -); - -parameter_types! { - pub const BaseXcmWeight: Weight = Weight::from_parts(1000u64, 1000u64); - pub KsmPerSecond: (AssetId, u128, u128) = (AssetId(KsmLocation::get()), 1, 1); - pub const MaxInstructions: u32 = 100; - pub const MaxAssetsIntoHolding: u32 = 64; - pub MatcherLocation: Location = Location::here(); -} - -pub type XcmRouter = super::RelayChainXcmRouter; - -pub type XcmBarrier = ( - // Weight that is paid for may be consumed. - TakeWeightCredit, - // Expected responses are OK. - AllowKnownQueryResponses, - WithComputedOrigin< - ( - // If the message is one that immediately attemps to pay for execution, then allow it. - AllowTopLevelPaidExecutionFrom, - // Subscriptions for version tracking are OK. - AllowSubscriptionsFrom, - ), - UniversalLocation, - ConstU32<8>, - >, -); - -parameter_types! { - pub Kusama: AssetFilter = Wild(AllOf { fun: WildFungible, id: AssetId(KsmLocation::get()) }); - pub Statemine: Location = Parachain(1000).into(); - pub KusamaForStatemine: (AssetFilter, Location) = (Kusama::get(), Statemine::get()); -} - -pub type TrustedTeleporters = xcm_builder::Case; - -pub struct XcmConfig; -impl Config for XcmConfig { - type RuntimeCall = RuntimeCall; - type XcmSender = XcmRouter; - type AssetTransactor = LocalAssetTransactor; - type OriginConverter = LocalOriginConverter; - type IsReserve = (); - type IsTeleporter = TrustedTeleporters; - type UniversalLocation = UniversalLocation; - type Barrier = XcmBarrier; - type Weigher = FixedWeightBounds; - type Trader = FixedRateOfFungible; - type ResponseHandler = XcmPallet; - type AssetTrap = XcmPallet; - type AssetClaims = XcmPallet; - type SubscriptionService = XcmPallet; - type CallDispatcher = RuntimeCall; - type AssetLocker = (); - type AssetExchanger = (); - type PalletInstancesInfo = (); - type MaxAssetsIntoHolding = MaxAssetsIntoHolding; - type FeeManager = (); - type MessageExporter = (); - type UniversalAliases = Nothing; - type SafeCallFilter = Everything; - type Aliasers = Nothing; - type TransactionalProcessor = (); - type HrmpNewChannelOpenRequestHandler = (); - type HrmpChannelAcceptedHandler = (); - type HrmpChannelClosingHandler = (); - type XcmRecorder = XcmPallet; - type XcmEventEmitter = (); -} - -pub type LocalOriginToLocation = SignedToAccountId32; - -impl pallet_xcm::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - 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 UniversalLocation = UniversalLocation; - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; - type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; - type Currency = Balances; - type CurrencyMatcher = (); - type TrustedLockers = (); - type SovereignAccountOf = (); - type MaxLockers = ConstU32<8>; - type WeightInfo = pallet_xcm::TestWeightInfo; - type MaxRemoteLockConsumers = ConstU32<0>; - type RemoteLockConsumerIdentifier = (); - type AdminOrigin = frame_system::EnsureRoot; - type AuthorizedAliasConsideration = Disabled; -} - -parameter_types! { - pub const FirstMessageFactorPercent: u64 = 100; -} - -parameter_types! { - pub const ParasUnsignedPriority: TransactionPriority = TransactionPriority::max_value(); -} - -/// A very dumb implementation of `EstimateNextSessionRotation`. At the moment of writing, this -/// is more to satisfy type requirements rather than to test anything. -pub struct TestNextSessionRotation; - -impl frame_support::traits::EstimateNextSessionRotation for TestNextSessionRotation { - fn average_session_length() -> u32 { - 10 - } - - fn estimate_current_session_progress(_now: u32) -> (Option, Weight) { - (None, Weight::zero()) - } - - fn estimate_next_session_rotation(_now: u32) -> (Option, Weight) { - (None, Weight::zero()) - } -} - -impl paras::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = paras::TestWeightInfo; - type UnsignedPriority = ParasUnsignedPriority; - type NextSessionRotation = TestNextSessionRotation; - type QueueFootprinter = (); - type OnNewHead = (); - type AssignCoretime = (); - type Fungible = (); - type CooldownRemovalMultiplier = (); - type AuthorizeCurrentCodeOrigin = frame_system::EnsureRoot; -} - -impl dmp::Config for Runtime {} - -parameter_types! { - pub const DefaultChannelSizeAndCapacityWithSystem: (u32, u32) = (4, 1); -} - -impl hrmp::Config for Runtime { - type RuntimeOrigin = RuntimeOrigin; - type RuntimeEvent = RuntimeEvent; - type Currency = Balances; - type WeightInfo = TestHrmpWeightInfo; - type ChannelManager = frame_system::EnsureRoot; - type DefaultChannelSizeAndCapacityWithSystem = DefaultChannelSizeAndCapacityWithSystem; - type VersionWrapper = XcmPallet; -} - -impl frame_system::offchain::CreateTransactionBase for Runtime -where - RuntimeCall: From, -{ - type Extrinsic = UncheckedExtrinsic; - type RuntimeCall = RuntimeCall; -} - -impl origin::Config for Runtime {} - -type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; -type Block = frame_system::mocking::MockBlockU32; - -impl frame_system::offchain::CreateInherent for Runtime -where - RuntimeCall: From, -{ - fn create_inherent(call: RuntimeCall) -> UncheckedExtrinsic { - UncheckedExtrinsic::new_bare(call) - } - - fn create_bare(call: RuntimeCall) -> UncheckedExtrinsic { - UncheckedExtrinsic::new_bare(call) - } -} - -parameter_types! { - pub MessageQueueServiceWeight: Weight = Weight::from_parts(1_000_000_000, 1_000_000); - pub const MessageQueueHeapSize: u32 = 65_536; - pub const MessageQueueMaxStale: u32 = 16; -} - -pub struct MessageProcessor; -impl ProcessMessage for MessageProcessor { - type Origin = AggregateMessageOrigin; - - fn process_message( - message: &[u8], - origin: Self::Origin, - meter: &mut WeightMeter, - id: &mut [u8; 32], - ) -> Result { - let para = match origin { - AggregateMessageOrigin::Ump(UmpQueueId::Para(para)) => para, - }; - ProcessXcmMessage::, RuntimeCall>::process_message( - message, - Junction::Parachain(para.into()), - meter, - id, - ) - } -} - -impl pallet_message_queue::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Size = u32; - type HeapSize = MessageQueueHeapSize; - type MaxStale = MessageQueueMaxStale; - type ServiceWeight = MessageQueueServiceWeight; - type MessageProcessor = MessageProcessor; - type QueueChangeHandler = (); - type WeightInfo = (); - type QueuePausedQuery = (); - type IdleMaxServiceWeight = MessageQueueServiceWeight; -} - -construct_runtime!( - pub enum Runtime { - System: frame_system, - Balances: pallet_balances, - ParasOrigin: origin, - MessageQueue: pallet_message_queue, - XcmPallet: pallet_xcm, - Utility: pallet_utility, - Hrmp: hrmp, - Dmp: dmp, - Paras: paras, - Configuration: configuration, - } -); - -pub(crate) fn relay_events() -> Vec { - System::events() - .into_iter() - .map(|r| r.event) - .filter_map(|e| Some(e)) - .collect::>() -} - -use frame_support::traits::{Disabled, OnFinalize, OnInitialize}; - -pub(crate) fn relay_roll_to(n: BlockNumber) { - 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()); - } -} - -/// A weight info that is only suitable for testing. -pub struct TestHrmpWeightInfo; - -impl hrmp::WeightInfo for TestHrmpWeightInfo { - fn hrmp_accept_open_channel() -> Weight { - Weight::from_parts(1, 0) - } - fn force_clean_hrmp(_: u32, _: u32) -> Weight { - Weight::from_parts(1, 0) - } - fn force_process_hrmp_close(_: u32) -> Weight { - Weight::from_parts(1, 0) - } - fn force_process_hrmp_open(_: u32) -> Weight { - Weight::from_parts(1, 0) - } - fn hrmp_cancel_open_request(_: u32) -> Weight { - Weight::from_parts(1, 0) - } - fn hrmp_close_channel() -> Weight { - Weight::from_parts(1, 0) - } - fn hrmp_init_open_channel() -> Weight { - Weight::from_parts(1, 0) - } - fn clean_open_channel_requests(_: u32) -> Weight { - Weight::from_parts(1, 0) - } - fn force_open_hrmp_channel(_: u32) -> Weight { - Weight::from_parts(1, 0) - } - fn establish_system_channel() -> Weight { - Weight::from_parts(1, 0) - } - - fn poke_channel_deposits() -> Weight { - Weight::from_parts(1, 0) - } - - fn establish_channel_with_system() -> Weight { - Weight::from_parts(1, 0) - } -} diff --git a/runtime/moonbase/tests/xcm_mock/statemint_like.rs b/runtime/moonbase/tests/xcm_mock/statemint_like.rs deleted file mode 100644 index deb5bfe66ee..00000000000 --- a/runtime/moonbase/tests/xcm_mock/statemint_like.rs +++ /dev/null @@ -1,614 +0,0 @@ -// Copyright 2021 Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -//! Relay chain runtime mock. - -use frame_support::traits::Disabled; -use frame_support::{ - construct_runtime, parameter_types, - traits::{AsEnsureOriginWithArg, Contains, ContainsPair, Everything, Get, Nothing}, - weights::Weight, -}; -use frame_system::{EnsureRoot, EnsureSigned}; -use polkadot_core_primitives::BlockNumber as RelayBlockNumber; -use sp_core::H256; -use sp_runtime::{ - traits::{ConstU32, Hash, IdentityLookup}, - AccountId32, -}; - -use polkadot_parachain::primitives::Id as ParaId; -use polkadot_parachain::primitives::Sibling; -use sp_std::convert::TryFrom; -use xcm::latest::prelude::*; -use xcm::VersionedXcm; -use xcm_builder::{ - AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, - AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, - ConvertedConcreteId, EnsureXcmOrigin, FixedRateOfFungible, FixedWeightBounds, FungibleAdapter, - FungiblesAdapter, IsConcrete, NoChecking, ParentAsSuperuser, ParentIsPreset, - RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, -}; -use xcm_executor::{traits::JustTry, Config, XcmExecutor}; -use xcm_simulator::{ - DmpMessageHandlerT as DmpMessageHandler, XcmpMessageFormat, - XcmpMessageHandlerT as XcmpMessageHandler, -}; -pub type AccountId = AccountId32; -pub type Balance = u128; -pub type AssetId = u128; -pub type ReserveId = u128; - -parameter_types! { - pub const BlockHashCount: u32 = 250; -} - -impl frame_system::Config for Runtime { - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type RuntimeTask = RuntimeTask; - type Nonce = u64; - type Block = Block; - type Hash = H256; - type Hashing = ::sp_runtime::traits::BlakeTwo256; - type AccountId = AccountId; - type Lookup = IdentityLookup; - type RuntimeEvent = RuntimeEvent; - 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 = frame_support::traits::ConstU32<16>; - type SingleBlockMigrations = (); - type MultiBlockMigrator = (); - type PreInherents = (); - type PostInherents = (); - type PostTransactions = (); - type ExtensionsWeightInfo = (); -} - -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 RuntimeEvent = RuntimeEvent; - type DustRemoval = (); - type ExistentialDeposit = ExistentialDeposit; - type AccountStore = System; - type WeightInfo = (); - type MaxReserves = MaxReserves; - type ReserveIdentifier = [u8; 8]; - type RuntimeHoldReason = (); - type FreezeIdentifier = (); - type MaxFreezes = (); - type RuntimeFreezeReason = (); - type DoneSlashHandler = (); -} - -// Required for runtime benchmarks -pallet_assets::runtime_benchmarks_enabled! { - pub struct BenchmarkHelper; - impl pallet_assets::BenchmarkHelper for BenchmarkHelper - where - AssetIdParameter: From, - ReserveIdParameter: From, - { - fn create_asset_id_parameter(id: u32) -> AssetIdParameter { - (id as u128).into() - } - fn create_reserve_id_parameter(id: u32) -> ReserveIdParameter { - (id as u128).into() - } - } -} - -parameter_types! { - pub const AssetDeposit: Balance = 0; // 1 UNIT deposit to create asset - pub const ApprovalDeposit: Balance = 0; - pub const AssetsStringLimit: u32 = 50; - /// Key = 32 bytes, Value = 36 bytes (32+1+1+1+1) - // https://github.com/paritytech/substrate/blob/069917b/frame/assets/src/lib.rs#L257L271 - pub const MetadataDepositBase: Balance = 0; - pub const MetadataDepositPerByte: Balance = 0; - pub const ExecutiveBody: BodyId = BodyId::Executive; - pub const AssetAccountDeposit: Balance = 0; -} - -impl pallet_assets::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Balance = Balance; - type AssetId = AssetId; - type Currency = Balances; - type ForceOrigin = EnsureRoot; - type AssetDeposit = AssetDeposit; - type MetadataDepositBase = MetadataDepositBase; - type MetadataDepositPerByte = MetadataDepositPerByte; - type ApprovalDeposit = ApprovalDeposit; - type StringLimit = AssetsStringLimit; - type Freezer = (); - type Extra = (); - type AssetAccountDeposit = AssetAccountDeposit; - type WeightInfo = (); - type RemoveItemsLimit = ConstU32<656>; - type AssetIdParameter = AssetId; - type ReserveData = ReserveId; - type CreateOrigin = AsEnsureOriginWithArg>; - type CallbackHandle = (); - type Holder = (); - pallet_assets::runtime_benchmarks_enabled! { - type BenchmarkHelper = BenchmarkHelper; - } -} - -parameter_types! { - pub const KsmLocation: Location = Location::parent(); - pub const RelayNetwork: NetworkId = NetworkId::Kusama; - pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); - pub UniversalLocation: InteriorLocation = - [GlobalConsensus(RelayNetwork::get()), Parachain(MsgQueue::parachain_id().into())].into(); - pub Local: Location = Here.into(); - pub CheckingAccount: AccountId = PolkadotXcm::check_account(); - pub KsmPerSecond: (xcm::latest::prelude::AssetId, u128, u128) = - (AssetId(KsmLocation::get()), 1, 1); -} - -/// Type for specifying how a `Location` can be converted into an `AccountId`. This is used -/// when determining ownership of accounts for asset transacting and when attempting to use XCM -/// `Transact` in order to determine the dispatch Origin. -pub type LocationToAccountId = ( - // The parent (Relay-chain) origin converts to the default `AccountId`. - ParentIsPreset, - // Sibling parachain origins convert to AccountId via the `ParaId::into`. - SiblingParachainConvertsVia, - // Straight up local `AccountId32` origins just alias directly to `AccountId`. - AccountId32Aliases, -); - -/// Means for transacting the native currency on this chain. -pub type CurrencyTransactor = FungibleAdapter< - // Use this currency: - Balances, - // Use this currency when it is a fungible asset matching the given location or name: - IsConcrete, - // Convert an XCM Location into a local account id: - LocationToAccountId, - // Our chain's account ID type (we can't get away without mentioning it explicitly): - AccountId, - // We don't track any teleports of `Balances`. - (), ->; - -/// Means for transacting assets besides the native currency on this chain. -pub type FungiblesTransactor = FungiblesAdapter< - // Use this fungibles implementation: - Assets, - // Use this currency when it is a fungible asset matching the given location or name: - ConvertedConcreteId< - AssetId, - Balance, - AsPrefixedGeneralIndex, - JustTry, - >, - // Convert an XCM Location into a local account id: - LocationToAccountId, - // Our chain's account ID type (we can't get away without mentioning it explicitly): - AccountId, - // We only want to allow teleports of known assets. We use non-zero issuance as an indication - // that this asset is known. - NoChecking, - // The account to use for tracking teleports. - CheckingAccount, ->; -/// Means for transacting assets on this chain. -pub type AssetTransactors = (CurrencyTransactor, FungiblesTransactor); - -/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, -/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can -/// biases the kind of local `Origin` it will become. -pub type XcmOriginToTransactDispatchOrigin = ( - // Sovereign account converter; this attempts to derive an `AccountId` from the origin location - // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for - // foreign chains who want to have a local sovereign account on this chain which they control. - SovereignSignedViaLocation, - // Native converter for Relay-chain (Parent) location; will convert to a `Relay` origin when - // recognised. - RelayChainAsNative, - // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when - // recognised. - SiblingParachainAsNative, - // Superuser converter for the Relay-chain (Parent) location. This will allow it to issue a - // transaction from the Root origin. - ParentAsSuperuser, - // Native signed account converter; this just converts an `AccountId32` origin into a normal - // `RuntimeOrigin::signed` origin of the same 32-byte value. - SignedAccountId32AsNative, - // Xcm origins can be represented natively under the Xcm pallet's Xcm origin. - pallet_xcm::XcmPassthrough, -); - -parameter_types! { - // One XCM operation is 1_000_000_000 weight - almost certainly a conservative estimate. - pub UnitWeightCost: Weight = Weight::from_parts(100u64, 100u64); - pub const MaxInstructions: u32 = 100; -} - -pub struct ParentOrParentsExecutivePlurality; -impl Contains for ParentOrParentsExecutivePlurality { - fn contains(location: &Location) -> bool { - matches!( - location.unpack(), - (1, []) - | ( - 1, - [Plurality { - id: BodyId::Executive, - .. - }] - ) - ) - } -} - -pub struct ParentOrSiblings; -impl Contains for ParentOrSiblings { - fn contains(location: &Location) -> bool { - matches!(location.unpack(), (1, []) | (1, [_])) - } -} - -pub type Barrier = ( - TakeWeightCredit, - AllowTopLevelPaidExecutionFrom, - // Parent and its exec plurality get free execution - AllowUnpaidExecutionFrom, - // Expected responses are OK. - AllowKnownQueryResponses, - // Subscriptions for version tracking are OK. - AllowSubscriptionsFrom, -); - -parameter_types! { - pub MatcherLocation: Location = Location::here(); - pub const MaxAssetsIntoHolding: u32 = 64; - pub const RelayTokenLocation: Location = Location::parent(); -} - -// Copied from: -// -// https://github.com/paritytech/polkadot-sdk/blob/f4eb41773611008040c9d4d8a8e6b7323eccfca1/cumulus -// /parachains/common/src/xcm_config.rs#L118 -// -// The difference with the original "ConcreteAssetFromSystem" (which is used by AssetHub), -// is that in our tests we only need to check if the asset matches the relay one. -pub struct ConcreteAssetFromRelay(sp_std::marker::PhantomData); -impl> ContainsPair - for ConcreteAssetFromRelay -{ - fn contains(asset: &Asset, origin: &Location) -> bool { - let is_relay = match origin.unpack() { - // The Relay Chain - (1, []) => true, - // Others - _ => false, - }; - asset.id.0 == AssetLocation::get() && is_relay - } -} - -pub type TrustedTeleporters = (ConcreteAssetFromRelay,); - -pub struct XcmConfig; -impl Config for XcmConfig { - type RuntimeCall = RuntimeCall; - type XcmSender = XcmRouter; - type AssetTransactor = AssetTransactors; - type OriginConverter = XcmOriginToTransactDispatchOrigin; - type IsReserve = xcm_primitives::MultiNativeAsset; - type IsTeleporter = TrustedTeleporters; - type UniversalLocation = UniversalLocation; - type Barrier = Barrier; - type Weigher = FixedWeightBounds; - type Trader = FixedRateOfFungible; - type ResponseHandler = PolkadotXcm; - type AssetTrap = PolkadotXcm; - type AssetClaims = PolkadotXcm; - type SubscriptionService = PolkadotXcm; - type CallDispatcher = RuntimeCall; - type AssetLocker = (); - type AssetExchanger = (); - type PalletInstancesInfo = (); - type MaxAssetsIntoHolding = MaxAssetsIntoHolding; - type FeeManager = (); - type MessageExporter = (); - type UniversalAliases = Nothing; - type SafeCallFilter = Everything; - type Aliasers = Nothing; - type TransactionalProcessor = (); - type HrmpNewChannelOpenRequestHandler = (); - type HrmpChannelAcceptedHandler = (); - type HrmpChannelClosingHandler = (); - type XcmRecorder = PolkadotXcm; - type XcmEventEmitter = PolkadotXcm; -} - -/// No local origins on this chain are allowed to dispatch XCM sends/executions. -pub type LocalOriginToLocation = SignedToAccountId32; - -pub type XcmRouter = super::ParachainXcmRouter; - -impl pallet_xcm::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type SendXcmOrigin = EnsureXcmOrigin; - type XcmRouter = XcmRouter; - type ExecuteXcmOrigin = EnsureXcmOrigin; - type XcmExecuteFilter = Nothing; - type XcmExecutor = XcmExecutor; - type XcmTeleportFilter = Everything; - type XcmReserveTransferFilter = Everything; - type Weigher = FixedWeightBounds; - type UniversalLocation = UniversalLocation; - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; - type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; - type Currency = Balances; - type CurrencyMatcher = IsConcrete; - type TrustedLockers = (); - type SovereignAccountOf = (); - type MaxLockers = ConstU32<8>; - type WeightInfo = pallet_xcm::TestWeightInfo; - type MaxRemoteLockConsumers = ConstU32<0>; - type RemoteLockConsumerIdentifier = (); - type AdminOrigin = frame_system::EnsureRoot; - type AuthorizedAliasConsideration = Disabled; -} - -impl cumulus_pallet_xcm::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type XcmExecutor = XcmExecutor; -} - -#[frame_support::pallet] -pub mod mock_msg_queue { - use super::*; - use frame_support::pallet_prelude::*; - - #[pallet::config] - pub trait Config: frame_system::Config { - type XcmExecutor: ExecuteXcm; - } - - #[pallet::call] - impl Pallet {} - - #[pallet::pallet] - pub struct Pallet(_); - - #[pallet::storage] - #[pallet::getter(fn parachain_id)] - pub(super) type ParachainId = StorageValue<_, ParaId, 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, InstructionError), - /// 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 = Location::new(1, [Parachain(sender.into())]); - let mut id = [0u8; 32]; - id.copy_from_slice(hash.as_ref()); - match T::XcmExecutor::prepare_and_execute( - location, - xcm, - &mut id, - max_weight, - Weight::zero(), - ) { - Outcome::Error(error) => { - (Err(error.clone()), Event::Fail(Some(hash), error)) - } - Outcome::Complete { used } => (Ok(used), 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 { used, error } => { - (Ok(used), Event::Fail(Some(hash), error)) - } - } - } - Err(()) => ( - Err(InstructionError { - error: XcmError::UnhandledXcmVersion, - index: 0, - }), - 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 mut 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::prepare_and_execute( - Parent, - x, - &mut id, - limit, - Weight::zero(), - ); - - Self::deposit_event(Event::ExecutedDownward(id, outcome)); - } - } - } - limit - } - } -} -impl mock_msg_queue::Config for Runtime { - type XcmExecutor = XcmExecutor; -} - -// Pallet to cover test cases for change https://github.com/paritytech/cumulus/pull/831 -#[frame_support::pallet] -pub mod mock_statemint_prefix { - use super::*; - use frame_support::pallet_prelude::*; - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::call] - impl Pallet {} - - #[pallet::pallet] - #[pallet::without_storage_info] - pub struct Pallet(_); - - #[pallet::storage] - #[pallet::getter(fn current_prefix)] - pub(super) type CurrentPrefix = StorageValue<_, Location, ValueQuery>; - - impl Get for Pallet { - fn get() -> Location { - Self::current_prefix() - } - } - - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event { - // Changed Prefix - PrefixChanged(Location), - } - - impl Pallet { - pub fn set_prefix(prefix: Location) { - CurrentPrefix::::put(&prefix); - Self::deposit_event(Event::PrefixChanged(prefix)); - } - } -} - -impl mock_statemint_prefix::Config for Runtime {} - -type Block = frame_system::mocking::MockBlockU32; -construct_runtime!( - pub enum Runtime { - System: frame_system, - Balances: pallet_balances, - PolkadotXcm: pallet_xcm, - CumulusXcm: cumulus_pallet_xcm, - MsgQueue: mock_msg_queue, - Assets: pallet_assets, - PrefixChanger: mock_statemint_prefix, - - } -); diff --git a/runtime/moonbase/tests/xcm_tests.rs b/runtime/moonbase/tests/xcm_tests.rs deleted file mode 100644 index f22ddf6852f..00000000000 --- a/runtime/moonbase/tests/xcm_tests.rs +++ /dev/null @@ -1,5560 +0,0 @@ -// Copyright 2019-2025 PureStake Inc. -// This file is part of Moonbeam. - -// Moonbeam 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. - -// Moonbeam 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 Moonbeam. If not, see . - -//! Moonbase Runtime Xcm Tests - -mod xcm_mock; -use frame_support::{ - assert_ok, - traits::{ConstU32, PalletInfo, PalletInfoAccess}, - weights::constants::WEIGHT_REF_TIME_PER_SECOND, - weights::Weight, - BoundedVec, -}; -use moonbase_runtime::xcm_config::AssetType; -use pallet_xcm_transactor::{ - Currency, CurrencyPayment, HrmpInitParams, HrmpOperation, TransactWeights, -}; -use sp_runtime::traits::Convert; -use sp_std::boxed::Box; -use xcm::{ - latest::prelude::{ - AccountId32, AccountKey20, All, Asset, AssetId, Assets as XcmAssets, BuyExecution, - ClearOrigin, DepositAsset, Fungibility, GeneralIndex, Junction, Junctions, Limited, - Location, OriginKind, PalletInstance, Parachain, QueryResponse, Reanchorable, Response, - WeightLimit, Wild, WithdrawAsset, Xcm, - }, - VersionedAssetId, VersionedAssets, VersionedXcm, -}; -use xcm::{IntoVersion, VersionedLocation, WrapVersion}; -use xcm_executor::traits::ConvertLocation; -use xcm_executor::traits::TransferType; -use xcm_mock::*; -use xcm_primitives::{ - split_location_into_chain_part_and_beneficiary, UtilityEncodeCall, DEFAULT_PROOF_SIZE, -}; -use xcm_simulator::TestExt; -mod common; -use cumulus_primitives_core::relay_chain::HrmpChannelId; -use parachain::PolkadotXcm; - -fn add_supported_asset(asset_type: parachain::AssetType, units_per_second: u128) -> Result<(), ()> { - let parachain::AssetType::Xcm(location_v3) = asset_type; - let VersionedLocation::V5(location_v5) = VersionedLocation::V3(location_v3) - .into_version(xcm::latest::VERSION) - .map_err(|_| ())? - else { - return Err(()); - }; - use frame_support::weights::WeightToFee as _; - let native_amount_per_second: u128 = - ::WeightToFee::weight_to_fee( - &Weight::from_parts( - frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND, - 0, - ), - ) - .try_into() - .map_err(|_| ())?; - let precision_factor = 10u128.pow(pallet_xcm_weight_trader::RELATIVE_PRICE_DECIMALS); - let relative_price: u128 = if units_per_second > 0u128 { - native_amount_per_second - .saturating_mul(precision_factor) - .saturating_div(units_per_second) - } else { - 0u128 - }; - pallet_xcm_weight_trader::SupportedAssets::::insert( - location_v5, - (true, relative_price), - ); - Ok(()) -} - -/// Helper function to set fee per second for an asset location (for compatibility with old tests). -/// Converts fee_per_second to relative_price and adds/edits the asset in the weight-trader. -fn set_fee_per_second_for_location(location: Location, fee_per_second: u128) -> Result<(), ()> { - use moonbeam_tests_primitives::MemoryFeeTrader; - use xcm_primitives::XcmFeeTrader; - - // In tests, we configure fees for XcmTransactor via the in-memory fee trader - // instead of pallet-xcm-weight-trader, so that generic XCM funding transfers - // remain free (as on master) and only the transactor calls are charged. - let precision_factor = 10u128.pow(moonbeam_tests_primitives::RELATIVE_PRICE_DECIMALS); - let native_amount_per_second = - frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND as u128; - let relative_price = native_amount_per_second - .saturating_mul(precision_factor) - .checked_div(fee_per_second) - .ok_or(())?; - - ::set_asset_price(location, relative_price).map_err(|_| ()) -} - -fn currency_to_asset(currency_id: parachain::CurrencyId, amount: u128) -> Asset { - Asset { - id: AssetId( - ::CurrencyIdToLocation::convert( - currency_id, - ) - .unwrap(), - ), - fun: Fungibility::Fungible(amount), - } -} - -// Send a relay asset (like DOT) to a parachain A -#[test] -fn receive_relay_asset_from_relay() { - MockNet::reset(); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - // register relay asset in parachain A - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 0)); - }); - - // Actually send relay asset to parachain - let dest: Location = AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - Relay::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([], 123).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - // Verify that parachain received the asset - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(123)) - ); - }); -} - -// Send relay asset (like DOT) back from Parachain A to relaychain -#[test] -fn send_relay_asset_to_relay() { - MockNet::reset(); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - // Register relay asset in paraA - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - // Free execution - assert_ok!(add_supported_asset(source_location, 0)); - }); - - let dest: Location = Junction::AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - - // First send relay chain asset to Parachain like in previous test - Relay::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([], 123).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - ParaA::execute_with(|| { - // Free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(123)) - ); - }); - - // Lets gather the balance before sending back money - let mut balance_before_sending = 0; - Relay::execute_with(|| { - balance_before_sending = RelayBalances::free_balance(&RELAYALICE); - }); - - // We now send back some money to the relay - let dest = Location { - parents: 1, - interior: [AccountId32 { - network: None, - id: RELAYALICE.into(), - }] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_id), 123); - // free execution, full amount received - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary, - }]); - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from(asset)), - Box::new(TransferType::DestinationReserve), - Box::new(fees_id), - Box::new(TransferType::DestinationReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) - )); - }); - - // The balances in paraAlice should have been substracted - ParaA::execute_with(|| { - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(0)) - ); - }); - - // Balances in the relay should have been received - Relay::execute_with(|| { - // Free execution,x full amount received - assert!(RelayBalances::free_balance(&RELAYALICE) > balance_before_sending); - }); -} - -#[test] -fn send_relay_asset_to_para_b() { - MockNet::reset(); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - // Register asset in paraA. Free execution - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.clone().try_into().expect("too long"), - asset_metadata.name.clone().try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 0)); - }); - - // Register asset in paraB. Free execution - ParaB::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.clone().try_into().expect("too long"), - asset_metadata.name.clone().try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location, 0)); - }); - - // First send relay chain asset to Parachain A like in previous test - let dest: Location = Junction::AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - Relay::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([], 123).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - ParaA::execute_with(|| { - // Free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(123)) - ); - }); - - // Now send relay asset from para A to para B - let dest = Location { - parents: 1, - interior: [ - Parachain(2), - AccountKey20 { - network: None, - key: PARAALICE.into(), - }, - ] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_id), 100); - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary, - }]); - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from(vec![asset])), - Box::new(TransferType::RemoteReserve(Location::parent().into())), - Box::new(fees_id), - Box::new(TransferType::RemoteReserve(Location::parent().into())), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) - )); - }); - - // Para A balances should have been subtracted - ParaA::execute_with(|| { - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(23)) - ); - }); - - // Para B balances should have been credited - ParaB::execute_with(|| { - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(100)) - ); - }); -} - -#[test] -fn send_para_a_asset_to_para_b() { - MockNet::reset(); - - // this represents the asset in paraA - let para_a_balances = Location::new(1, [Parachain(1), PalletInstance(1u8)]); - let source_location: AssetType = para_a_balances - .try_into() - .expect("Location convertion to AssetType should succeed"); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"ParaAToken".to_vec(), - symbol: b"ParaA".to_vec(), - decimals: 18, - }; - - // Register asset in paraB. Free execution - ParaB::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location, 0)); - }); - - // Send para A asset from para A to para B - let dest = Location { - parents: 1, - interior: [ - Parachain(2), - AccountKey20 { - network: None, - key: PARAALICE.into(), - }, - ] - .into(), - }; - - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::SelfReserve, 100); - // Free execution, full amount received - assert_ok!(PolkadotXcm::transfer_assets( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedLocation::from(beneficiary)), - Box::new(VersionedAssets::from(vec![asset])), - 0, - WeightLimit::Limited(Weight::from_parts(800000u64, DEFAULT_PROOF_SIZE)) - )); - }); - - // Native token is substracted in paraA - ParaA::execute_with(|| { - assert_eq!( - ParaBalances::free_balance(&PARAALICE.into()), - INITIAL_BALANCE - 100 - ); - }); - - // Asset is minted in paraB - ParaB::execute_with(|| { - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(100)) - ); - }); -} - -#[test] -fn send_para_a_asset_from_para_b_to_para_c() { - MockNet::reset(); - - // Represents para A asset - let para_a_balances = Location::new(1, [Parachain(1), PalletInstance(1u8)]); - let source_location: AssetType = para_a_balances - .try_into() - .expect("Location convertion to AssetType should succeed"); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"ParaAToken".to_vec(), - symbol: b"ParaA".to_vec(), - decimals: 18, - }; - - // Register para A asset in parachain B. Free execution - ParaB::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.clone().try_into().expect("too long"), - asset_metadata.name.clone().try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 0)); - }); - - // Register para A asset in parachain C. Free execution - ParaC::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.clone().try_into().expect("too long"), - asset_metadata.name.clone().try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location, 0)); - }); - - // Send para A asset to para B - let dest = Location { - parents: 1, - interior: [ - Parachain(2), - AccountKey20 { - network: None, - key: PARAALICE.into(), - }, - ] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::SelfReserve, 100); - // free execution, full amount received - assert_ok!(PolkadotXcm::transfer_assets( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedLocation::from(beneficiary)), - Box::new(VersionedAssets::from(vec![asset])), - 0, - WeightLimit::Limited(Weight::from_parts(80u64, DEFAULT_PROOF_SIZE)) - )); - }); - - // Para A balances have been substracted - ParaA::execute_with(|| { - assert_eq!( - ParaBalances::free_balance(&PARAALICE.into()), - INITIAL_BALANCE - 100 - ); - }); - - // Para B balances have been credited - ParaB::execute_with(|| { - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(100)) - ); - }); - - // Send para A asset from para B to para C - let dest = Location { - parents: 1, - interior: [ - Parachain(3), - AccountKey20 { - network: None, - key: PARAALICE.into(), - }, - ] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - ParaB::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_id), 100); - assert_ok!(PolkadotXcm::transfer_assets( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedLocation::from(beneficiary)), - Box::new(VersionedAssets::from(vec![asset])), - 0, - WeightLimit::Limited(Weight::from_parts(80u64, DEFAULT_PROOF_SIZE)) - )); - }); - - // The message passed through parachainA so we needed to pay since its the native token - // The message passed through parachainA so we needed to pay since its the native token - ParaC::execute_with(|| { - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(95)) - ); - }); -} - -#[test] -fn send_para_a_asset_to_para_b_and_back_to_para_a() { - MockNet::reset(); - - // Para A asset - let para_a_balances = Location::new(1, [Parachain(1), PalletInstance(1u8)]); - let source_location: AssetType = para_a_balances - .try_into() - .expect("Location convertion to AssetType should succeed"); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"ParaAToken".to_vec(), - symbol: b"ParaA".to_vec(), - decimals: 18, - }; - - // Register para A asset in para B - ParaB::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location, 0)); - }); - - // Send para A asset to para B - let dest = Location { - parents: 1, - interior: [ - Parachain(2), - AccountKey20 { - network: None, - key: PARAALICE.into(), - }, - ] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::SelfReserve, 100); - assert_ok!(PolkadotXcm::transfer_assets( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedLocation::from(beneficiary)), - Box::new(VersionedAssets::from(vec![asset])), - 0, - WeightLimit::Limited(Weight::from_parts(80u64, DEFAULT_PROOF_SIZE)) - )); - }); - - // Balances have been subtracted - ParaA::execute_with(|| { - assert_eq!( - ParaBalances::free_balance(&PARAALICE.into()), - INITIAL_BALANCE - 100 - ); - }); - - // Para B balances have been credited - ParaB::execute_with(|| { - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(100)) - ); - }); - - // Send back para A asset to para A - let dest = Location { - parents: 1, - interior: [ - Parachain(1), - AccountKey20 { - network: None, - key: PARAALICE.into(), - }, - ] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - ParaB::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_id), 100); - assert_ok!(PolkadotXcm::transfer_assets( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedLocation::from(beneficiary)), - Box::new(VersionedAssets::from(vec![asset])), - 0, - WeightLimit::Limited(Weight::from_parts(80u64, DEFAULT_PROOF_SIZE)) - )); - }); - - ParaA::execute_with(|| { - // Weight used is 4 - assert_eq!( - ParaBalances::free_balance(&PARAALICE.into()), - INITIAL_BALANCE - 4 - ); - }); -} - -#[test] -fn send_para_a_asset_to_para_b_and_back_to_para_a_with_new_reanchoring() { - MockNet::reset(); - - let para_a_balances = Location::new(1, [Parachain(1), PalletInstance(1u8)]); - let source_location: AssetType = para_a_balances - .try_into() - .expect("Location convertion to AssetType should succeed"); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"ParaAToken".to_vec(), - symbol: b"ParaA".to_vec(), - decimals: 18, - }; - - ParaB::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location, 0)); - }); - - let dest = Location { - parents: 1, - interior: [ - Parachain(2), - AccountKey20 { - network: None, - key: PARAALICE.into(), - }, - ] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::SelfReserve, 100); - assert_ok!(PolkadotXcm::transfer_assets( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedLocation::from(beneficiary)), - Box::new(VersionedAssets::from(vec![asset])), - 0, - WeightLimit::Limited(Weight::from_parts(80u64, DEFAULT_PROOF_SIZE)) - )); - }); - - // Para A asset has been credited - ParaA::execute_with(|| { - assert_eq!( - ParaBalances::free_balance(&PARAALICE.into()), - INITIAL_BALANCE - 100 - ); - }); - - ParaB::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(100)) - ); - }); - - // This time we will force the new reanchoring by manually sending the - // Message through polkadotXCM pallet - - let dest = Location { - parents: 1, - interior: [Parachain(1)].into(), - }; - - let reanchored_para_a_balances = Location::new(0, [PalletInstance(1u8)]); - - let message = xcm::VersionedXcm::<()>::V5(Xcm(vec![ - WithdrawAsset((reanchored_para_a_balances.clone(), 100).into()), - ClearOrigin, - BuyExecution { - fees: (reanchored_para_a_balances, 100).into(), - weight_limit: Limited(80.into()), - }, - DepositAsset { - assets: All.into(), - beneficiary: Location::new( - 0, - [AccountKey20 { - network: None, - key: PARAALICE, - }], - ), - }, - ])); - ParaB::execute_with(|| { - // Send a message to the sovereign account in ParaA to withdraw - // and deposit asset - assert_ok!(ParachainPalletXcm::send( - parachain::RuntimeOrigin::root(), - Box::new(dest.into()), - Box::new(message), - )); - }); - - ParaA::execute_with(|| { - // Weight used is 4 - assert_eq!( - ParaBalances::free_balance(&PARAALICE.into()), - INITIAL_BALANCE - 4 - ); - }); -} - -#[test] -fn receive_relay_asset_with_trader() { - MockNet::reset(); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - // This time we are gonna put a rather high number of units per second - // we know later we will divide by 1e12 - // Lets put 1e6 as units per second - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location, 2_500_000_000_000)); - }); - - let dest: Location = Junction::AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - // We are sending 100 tokens from relay. - // Amount spent in fees is Units per second * weight / 1_000_000_000_000 (weight per second) - // weight is 4 since we are executing 4 instructions with a unitweightcost of 1. - // Units per second should be 2_500_000_000_000_000 - // Therefore with no refund, we should receive 10 tokens less - // Native trader fails for this, and we use the asset trader - Relay::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([], 100).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - ParaA::execute_with(|| { - // non-free execution, not full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(90)) - ); - // Fee should have been received by treasury - assert_eq!( - EvmForeignAssets::balance(source_id, Treasury::account_id()), - Ok(U256::from(10)) - ); - }); -} - -#[test] -fn send_para_a_asset_to_para_b_with_trader() { - MockNet::reset(); - - let para_a_balances = Location::new(1, [Parachain(1), PalletInstance(1u8)]); - let source_location: AssetType = para_a_balances - .try_into() - .expect("Location convertion to AssetType should succeed"); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"ParaAToken".to_vec(), - symbol: b"ParaA".to_vec(), - decimals: 18, - }; - - ParaB::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location, 2500000000000)); - }); - - let dest = Location { - parents: 1, - interior: [ - Parachain(2), - AccountKey20 { - network: None, - key: PARAALICE.into(), - }, - ] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - // In destination chain, we only need 4 weight - // We put 10 weight, 6 of which should be refunded and 4 of which should go to treasury - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::SelfReserve, 100); - assert_ok!(PolkadotXcm::transfer_assets( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedLocation::from(beneficiary)), - Box::new(VersionedAssets::from(vec![asset])), - 0, - WeightLimit::Limited(Weight::from_parts(10u64, DEFAULT_PROOF_SIZE)) - )); - }); - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - ParaBalances::free_balance(&PARAALICE.into()), - INITIAL_BALANCE - 100 - ); - }); - - // We are sending 100 tokens from para A. - // Amount spent in fees is Units per second * weight / 1_000_000_000_000 (weight per second) - // weight is 4 since we are executing 4 instructions with a unitweightcost of 1. - // Units per second should be 2_500_000_000_000_000 - // Since we set 10 weight in destination chain, 25 will be charged upfront - // 15 of those will be refunded, while 10 will go to treasury as the true weight used - // will be 4 - ParaB::execute_with(|| { - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(90)) - ); - // Fee should have been received by treasury - assert_eq!( - EvmForeignAssets::balance(source_id, Treasury::account_id()), - Ok(U256::from(10)) - ); - }); -} - -#[test] -fn send_para_a_asset_to_para_b_with_trader_and_fee() { - MockNet::reset(); - - let para_a_balances = Location::new(1, [Parachain(1), PalletInstance(1u8)]); - let source_location: AssetType = para_a_balances - .try_into() - .expect("Location convertion to AssetType should succeed"); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"ParaAToken".to_vec(), - symbol: b"ParaA".to_vec(), - decimals: 18, - }; - - ParaB::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - // With these units per second, 80K weight convrets to 1 asset unit - assert_ok!(add_supported_asset(source_location, 12500000)); - }); - - let dest = Location { - parents: 1, - interior: [ - Parachain(2), - AccountKey20 { - network: None, - key: PARAALICE.into(), - }, - ] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - // we use transfer_with_fee - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::SelfReserve, 100); - let asset_fee = currency_to_asset(parachain::CurrencyId::SelfReserve, 1); - assert_ok!(PolkadotXcm::transfer_assets( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedLocation::from(beneficiary)), - Box::new(VersionedAssets::from(vec![asset_fee, asset])), - 0, - WeightLimit::Limited(Weight::from_parts(800000u64, DEFAULT_PROOF_SIZE)) - )); - }); - ParaA::execute_with(|| { - // 100 tokens transferred plus 1 taken from fees - assert_eq!( - ParaBalances::free_balance(&PARAALICE.into()), - INITIAL_BALANCE - 100 - 1 - ); - }); - - ParaB::execute_with(|| { - // free execution, full amount received because the xcm instruction does not cost - // what it is specified - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(101)) - ); - }); -} - -#[test] -fn error_when_not_paying_enough() { - MockNet::reset(); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - let dest: Location = Junction::AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - // This time we are gonna put a rather high number of units per second - // we know later we will divide by 1e12 - // Lets put 1e6 as units per second - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location, 2500000000000)); - }); - - ParaA::execute_with(|| { - // amount not received as it is not paying enough - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(0)) - ); - }); - - // We are sending 100 tokens from relay. - // If we set the dest weight to be 1e7, we know the buy_execution will spend 1e7*1e6/1e12 = 10 - // Therefore with no refund, we should receive 10 tokens less - Relay::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([], 5).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - ParaA::execute_with(|| { - // amount not received as it is not paying enough - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(0)) - ); - }); -} - -#[test] -fn transact_through_derivative_multilocation() { - MockNet::reset(); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location, 1)); - - // Root can set transact info - assert_ok!(XcmTransactor::set_transact_info( - parachain::RuntimeOrigin::root(), - Box::new(xcm::VersionedLocation::from(Location::parent())), - // Relay charges 1000 for every instruction, and we have 3, so 3000 - 3000.into(), - 20000000000.into(), - None - )); - // Root can set transact info - // Set fee per second using weight-trader (replaces old set_fee_per_second) - set_fee_per_second_for_location(Location::parent(), WEIGHT_REF_TIME_PER_SECOND as u128) - .expect("must succeed"); - }); - - // Let's construct the call to know how much weight it is going to require - - let dest: Location = AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - Relay::execute_with(|| { - // 4000000000 transact + 3000 correspond to 4000003000 tokens. 100 more for the transfer call - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([], 4000003100u128).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(4000003100u128)) - ); - }); - - // Register address - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::register( - parachain::RuntimeOrigin::root(), - PARAALICE.into(), - 0, - )); - }); - - // Send to registered address - - let registered_address = derivative_account_id(para_a_account(), 0); - let dest = Location { - parents: 1, - interior: [AccountId32 { - network: None, - id: registered_address.clone().into(), - }] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_id), 100); - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: beneficiary.clone(), - }]); - // free execution, full amount received - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from(vec![asset])), - Box::new(TransferType::DestinationReserve), - Box::new(fees_id), - Box::new(TransferType::DestinationReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) - )); - }); - - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(4000003000u128)) - ); - }); - - // What we will do now is transfer this relay tokens from the derived account to the sovereign - // again - Relay::execute_with(|| { - // free execution,x full amount received - assert!(RelayBalances::free_balance(¶_a_account()) == 4000003000); - }); - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = ::PalletInfo::index::< - relay_chain::Balances, - >() - .unwrap() as u8; - - encoded.push(index); - - // Then call bytes - let mut call_bytes = pallet_balances::Call::::transfer_allow_death { - dest: para_a_account(), - value: 100u32.into(), - } - .encode(); - encoded.append(&mut call_bytes); - - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::transact_through_derivative( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - MockTransactors::Relay, - 0, - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - Location::parent() - ))), - fee_amount: None - }, - // 4000000000 + 3000 we should have taken out 4000003000 tokens from the caller - encoded, - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: None - }, - false - )); - let event_found: Option = parachain::para_events() - .iter() - .find_map(|event| match event.clone() { - parachain::RuntimeEvent::PolkadotXcm(pallet_xcm::Event::AssetsTrapped { - .. - }) => Some(event.clone()), - _ => None, - }); - // Assert that the events do not contain the assets being trapped - assert!(event_found.is_none()); - }); - - Relay::execute_with(|| { - // free execution,x full amount received - assert!(RelayBalances::free_balance(¶_a_account()) == 100); - - assert!(RelayBalances::free_balance(®istered_address) == 0); - }); -} - -#[test] -fn transact_through_derivative_with_custom_fee_weight() { - MockNet::reset(); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location, 1)); - }); - - // Let's construct the call to know how much weight it is going to require - - let dest: Location = AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - Relay::execute_with(|| { - // 4000000000 transact + 3000 correspond to 4000003000 tokens. 100 more for the transfer call - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([], 4000003100u128).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(4000003100u128)) - ); - }); - - // Register address - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::register( - parachain::RuntimeOrigin::root(), - PARAALICE.into(), - 0, - )); - }); - - // Send to registered address - - let registered_address = derivative_account_id(para_a_account(), 0); - let dest = Location { - parents: 1, - interior: [AccountId32 { - network: None, - id: registered_address.clone().into(), - }] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_id), 100); - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: beneficiary.clone(), - }]); - // free execution, full amount received - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from(vec![asset])), - Box::new(TransferType::DestinationReserve), - Box::new(fees_id), - Box::new(TransferType::DestinationReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) - )); - }); - - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(4000003000u128)) - ); - }); - - // What we will do now is transfer this relay tokens from the derived account to the sovereign - // again - Relay::execute_with(|| { - // free execution,x full amount received - assert!(RelayBalances::free_balance(¶_a_account()) == 4000003000); - }); - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = ::PalletInfo::index::< - relay_chain::Balances, - >() - .unwrap() as u8; - - encoded.push(index); - - // Then call bytes - let mut call_bytes = pallet_balances::Call::::transfer_allow_death { - dest: para_a_account(), - value: 100u32.into(), - } - .encode(); - encoded.append(&mut call_bytes); - - let overall_weight = 4000003000u64; - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::transact_through_derivative( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - MockTransactors::Relay, - 0, - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - Location::parent() - ))), - // 1-1 fee weight mapping - fee_amount: Some(overall_weight as u128) - }, - // 4000000000 + 3000 we should have taken out 4000003000 tokens from the caller - encoded, - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: Some(Limited(overall_weight.into())) - }, - false - )); - let event_found: Option = parachain::para_events() - .iter() - .find_map(|event| match event.clone() { - parachain::RuntimeEvent::PolkadotXcm(pallet_xcm::Event::AssetsTrapped { - .. - }) => Some(event.clone()), - _ => None, - }); - // Assert that the events do not contain the assets being trapped - assert!(event_found.is_none()); - }); - - Relay::execute_with(|| { - // free execution,x full amount received - assert!(RelayBalances::free_balance(¶_a_account()) == 100); - - assert!(RelayBalances::free_balance(®istered_address) == 0); - }); -} - -#[test] -fn transact_through_derivative_with_custom_fee_weight_refund() { - MockNet::reset(); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location, 1)); - }); - - // Let's construct the call to know how much weight it is going to require - - let dest: Location = AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - Relay::execute_with(|| { - // 4000000000 transact + 9000 correspond to 4000009000 tokens. 100 more for the transfer call - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([], 4000009100u128).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(4000009100u128)) - ); - }); - - // Register address - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::register( - parachain::RuntimeOrigin::root(), - PARAALICE.into(), - 0, - )); - }); - - // Send to registered address - - let registered_address = derivative_account_id(para_a_account(), 0); - let dest = Location { - parents: 1, - interior: [AccountId32 { - network: None, - id: registered_address.clone().into(), - }] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_id), 100); - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: beneficiary.clone(), - }]); - // free execution, full amount received - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from(vec![asset])), - Box::new(TransferType::DestinationReserve), - Box::new(fees_id), - Box::new(TransferType::DestinationReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) - )); - }); - - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(4000009000u128)) - ); - }); - - // What we will do now is transfer this relay tokens from the derived account to the sovereign - // again - Relay::execute_with(|| { - // free execution,x full amount received - assert!(RelayBalances::free_balance(¶_a_account()) == 4000009000); - }); - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = ::PalletInfo::index::< - relay_chain::Balances, - >() - .unwrap() as u8; - - encoded.push(index); - - // Then call bytes - let mut call_bytes = pallet_balances::Call::::transfer_allow_death { - dest: para_a_account(), - value: 100u32.into(), - } - .encode(); - encoded.append(&mut call_bytes); - - let overall_weight = 4000009000u64; - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::transact_through_derivative( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - MockTransactors::Relay, - 0, - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - Location::parent() - ))), - // 1-1 fee weight mapping - fee_amount: Some(overall_weight as u128) - }, - encoded, - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: Some(Limited(overall_weight.into())) - }, - true - )); - let event_found: Option = parachain::para_events() - .iter() - .find_map(|event| match event.clone() { - parachain::RuntimeEvent::PolkadotXcm(pallet_xcm::Event::AssetsTrapped { - .. - }) => Some(event.clone()), - _ => None, - }); - // Assert that the events do not contain the assets being trapped - assert!(event_found.is_none()); - }); - - Relay::execute_with(|| { - // free execution, full amount received - // 4000009000 refunded + 100 transferred = 4000009100 - assert_eq!(RelayBalances::free_balance(¶_a_account()), 4000009100); - assert_eq!(RelayBalances::free_balance(®istered_address), 0); - }); -} - -#[test] -fn transact_through_sovereign() { - MockNet::reset(); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location, 1)); - - // Root can set transact info - assert_ok!(XcmTransactor::set_transact_info( - parachain::RuntimeOrigin::root(), - Box::new(xcm::VersionedLocation::from(Location::parent())), - // Relay charges 1000 for every instruction, and we have 3, so 3000 - 3000.into(), - 20000000000.into(), - None - )); - // Root can set transact info - // Set fee per second using weight-trader (replaces old set_fee_per_second) - set_fee_per_second_for_location(Location::parent(), WEIGHT_REF_TIME_PER_SECOND as u128) - .expect("must succeed"); - }); - - let dest: Location = AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - Relay::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([], 4000003100u128).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(4000003100u128)) - ); - }); - - // Register address - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::register( - parachain::RuntimeOrigin::root(), - PARAALICE.into(), - 0, - )); - }); - - // Send to registered address - let registered_address = derivative_account_id(para_a_account(), 0); - let dest = Location { - parents: 1, - interior: [AccountId32 { - network: None, - id: registered_address.clone().into(), - }] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_id), 100); - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: beneficiary.clone(), - }]); - // free execution, full amount received - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from(vec![asset])), - Box::new(TransferType::DestinationReserve), - Box::new(fees_id), - Box::new(TransferType::DestinationReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) - )); - }); - - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(4000003000u128)) - ); - }); - - // What we will do now is transfer this relay tokens from the derived account to the sovereign - // again - Relay::execute_with(|| { - // free execution,x full amount received - assert!(RelayBalances::free_balance(¶_a_account()) == 4000003000); - 0 - }); - - // We send the xcm transact operation to parent - let dest = Location { - parents: 1, - interior: [].into(), - }; - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = ::PalletInfo::index::< - relay_chain::Balances, - >() - .unwrap() as u8; - - encoded.push(index); - - // Then call bytes - let mut call_bytes = pallet_balances::Call::::transfer_allow_death { - dest: para_a_account(), - value: 100u32.into(), - } - .encode(); - encoded.append(&mut call_bytes); - - // Root can directly pass the execution byes to the sovereign - ParaA::execute_with(|| { - let utility_bytes = ::encode_call( - moonbase_runtime::xcm_config::Transactors::Relay, - xcm_primitives::UtilityAvailableCalls::AsDerivative(0, encoded), - ); - - assert_ok!(XcmTransactor::transact_through_sovereign( - parachain::RuntimeOrigin::root(), - Box::new(xcm::VersionedLocation::from(dest)), - Some(PARAALICE.into()), - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - Location::parent() - ))), - fee_amount: None - }, - utility_bytes, - OriginKind::SovereignAccount, - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: None - }, - false - )); - }); - - Relay::execute_with(|| { - // free execution,x full amount received - assert!(RelayBalances::free_balance(¶_a_account()) == 100); - - assert!(RelayBalances::free_balance(®istered_address) == 0); - }); -} - -#[test] -fn transact_through_sovereign_fee_payer_none() { - MockNet::reset(); - - ParaA::execute_with(|| { - // Root can set transact info - assert_ok!(XcmTransactor::set_transact_info( - parachain::RuntimeOrigin::root(), - Box::new(xcm::VersionedLocation::from(Location::parent())), - // Relay charges 1000 for every instruction, and we have 3, so 3000 - 3000.into(), - 20000000000.into(), - None - )); - // Root can set transact info - // Set fee per second using weight-trader (replaces old set_fee_per_second) - set_fee_per_second_for_location(Location::parent(), WEIGHT_REF_TIME_PER_SECOND as u128) - .expect("must succeed"); - }); - - let derivative_address = derivative_account_id(para_a_account(), 0); - - Relay::execute_with(|| { - // Transfer 100 tokens to derivative_address on the relay - assert_ok!(RelayBalances::transfer_keep_alive( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - derivative_address.clone(), - 100u128 - )); - - // Transfer the XCM execution fee amount to ParaA's sovereign account - assert_ok!(RelayBalances::transfer_keep_alive( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - para_a_account(), - 4000003000u128 - )); - }); - - // Check balances before the transact call - Relay::execute_with(|| { - assert_eq!(RelayBalances::free_balance(¶_a_account()), 4000003000); - assert_eq!(RelayBalances::free_balance(&derivative_address), 100); - assert_eq!(RelayBalances::free_balance(&RELAYBOB), 0); - }); - - // Encode the call. Balances transfer of 100 relay tokens to RELAYBOB - let mut encoded: Vec = Vec::new(); - let index = ::PalletInfo::index::< - relay_chain::Balances, - >() - .unwrap() as u8; - - encoded.push(index); - - let mut call_bytes = pallet_balances::Call::::transfer_allow_death { - dest: RELAYBOB, - value: 100u32.into(), - } - .encode(); - encoded.append(&mut call_bytes); - - // We send the xcm transact operation to parent - let dest = Location { - parents: 1, - interior: /* Here */ [].into(), - }; - - // Root can directly pass the execution byes to the sovereign - ParaA::execute_with(|| { - // The final call will be an AsDerivative using index 0 - let utility_bytes = ::encode_call( - moonbase_runtime::xcm_config::Transactors::Relay, - xcm_primitives::UtilityAvailableCalls::AsDerivative(0, encoded), - ); - - assert_ok!(XcmTransactor::transact_through_sovereign( - parachain::RuntimeOrigin::root(), - Box::new(xcm::VersionedLocation::from(dest)), - // No fee_payer here. The sovereign account will pay the fees on destination. - None, - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - Location::parent() - ))), - fee_amount: None - }, - utility_bytes, - OriginKind::SovereignAccount, - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: None - }, - false - )); - }); - - // Check balances after the transact call are correct - Relay::execute_with(|| { - assert_eq!(RelayBalances::free_balance(¶_a_account()), 0); - assert_eq!(RelayBalances::free_balance(&derivative_address), 0); - assert_eq!(RelayBalances::free_balance(&RELAYBOB), 100); - }); -} - -#[test] -fn transact_through_sovereign_with_custom_fee_weight() { - MockNet::reset(); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location; - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location, 1)); - }); - - let dest: Location = AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - Relay::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([], 4000003100u128).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(4000003100u128)) - ); - }); - - // Register address - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::register( - parachain::RuntimeOrigin::root(), - PARAALICE.into(), - 0, - )); - }); - - // Send to registered address - let registered_address = derivative_account_id(para_a_account(), 0); - let dest = Location { - parents: 1, - interior: [AccountId32 { - network: None, - id: registered_address.clone().into(), - }] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_id), 100); - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: beneficiary.clone(), - }]); - // free execution, full amount received - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from(vec![asset])), - Box::new(TransferType::DestinationReserve), - Box::new(fees_id), - Box::new(TransferType::DestinationReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) - )); - }); - - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(4000003000u128)) - ); - }); - - // What we will do now is transfer this relay tokens from the derived account to the sovereign - // again - Relay::execute_with(|| { - // free execution,x full amount received - assert!(RelayBalances::free_balance(¶_a_account()) == 4000003000); - 0 - }); - - // We send the xcm transact operation to parent - let dest = Location { - parents: 1, - interior: [].into(), - }; - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = ::PalletInfo::index::< - relay_chain::Balances, - >() - .unwrap() as u8; - - encoded.push(index); - - // Then call bytes - let mut call_bytes = pallet_balances::Call::::transfer_allow_death { - dest: para_a_account(), - value: 100u32.into(), - } - .encode(); - encoded.append(&mut call_bytes); - - let total_weight = 4000003000u64; - // Root can directly pass the execution byes to the sovereign - ParaA::execute_with(|| { - let utility_bytes = ::encode_call( - moonbase_runtime::xcm_config::Transactors::Relay, - xcm_primitives::UtilityAvailableCalls::AsDerivative(0, encoded), - ); - - assert_ok!(XcmTransactor::transact_through_sovereign( - parachain::RuntimeOrigin::root(), - Box::new(xcm::VersionedLocation::from(dest)), - Some(PARAALICE.into()), - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - Location::parent() - ))), - // 1-1 fee-weight mapping - fee_amount: Some(total_weight as u128) - }, - utility_bytes, - OriginKind::SovereignAccount, - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: Some(Limited(total_weight.into())) - }, - false - )); - }); - - Relay::execute_with(|| { - // free execution,x full amount received - assert!(RelayBalances::free_balance(¶_a_account()) == 100); - - assert!(RelayBalances::free_balance(®istered_address) == 0); - }); -} - -#[test] -fn transact_through_sovereign_with_custom_fee_weight_refund() { - MockNet::reset(); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location, 1)); - }); - - let dest: Location = AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - Relay::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([], 4000009100u128).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(4000009100u128)) - ); - }); - - // Register address - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::register( - parachain::RuntimeOrigin::root(), - PARAALICE.into(), - 0, - )); - }); - - // Send to registered address - let registered_address = derivative_account_id(para_a_account(), 0); - let dest = Location { - parents: 1, - interior: [AccountId32 { - network: None, - id: registered_address.clone().into(), - }] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_id), 100); - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: beneficiary.clone(), - }]); - // free execution, full amount received - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from(vec![asset])), - Box::new(TransferType::DestinationReserve), - Box::new(fees_id), - Box::new(TransferType::DestinationReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) - )); - }); - - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(4000009000u128)) - ); - }); - - // What we will do now is transfer this relay tokens from the derived account to the sovereign - // again - Relay::execute_with(|| { - // free execution,x full amount received - assert!(RelayBalances::free_balance(¶_a_account()) == 4000009000); - 0 - }); - - // We send the xcm transact operation to parent - let dest = Location { - parents: 1, - interior: [].into(), - }; - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = ::PalletInfo::index::< - relay_chain::Balances, - >() - .unwrap() as u8; - - encoded.push(index); - - // Then call bytes - let mut call_bytes = pallet_balances::Call::::transfer_allow_death { - dest: para_a_account(), - value: 100u32.into(), - } - .encode(); - encoded.append(&mut call_bytes); - - let total_weight = 4000009000u64; - // Root can directly pass the execution byes to the sovereign - ParaA::execute_with(|| { - let utility_bytes = ::encode_call( - moonbase_runtime::xcm_config::Transactors::Relay, - xcm_primitives::UtilityAvailableCalls::AsDerivative(0, encoded), - ); - - assert_ok!(XcmTransactor::transact_through_sovereign( - parachain::RuntimeOrigin::root(), - Box::new(xcm::VersionedLocation::from(dest)), - Some(PARAALICE.into()), - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - Location::parent() - ))), - // 1-1 fee-weight mapping - fee_amount: Some(total_weight as u128) - }, - utility_bytes, - OriginKind::SovereignAccount, - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: Some(Limited(total_weight.into())) - }, - true - )); - }); - - Relay::execute_with(|| { - // free execution, full amount received - // 4000009000 refunded + 100 transferred = 4000009100 - assert_eq!(RelayBalances::free_balance(¶_a_account()), 4000009100); - - assert_eq!(RelayBalances::free_balance(®istered_address), 0); - }); -} - -#[test] -fn test_automatic_versioning_on_runtime_upgrade_with_relay() { - MockNet::reset(); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - // register relay asset in parachain A and set XCM version to 1 - ParaA::execute_with(|| { - parachain::XcmVersioner::set_version(1); - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - 3, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location, 0)); - }); - - let response = Response::Version(2); - let querier: Location = [].into(); - - // 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: Weight::zero(), - querier: Some(querier), - }]); - // The router is mocked, and we cannot use WrapVersion in ChildParachainRouter. So we will force - // it directly here - // Actually send relay asset to parachain - let dest: Location = AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - - Relay::execute_with(|| { - // This sets the default version, for not known destinations - assert_ok!(RelayChainPalletXcm::force_default_xcm_version( - relay_chain::RuntimeOrigin::root(), - Some(3) - )); - - // Wrap version, which sets VersionedStorage - // This is necessary because the mock router does not use wrap_version, but - // this is not necessary in prod - assert_ok!(::wrap_version( - &Parachain(1).into(), - mock_message - )); - - // Transfer assets. Since it is an unknown destination, it will query for version - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([], 123).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - - // 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::RuntimeEvent = - pallet_xcm::Event::SupportedVersionChanged { - location: Location { - parents: 0, - interior: [Parachain(1)].into(), - }, - version: 1, - } - .into(); - - Relay::execute_with(|| { - // Assert that the events vector contains the version change - assert!(relay_chain::relay_events().contains(&expected_supported_version)); - }); - - // 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().iter().any(|e| matches!( - e, - parachain::RuntimeEvent::PolkadotXcm(pallet_xcm::Event::VersionChangeNotified { - result: 2, - .. - }) - ))); - }); - - // This event should have been seen in the relay - let expected_supported_version_2: relay_chain::RuntimeEvent = - pallet_xcm::Event::SupportedVersionChanged { - location: Location { - parents: 0, - interior: [Parachain(1)].into(), - }, - version: 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(); - - let para_a_balances = Location::new(1, [Parachain(1), PalletInstance(1u8)]); - let source_location: AssetType = para_a_balances - .try_into() - .expect("Location convertion to AssetType should succeed"); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"ParaAToken".to_vec(), - symbol: b"ParaA".to_vec(), - decimals: 18, - }; - let response = Response::Version(2); - let querier: Location = [].into(); - - // 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: Weight::zero(), - querier: Some(querier), - }]); - - ParaA::execute_with(|| { - // advertised version - parachain::XcmVersioner::set_version(2); - }); - - ParaB::execute_with(|| { - // Let's try with v0 - parachain::XcmVersioner::set_version(0); - - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location, 0)); - }); - - ParaA::execute_with(|| { - // This sets the default version, for not known destinations - assert_ok!(ParachainPalletXcm::force_default_xcm_version( - parachain::RuntimeOrigin::root(), - Some(3) - )); - // Wrap version, which sets VersionedStorage - assert_ok!(::wrap_version( - &Location::new(1, [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::RuntimeEvent = - pallet_xcm::Event::SupportedVersionChanged { - location: Location { - parents: 1, - interior: [Parachain(2)].into(), - }, - version: 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 = Location { - parents: 1, - interior: [ - Parachain(2), - AccountKey20 { - network: None, - key: PARAALICE.into(), - }, - ] - .into(), - } - .into(); - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::SelfReserve, 100); - // free execution, full amount received - assert_ok!(PolkadotXcm::transfer_assets( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedLocation::from(beneficiary)), - Box::new(VersionedAssets::from(vec![asset])), - 0, - WeightLimit::Limited(Weight::from_parts(80u64, DEFAULT_PROOF_SIZE)) - )); - // free execution, full amount received - assert_eq!( - ParaBalances::free_balance(&PARAALICE.into()), - INITIAL_BALANCE - 100 - ); - }); - - ParaB::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(100)) - ); - }); - - // 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().iter().any(|e| matches!( - e, - parachain::RuntimeEvent::PolkadotXcm(pallet_xcm::Event::VersionChangeNotified { - result: 2, - .. - }) - ))); - }); - - // This event should have been seen in para A - let expected_supported_version_2: parachain::RuntimeEvent = - pallet_xcm::Event::SupportedVersionChanged { - location: Location { - parents: 1, - interior: [Parachain(2)].into(), - }, - version: 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)); - }); -} - -#[test] -fn receive_asset_with_no_sufficients_is_possible_for_non_existent_account() { - MockNet::reset(); - - let fresh_account = PARABOB; - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - // register relay asset in parachain A - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location, 0)); - }); - - // Actually send relay asset to parachain - let dest: Location = AccountKey20 { - network: None, - key: fresh_account, - } - .into(); - Relay::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([], 123).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - // parachain should not have received assets - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, fresh_account.into()), - Ok(U256::from(123)) - ); - }); -} - -#[test] -fn receive_assets_with_sufficients_true_allows_non_funded_account_to_receive_assets() { - MockNet::reset(); - - let fresh_account = [2u8; 20]; - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - // register relay asset in parachain A - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location, 0)); - }); - - // Actually send relay asset to parachain - let dest: Location = AccountKey20 { - network: None, - key: fresh_account, - } - .into(); - Relay::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([], 123).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - // parachain should have received assets - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, fresh_account.into()), - Ok(U256::from(123)) - ); - }); -} - -#[test] -fn evm_account_receiving_assets_should_handle_sufficients_ref_count() { - MockNet::reset(); - - let mut sufficient_account = [0u8; 20]; - sufficient_account[0..20].copy_from_slice(&evm_account()[..]); - - let evm_account_id = parachain::AccountId::from(sufficient_account); - - // Evm account is self sufficient - ParaA::execute_with(|| { - assert_eq!(parachain::System::account(evm_account_id).sufficients, 1); - }); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - // register relay asset in parachain A - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - 1, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location, 0)); - }); - - // Actually send relay asset to parachain - let dest: Location = AccountKey20 { - network: None, - key: sufficient_account, - } - .into(); - Relay::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([], 123).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - // Evm account sufficient ref count increased by 1. - ParaA::execute_with(|| { - // TODO: since the suicided logic was introduced the data of the smart contract is not - // removed, it will have to be updated in a future release when there is the ability to - // remove contract data - // assert_eq!(parachain::System::account(evm_account_id).sufficients, 2); - }); - - ParaA::execute_with(|| { - // Remove the account from the evm context. - parachain::EVM::remove_account(&evm_account()); - // Evm account sufficient ref count decreased by 1. - // TODO: since the suicided logic was introduced the data of the smart contract is not - // removed, it will have to be updated in a future release when there is the ability to - // remove contract data - // assert_eq!(parachain::System::account(evm_account_id).sufficients, 1); - }); -} - -#[test] -fn empty_account_should_not_be_reset() { - MockNet::reset(); - - // Test account has nonce 1 when used for the first time. - let sufficient_account = PARABOB; - - let evm_account_id = parachain::AccountId::from(sufficient_account); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - // register relay asset in parachain A - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location, 0)); - }); - - // Send native token to evm_account - ParaA::execute_with(|| { - assert_ok!(ParaBalances::transfer_allow_death( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - evm_account_id, - 100 - )); - }); - - // Actually send relay asset to parachain - let dest: Location = AccountKey20 { - network: None, - key: sufficient_account, - } - .into(); - Relay::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([], 123).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - ParaA::execute_with(|| { - // Empty the assets from the account. - // As this makes the account go below the `min_balance`, the account is considered dead - // at eyes of pallet-assets, and the consumer reference is decreased by 1 and is now Zero. - assert_ok!(parachain::EvmForeignAssets::transfer( - source_id, - evm_account_id, - PARAALICE.into(), - U256::from(123) - )); - // Verify account asset balance is Zero. - assert_eq!( - parachain::EvmForeignAssets::balance(source_id, evm_account_id.into()), - Ok(U256::from(0)) - ); - // Because we no longer have consumer references, we can set the balance to Zero. - // This would reset the account if our ED were to be > than Zero. - assert_ok!(ParaBalances::force_set_balance( - parachain::RuntimeOrigin::root(), - evm_account_id, - 0, - )); - // Verify account native balance is Zero. - assert_eq!(ParaBalances::free_balance(&evm_account_id), 0); - // Remove the account from the evm context. - // This decreases the sufficients reference by 1 and now is Zero. - parachain::EVM::remove_account(&evm_account()); - // Verify reference count. - let account = parachain::System::account(evm_account_id); - assert_eq!(account.sufficients, 0); - assert_eq!(account.consumers, 0); - assert_eq!(account.providers, 1); - // We expect the account to be alive in a Zero ED context. - assert_eq!(parachain::System::account_nonce(evm_account_id), 1); - }); -} - -#[test] -fn test_statemint_like() { - MockNet::reset(); - - let dest_para = Location::new(1, [Parachain(1)]); - - let sov = xcm_builder::SiblingParachainConvertsVia::< - polkadot_parachain::primitives::Sibling, - statemint_like::AccountId, - >::convert_location(&dest_para) - .unwrap(); - - let statemint_asset_a_balances = Location::new( - 1, - [ - Parachain(1000), - PalletInstance(5), - xcm::latest::prelude::GeneralIndex(0u128), - ], - ); - let source_location: AssetType = statemint_asset_a_balances - .try_into() - .expect("Location convertion to AssetType should succeed"); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"StatemintToken".to_vec(), - symbol: b"StatemintToken".to_vec(), - decimals: 12, - }; - - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location, 0)); - }); - - Statemint::execute_with(|| { - // Set new prefix - statemint_like::PrefixChanger::set_prefix( - PalletInstance(::index() as u8).into(), - ); - assert_ok!(StatemintAssets::create( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - 0, - RELAYALICE, - 1 - )); - - assert_ok!(StatemintAssets::mint( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - 0, - RELAYALICE, - 300000000000000 - )); - - // This is needed, since the asset is created as non-sufficient - assert_ok!(StatemintBalances::transfer_allow_death( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - sov, - 100000000000000 - )); - - // Actually send relay asset to parachain - let dest: Location = AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - - // Send asset with previous prefix - let fees_id: VersionedAssetId = AssetId(Location::new( - 0, - [ - xcm::latest::prelude::PalletInstance( - ::index() as u8, - ), - xcm::latest::prelude::GeneralIndex(0), - ], - )) - .into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!( - StatemintChainPalletXcm::transfer_assets_using_type_and_then( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new( - ( - [ - xcm::latest::prelude::PalletInstance( - ::index() as u8 - ), - xcm::latest::prelude::GeneralIndex(0), - ], - 123 - ) - .into() - ), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - }); - - ParaA::execute_with(|| { - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(123)) - ); - }); -} - -#[test] -fn send_statemint_asset_from_para_a_to_statemint_with_relay_fee() { - MockNet::reset(); - - // Relay asset - let relay_location = Location::parent(); - let relay_location_v3 = - match xcm::VersionedLocation::V5(relay_location.clone()).into_version(xcm::v3::VERSION) { - Ok(xcm::VersionedLocation::V3(loc)) => loc.into(), - _ => panic!("Failed to convert relay location to v3"), - }; - let relay_asset_type = parachain::AssetType::Xcm(relay_location_v3); - let source_relay_id: parachain::AssetId = relay_asset_type.clone().into(); - - let relay_asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - // Statemint asset - let statemint_asset = Location::new( - 1, - [ - Parachain(1000u32), - PalletInstance(5u8), - GeneralIndex(10u128), - ], - ); - let statemint_location_asset: AssetType = statemint_asset - .clone() - .try_into() - .expect("Location convertion to AssetType should succeed"); - let source_statemint_asset_id: parachain::AssetId = statemint_location_asset.clone().into(); - - let asset_metadata_statemint_asset = parachain::AssetMetadata { - name: b"USDC".to_vec(), - symbol: b"USDC".to_vec(), - decimals: 12, - }; - - let dest_para = Location::new(1, [Parachain(1)]); - - let sov = xcm_builder::SiblingParachainConvertsVia::< - polkadot_parachain::primitives::Sibling, - statemint_like::AccountId, - >::convert_location(&dest_para) - .unwrap(); - - ParaA::execute_with(|| { - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_relay_id, - relay_location.clone(), - relay_asset_metadata.decimals, - relay_asset_metadata.symbol.try_into().expect("too long"), - relay_asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(relay_asset_type.clone(), 0)); - - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_statemint_asset_id, - statemint_asset.clone(), - asset_metadata_statemint_asset.decimals, - asset_metadata_statemint_asset - .symbol - .try_into() - .expect("too long"), - asset_metadata_statemint_asset - .name - .try_into() - .expect("too long"), - )); - assert_ok!(add_supported_asset(statemint_location_asset, 0)); - }); - - let parachain_beneficiary_from_relay: Location = Junction::AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - - // Send relay chain asset to Alice in Parachain A - Relay::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_from_relay.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([], 200).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - Statemint::execute_with(|| { - // Set new prefix - statemint_like::PrefixChanger::set_prefix( - PalletInstance(::index() as u8).into(), - ); - - assert_ok!(StatemintAssets::create( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - 10, - RELAYALICE, - 1 - )); - - assert_ok!(StatemintAssets::mint( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - 10, - RELAYALICE, - 300000000000000 - )); - - // Send some native statemint tokens to sovereign for fees. - // We can't pay fees with USDC as the asset is minted as non-sufficient. - assert_ok!(StatemintBalances::transfer_allow_death( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - sov, - 100000000000000 - )); - - // Send statemint USDC asset to Alice in Parachain A - let parachain_beneficiary_from_statemint: Location = AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - - // Send with new prefix - let fees_id: VersionedAssetId = AssetId(Location::new( - 0, - [ - xcm::latest::prelude::PalletInstance( - ::index() as u8, - ), - GeneralIndex(10), - ], - )) - .into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_from_statemint.clone(), - }]); - assert_ok!( - StatemintChainPalletXcm::transfer_assets_using_type_and_then( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new( - ( - [ - xcm::latest::prelude::PalletInstance( - ::index() as u8 - ), - GeneralIndex(10), - ], - 125 - ) - .into() - ), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - }); - - let statemint_beneficiary = Location { - parents: 1, - interior: [ - Parachain(1000), - AccountId32 { - network: None, - id: RELAYBOB.into(), - }, - ] - .into(), - }; - - ParaA::execute_with(|| { - // Alice has received 125 USDC - assert_eq!( - EvmForeignAssets::balance(source_statemint_asset_id, PARAALICE.into()), - Ok(U256::from(125)) - ); - - // Alice has received 200 Relay assets - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(200)) - ); - }); - - Statemint::execute_with(|| { - // Check that BOB's balance is empty before the transfer - assert_eq!(StatemintAssets::account_balances(RELAYBOB), vec![]); - }); - - let (chain_part, beneficiary) = - split_location_into_chain_part_and_beneficiary(statemint_beneficiary).unwrap(); - - // Transfer USDC from Parachain A to Statemint using Relay asset as fee - ParaA::execute_with(|| { - let asset = currency_to_asset( - parachain::CurrencyId::ForeignAsset(source_statemint_asset_id), - 100, - ); - let asset_fee = - currency_to_asset(parachain::CurrencyId::ForeignAsset(source_relay_id), 100); - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: beneficiary.clone(), - }]); - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from(vec![asset_fee, asset])), - Box::new(TransferType::DestinationReserve), - Box::new(fees_id), - Box::new(TransferType::DestinationReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(80_000_000u64, 100_000u64)) - )); - }); - - ParaA::execute_with(|| { - // Alice has 100 USDC less - assert_eq!( - EvmForeignAssets::balance(source_statemint_asset_id, PARAALICE.into()), - Ok(U256::from(25)) - ); - - // Alice has 100 relay asset less - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(100)) - ); - }); - - Statemint::execute_with(|| { - // Check that BOB received 100 USDC on statemint - assert_eq!(StatemintAssets::account_balances(RELAYBOB), vec![(10, 100)]); - }); -} - -#[test] -fn send_dot_from_moonbeam_to_statemint_via_xtokens_transfer() { - MockNet::reset(); - - // Relay asset - let relay_location_v3 = xcm::v3::Location::parent(); - let relay_location = parachain::AssetType::Xcm(relay_location_v3.clone()); - let relay_location_v5 = Location::parent(); - let source_relay_id: parachain::AssetId = relay_location.clone().into(); - - let relay_asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - let dest_para = Location::new(1, [Parachain(1)]); - - let sov = xcm_builder::SiblingParachainConvertsVia::< - polkadot_parachain::primitives::Sibling, - statemint_like::AccountId, - >::convert_location(&dest_para) - .unwrap(); - - ParaA::execute_with(|| { - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_relay_id, - relay_location_v5.clone(), - relay_asset_metadata.decimals, - relay_asset_metadata.symbol.try_into().expect("too long"), - relay_asset_metadata.name.try_into().expect("too long"), - )); - XcmWeightTrader::set_asset_price(relay_location_v5.clone(), 0u128); - }); - - let parachain_beneficiary_absolute: Location = Junction::AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - - let statemint_beneficiary_absolute: Location = Junction::AccountId32 { - network: None, - id: RELAYALICE.into(), - } - .into(); - - // First we send relay chain asset to Alice in AssetHub (via teleport) - Relay::execute_with(|| { - assert_ok!(RelayChainPalletXcm::limited_teleport_assets( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1000).into()), - Box::new( - VersionedLocation::from(statemint_beneficiary_absolute) - .clone() - .into() - ), - Box::new(([], 200).into()), - 0, - WeightLimit::Unlimited - )); - }); - - // Send DOTs from AssetHub to ParaA (Moonbeam) - Statemint::execute_with(|| { - // Check Alice received 200 tokens on AssetHub - assert_eq!( - StatemintBalances::free_balance(RELAYALICE), - INITIAL_BALANCE + 200 - ); - - assert_ok!(StatemintBalances::transfer_allow_death( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - sov, - 110000000000000 - )); - - // Now send those tokens to ParaA - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_absolute.clone(), - }]); - assert_ok!( - StatemintChainPalletXcm::transfer_assets_using_type_and_then( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new((Location::parent(), 200).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - }); - - ParaA::execute_with(|| { - // Alice should have received the DOTs - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(200)) - ); - }); - - let dest = Location::new( - 1, - [ - Parachain(1000), - AccountId32 { - network: None, - id: RELAYBOB.into(), - }, - ], - ); - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - // Finally we test that we are able to send back the DOTs to AssetHub from the ParaA - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_relay_id), 100); - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: beneficiary.clone(), - }]); - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from(vec![asset])), - Box::new(TransferType::DestinationReserve), - Box::new(fees_id), - Box::new(TransferType::DestinationReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) - )); - - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(100)) - ); - }); - - Statemint::execute_with(|| { - // Check that Bob received the tokens back in AssetHub - assert_eq!( - StatemintBalances::free_balance(RELAYBOB), - INITIAL_BALANCE + 100 - ); - }); - - // Send back tokens from AH to ParaA from Bob's account - Statemint::execute_with(|| { - // Now send those tokens to ParaA - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_absolute.clone(), - }]); - assert_ok!( - StatemintChainPalletXcm::transfer_assets_using_type_and_then( - statemint_like::RuntimeOrigin::signed(RELAYBOB), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new((Location::parent(), 100).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - - // 100 DOTs were deducted from Bob's account - assert_eq!(StatemintBalances::free_balance(RELAYBOB), INITIAL_BALANCE); - }); - - ParaA::execute_with(|| { - // Alice should have received 100 DOTs - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(200)) - ); - }); -} - -#[test] -fn send_dot_from_moonbeam_to_statemint_via_xtokens_transfer_with_fee() { - MockNet::reset(); - - // Relay asset - let relay_location_v3 = xcm::v3::Location::parent(); - let relay_location = parachain::AssetType::Xcm(relay_location_v3.clone()); - let relay_location_v5 = Location::parent(); - let source_relay_id: parachain::AssetId = relay_location.clone().into(); - - let relay_asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - let dest_para = Location::new(1, [Parachain(1)]); - - let sov = xcm_builder::SiblingParachainConvertsVia::< - polkadot_parachain::primitives::Sibling, - statemint_like::AccountId, - >::convert_location(&dest_para) - .unwrap(); - - ParaA::execute_with(|| { - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_relay_id, - relay_location_v5.clone(), - relay_asset_metadata.decimals, - relay_asset_metadata.symbol.try_into().expect("too long"), - relay_asset_metadata.name.try_into().expect("too long"), - )); - XcmWeightTrader::set_asset_price(Location::parent(), 0u128); - }); - - let parachain_beneficiary_absolute: Location = Junction::AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - - let statemint_beneficiary_absolute: Location = Junction::AccountId32 { - network: None, - id: RELAYALICE.into(), - } - .into(); - - // First we send relay chain asset to Alice in AssetHub (via teleport) - Relay::execute_with(|| { - assert_ok!(RelayChainPalletXcm::limited_teleport_assets( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1000).into()), - Box::new( - VersionedLocation::from(statemint_beneficiary_absolute) - .clone() - .into() - ), - Box::new(([], 200).into()), - 0, - WeightLimit::Unlimited - )); - }); - - // Send DOTs from AssetHub to ParaA (Moonbeam) - Statemint::execute_with(|| { - // Check Alice received 200 tokens on AssetHub - assert_eq!( - StatemintBalances::free_balance(RELAYALICE), - INITIAL_BALANCE + 200 - ); - - assert_ok!(StatemintBalances::transfer_allow_death( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - sov, - 110000000000000 - )); - - // Now send those tokens to ParaA - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_absolute.clone(), - }]); - assert_ok!( - StatemintChainPalletXcm::transfer_assets_using_type_and_then( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new((Location::parent(), 200).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - }); - - ParaA::execute_with(|| { - // Alice should have received the DOTs - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(200)) - ); - }); - - let dest = Location::new( - 1, - [ - Parachain(1000), - AccountId32 { - network: None, - id: RELAYBOB.into(), - }, - ], - ); - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - // Finally we test that we are able to send back the DOTs to AssetHub from the ParaA - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_relay_id), 100); - let asset_fee = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_relay_id), 10); - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: beneficiary.clone(), - }]); - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from(vec![asset_fee, asset])), - Box::new(TransferType::DestinationReserve), - Box::new(fees_id), - Box::new(TransferType::DestinationReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) - )); - - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(90)) - ); - }); - - Statemint::execute_with(|| { - // Free execution: check that Bob received the tokens back in AssetHub - assert_eq!( - StatemintBalances::free_balance(RELAYBOB), - INITIAL_BALANCE + 110 - ); - }); - - // Send back tokens from AH to ParaA from Bob's account - Statemint::execute_with(|| { - // Now send those tokens to ParaA - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_absolute.clone(), - }]); - assert_ok!( - StatemintChainPalletXcm::transfer_assets_using_type_and_then( - statemint_like::RuntimeOrigin::signed(RELAYBOB), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new((Location::parent(), 100).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - - // 100 DOTs were deducted from Bob's account - assert_eq!( - StatemintBalances::free_balance(RELAYBOB), - INITIAL_BALANCE + 10 - ); - }); - - ParaA::execute_with(|| { - // Alice should have received 100 DOTs - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(190)) - ); - }); -} - -#[test] -fn send_dot_from_moonbeam_to_statemint_via_xtokens_transfer_multiasset() { - MockNet::reset(); - - // Relay asset - let relay_location_v3 = xcm::v3::Location::parent(); - let relay_location = parachain::AssetType::Xcm(relay_location_v3.clone()); - let relay_location_v5 = Location::parent(); - let source_relay_id: parachain::AssetId = relay_location.clone().into(); - - let relay_asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - let dest_para = Location::new(1, [Parachain(1)]); - - let sov = xcm_builder::SiblingParachainConvertsVia::< - polkadot_parachain::primitives::Sibling, - statemint_like::AccountId, - >::convert_location(&dest_para) - .unwrap(); - - ParaA::execute_with(|| { - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_relay_id, - relay_location_v5.clone(), - relay_asset_metadata.decimals, - relay_asset_metadata.symbol.try_into().expect("too long"), - relay_asset_metadata.name.try_into().expect("too long"), - )); - XcmWeightTrader::set_asset_price(Location::parent(), 0u128); - }); - - let parachain_beneficiary_absolute: Location = Junction::AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - - let statemint_beneficiary_absolute: Location = Junction::AccountId32 { - network: None, - id: RELAYALICE.into(), - } - .into(); - - // First we send relay chain asset to Alice in AssetHub (via teleport) - Relay::execute_with(|| { - assert_ok!(RelayChainPalletXcm::limited_teleport_assets( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1000).into()), - Box::new( - VersionedLocation::from(statemint_beneficiary_absolute) - .clone() - .into() - ), - Box::new(([], 200).into()), - 0, - WeightLimit::Unlimited - )); - }); - - // Send DOTs from AssetHub to ParaA (Moonbeam) - Statemint::execute_with(|| { - // Check Alice received 200 tokens on AssetHub - assert_eq!( - StatemintBalances::free_balance(RELAYALICE), - INITIAL_BALANCE + 200 - ); - - assert_ok!(StatemintBalances::transfer_allow_death( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - sov, - 110000000000000 - )); - - // Now send those tokens to ParaA - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_absolute.clone(), - }]); - assert_ok!( - StatemintChainPalletXcm::transfer_assets_using_type_and_then( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new((Location::parent(), 200).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - }); - - ParaA::execute_with(|| { - // Alice should have received the DOTs - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(200)) - ); - }); - - let dest = Location::new( - 1, - [ - Parachain(1000), - AccountId32 { - network: None, - id: RELAYBOB.into(), - }, - ], - ); - - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - let asset = Asset { - id: AssetId(Location::parent()), - fun: Fungibility::Fungible(100), - }; - // Finally we test that we are able to send back the DOTs to AssetHub from the ParaA - ParaA::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary, - }]); - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from(asset)), - Box::new(TransferType::DestinationReserve), - Box::new(fees_id), - Box::new(TransferType::DestinationReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) - )); - - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(100)) - ); - }); - - Statemint::execute_with(|| { - // Check that Bob received the tokens back in AssetHub - assert_eq!( - StatemintBalances::free_balance(RELAYBOB), - INITIAL_BALANCE + 100 - ); - }); - - // Send back tokens from AH to ParaA from Bob's account - Statemint::execute_with(|| { - // Now send those tokens to ParaA - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_absolute.clone(), - }]); - assert_ok!( - StatemintChainPalletXcm::transfer_assets_using_type_and_then( - statemint_like::RuntimeOrigin::signed(RELAYBOB), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new((Location::parent(), 100).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - - // 100 DOTs were deducted from Bob's account - assert_eq!(StatemintBalances::free_balance(RELAYBOB), INITIAL_BALANCE); - }); - - ParaA::execute_with(|| { - // Alice should have received 100 DOTs - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(200)) - ); - }); -} - -#[test] -fn send_dot_from_moonbeam_to_statemint_via_xtokens_transfer_multicurrencies() { - MockNet::reset(); - - // Relay asset - let relay_location_v3 = xcm::v3::Location::parent(); - let relay_location = parachain::AssetType::Xcm(relay_location_v3.clone()); - let relay_location_v5 = Location::parent(); - let source_relay_id: parachain::AssetId = relay_location.clone().into(); - - let relay_asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - // Statemint asset - let statemint_asset = Location::new( - 1, - [ - Parachain(1000u32), - PalletInstance(5u8), - GeneralIndex(10u128), - ], - ); - let statemint_location_asset: AssetType = statemint_asset - .clone() - .try_into() - .expect("Location convertion to AssetType should succeed"); - let source_statemint_asset_id: parachain::AssetId = statemint_location_asset.clone().into(); - - let asset_metadata_statemint_asset = parachain::AssetMetadata { - name: b"USDC".to_vec(), - symbol: b"USDC".to_vec(), - decimals: 12, - }; - - let dest_para = Location::new(1, [Parachain(1)]); - - let sov = xcm_builder::SiblingParachainConvertsVia::< - polkadot_parachain::primitives::Sibling, - statemint_like::AccountId, - >::convert_location(&dest_para) - .unwrap(); - - ParaA::execute_with(|| { - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_relay_id, - relay_location_v5.clone(), - relay_asset_metadata.decimals, - relay_asset_metadata.symbol.try_into().expect("too long"), - relay_asset_metadata.name.try_into().expect("too long"), - )); - XcmWeightTrader::set_asset_price(Location::parent(), 0u128); - - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_statemint_asset_id, - statemint_asset.clone(), - asset_metadata_statemint_asset.decimals, - asset_metadata_statemint_asset - .symbol - .try_into() - .expect("too long"), - asset_metadata_statemint_asset - .name - .try_into() - .expect("too long"), - )); - XcmWeightTrader::set_asset_price(statemint_asset, 0u128); - }); - - let parachain_beneficiary_absolute: Location = Junction::AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - - let statemint_beneficiary_absolute: Location = Junction::AccountId32 { - network: None, - id: RELAYALICE.into(), - } - .into(); - - // First we send relay chain asset to Alice in AssetHub (via teleport) - Relay::execute_with(|| { - assert_ok!(RelayChainPalletXcm::limited_teleport_assets( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1000).into()), - Box::new( - VersionedLocation::from(statemint_beneficiary_absolute) - .clone() - .into() - ), - Box::new(([], 200).into()), - 0, - WeightLimit::Unlimited - )); - }); - - // Send DOTs and USDC from AssetHub to ParaA (Moonbeam) - Statemint::execute_with(|| { - // Check Alice received 200 tokens on AssetHub - assert_eq!( - StatemintBalances::free_balance(RELAYALICE), - INITIAL_BALANCE + 200 - ); - - assert_ok!(StatemintBalances::transfer_allow_death( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - sov, - 110000000000000 - )); - - statemint_like::PrefixChanger::set_prefix( - PalletInstance(::index() as u8).into(), - ); - - assert_ok!(StatemintAssets::create( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - 10, - RELAYALICE, - 1 - )); - - assert_ok!(StatemintAssets::mint( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - 10, - RELAYALICE, - 300000000000000 - )); - - // Now send relay tokens to ParaA - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_absolute.clone(), - }]); - assert_ok!( - StatemintChainPalletXcm::transfer_assets_using_type_and_then( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new((Location::parent(), 200).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - - // Send USDC - let fees_id: VersionedAssetId = AssetId(Location::new( - 0, - [ - xcm::latest::prelude::PalletInstance( - ::index() as u8, - ), - GeneralIndex(10), - ], - )) - .into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_absolute.clone(), - }]); - assert_ok!( - StatemintChainPalletXcm::transfer_assets_using_type_and_then( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new( - ( - [ - xcm::latest::prelude::PalletInstance( - ::index() as u8 - ), - GeneralIndex(10), - ], - 125 - ) - .into() - ), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - }); - - ParaA::execute_with(|| { - // Alice should have received the DOTs - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(200)) - ); - - // Alice has received 125 USDC - assert_eq!( - EvmForeignAssets::balance(source_statemint_asset_id, PARAALICE.into()), - Ok(U256::from(125)) - ); - }); - - let dest = Location::new( - 1, - [ - Parachain(1000), - AccountId32 { - network: None, - id: RELAYBOB.into(), - }, - ], - ); - - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - // Finally we test that we are able to send back the DOTs to AssetHub from the ParaA - ParaA::execute_with(|| { - let asset = currency_to_asset( - parachain::CurrencyId::ForeignAsset(source_statemint_asset_id), - 100, - ); - let asset_fee = - currency_to_asset(parachain::CurrencyId::ForeignAsset(source_relay_id), 100); - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: beneficiary.clone(), - }]); - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from(vec![asset_fee, asset])), - Box::new(TransferType::DestinationReserve), - Box::new(fees_id), - Box::new(TransferType::DestinationReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(80_000_000u64, 100_000u64)) - )); - - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(100)) - ); - }); - - Statemint::execute_with(|| { - // Check that Bob received relay tokens back in AssetHub - assert_eq!( - StatemintBalances::free_balance(RELAYBOB), - INITIAL_BALANCE + 100 - ); - - // Check that BOB received 100 USDC on AssetHub - assert_eq!(StatemintAssets::account_balances(RELAYBOB), vec![(10, 100)]); - }); - - // Send back tokens from AH to ParaA from Bob's account - Statemint::execute_with(|| { - let bob_previous_balance = StatemintBalances::free_balance(RELAYBOB); - - // Now send those tokens to ParaA - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_absolute.clone(), - }]); - assert_ok!( - StatemintChainPalletXcm::transfer_assets_using_type_and_then( - statemint_like::RuntimeOrigin::signed(RELAYBOB), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new((Location::parent(), 100).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - - // 100 DOTs were deducted from Bob's account - assert_eq!( - StatemintBalances::free_balance(RELAYBOB), - bob_previous_balance - 100 - ); - }); - - ParaA::execute_with(|| { - // Alice should have received 100 DOTs - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(200)) - ); - }); -} - -#[test] -fn send_dot_from_moonbeam_to_statemint_via_xtokens_transfer_multiassets() { - MockNet::reset(); - - // Relay asset - let relay_location_v3 = xcm::v3::Location::parent(); - let relay_location = parachain::AssetType::Xcm(relay_location_v3.clone()); - let relay_location_v5 = Location::parent(); - let source_relay_id: parachain::AssetId = relay_location.clone().into(); - - let relay_asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - // Statemint asset - let statemint_asset = Location::new( - 1, - [ - Parachain(1000u32), - PalletInstance(5u8), - GeneralIndex(10u128), - ], - ); - let statemint_location_asset: AssetType = statemint_asset - .clone() - .try_into() - .expect("Location convertion to AssetType should succeed"); - let source_statemint_asset_id: parachain::AssetId = statemint_location_asset.clone().into(); - - let asset_metadata_statemint_asset = parachain::AssetMetadata { - name: b"USDC".to_vec(), - symbol: b"USDC".to_vec(), - decimals: 12, - }; - - let dest_para = Location::new(1, [Parachain(1)]); - - let sov = xcm_builder::SiblingParachainConvertsVia::< - polkadot_parachain::primitives::Sibling, - statemint_like::AccountId, - >::convert_location(&dest_para) - .unwrap(); - - ParaA::execute_with(|| { - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_relay_id, - relay_location_v5.clone(), - relay_asset_metadata.decimals, - relay_asset_metadata.symbol.try_into().expect("too long"), - relay_asset_metadata.name.try_into().expect("too long"), - )); - XcmWeightTrader::set_asset_price(Location::parent(), 0u128); - - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_statemint_asset_id, - statemint_asset.clone(), - asset_metadata_statemint_asset.decimals, - asset_metadata_statemint_asset - .symbol - .try_into() - .expect("too long"), - asset_metadata_statemint_asset - .name - .try_into() - .expect("too long"), - )); - XcmWeightTrader::set_asset_price(statemint_asset.clone(), 0u128); - }); - - let parachain_beneficiary_absolute: Location = Junction::AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - - let statemint_beneficiary_absolute: Location = Junction::AccountId32 { - network: None, - id: RELAYALICE.into(), - } - .into(); - - // First we send relay chain asset to Alice in AssetHub (via teleport) - Relay::execute_with(|| { - assert_ok!(RelayChainPalletXcm::limited_teleport_assets( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1000).into()), - Box::new( - VersionedLocation::from(statemint_beneficiary_absolute) - .clone() - .into() - ), - Box::new(([], 200).into()), - 0, - WeightLimit::Unlimited - )); - }); - - // Send DOTs and USDC from AssetHub to ParaA (Moonbeam) - Statemint::execute_with(|| { - // Check Alice received 200 tokens on AssetHub - assert_eq!( - StatemintBalances::free_balance(RELAYALICE), - INITIAL_BALANCE + 200 - ); - - assert_ok!(StatemintBalances::transfer_allow_death( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - sov, - 110000000000000 - )); - - statemint_like::PrefixChanger::set_prefix( - PalletInstance(::index() as u8).into(), - ); - - assert_ok!(StatemintAssets::create( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - 10, - RELAYALICE, - 1 - )); - - assert_ok!(StatemintAssets::mint( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - 10, - RELAYALICE, - 300000000000000 - )); - - // Now send relay tokens to ParaA - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_absolute.clone(), - }]); - assert_ok!( - StatemintChainPalletXcm::transfer_assets_using_type_and_then( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new((Location::parent(), 200).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - - // Send USDC - let fees_id: VersionedAssetId = AssetId(Location::new( - 0, - [ - xcm::latest::prelude::PalletInstance( - ::index() as u8, - ), - GeneralIndex(10), - ], - )) - .into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_absolute.clone(), - }]); - assert_ok!( - StatemintChainPalletXcm::transfer_assets_using_type_and_then( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new( - ( - [ - xcm::latest::prelude::PalletInstance( - ::index() as u8 - ), - GeneralIndex(10), - ], - 125 - ) - .into() - ), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - }); - - ParaA::execute_with(|| { - // Alice should have received the DOTs - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(200)) - ); - - // Alice has received 125 USDC - assert_eq!( - EvmForeignAssets::balance(source_statemint_asset_id, PARAALICE.into()), - Ok(U256::from(125)) - ); - }); - - let dest = Location::new( - 1, - [ - Parachain(1000), - AccountId32 { - network: None, - id: RELAYBOB.into(), - }, - ], - ); - - let statemint_asset_to_send = Asset { - id: AssetId(statemint_asset), - fun: Fungibility::Fungible(100), - }; - - let relay_asset_to_send = Asset { - id: AssetId(Location::parent()), - fun: Fungibility::Fungible(100), - }; - - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - let assets_to_send: XcmAssets = - XcmAssets::from(vec![statemint_asset_to_send, relay_asset_to_send.clone()]); - - // For some reason the order of the assets is inverted when creating the array above. - // We need to use relay asset for fees, so we pick index 0. - assert_eq!(assets_to_send.get(0).unwrap(), &relay_asset_to_send); - - // Finally we test that we are able to send back the DOTs to AssetHub from the ParaA - ParaA::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary, - }]); - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from(assets_to_send)), - Box::new(TransferType::DestinationReserve), - Box::new(fees_id), - Box::new(TransferType::DestinationReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(80_000_000u64, 100_000u64)) - )); - - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(100)) - ); - }); - - Statemint::execute_with(|| { - // Check that Bob received relay tokens back in AssetHub - assert_eq!( - StatemintBalances::free_balance(RELAYBOB), - INITIAL_BALANCE + 100 - ); - - // Check that BOB received 100 USDC on AssetHub - assert_eq!(StatemintAssets::account_balances(RELAYBOB), vec![(10, 100)]); - }); - - // Send back tokens from AH to ParaA from Bob's account - Statemint::execute_with(|| { - let bob_previous_balance = StatemintBalances::free_balance(RELAYBOB); - - // Now send those tokens to ParaA - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_absolute.clone(), - }]); - assert_ok!( - StatemintChainPalletXcm::transfer_assets_using_type_and_then( - statemint_like::RuntimeOrigin::signed(RELAYBOB), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new((Location::parent(), 100).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - - // 100 DOTs were deducted from Bob's account - assert_eq!( - StatemintBalances::free_balance(RELAYBOB), - bob_previous_balance - 100 - ); - }); - - ParaA::execute_with(|| { - // Alice should have received 100 DOTs - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(200)) - ); - }); -} - -#[test] -fn transact_through_signed_multilocation() { - MockNet::reset(); - let mut ancestry = Location::parent(); - - ParaA::execute_with(|| { - // Root can set transact info - assert_ok!(XcmTransactor::set_transact_info( - parachain::RuntimeOrigin::root(), - Box::new(xcm::VersionedLocation::from(Location::parent())), - // Relay charges 1000 for every instruction, and we have 3, so 3000 - 3000.into(), - 20000000000.into(), - // 4 instructions in transact through signed - Some(4000.into()) - )); - // Root can set transact info - // Set fee per second using weight-trader (replaces old set_fee_per_second) - set_fee_per_second_for_location(Location::parent(), WEIGHT_REF_TIME_PER_SECOND as u128) - .expect("must succeed"); - ancestry = parachain::UniversalLocation::get().into(); - }); - - // Let's construct the Junction that we will append with DescendOrigin - let signed_origin: Junctions = [AccountKey20 { - network: None, - key: PARAALICE, - }] - .into(); - - let mut descend_origin_multilocation = parachain::SelfLocation::get(); - descend_origin_multilocation - .append_with(signed_origin) - .unwrap(); - - // To convert it to what the relay will see instead of us - descend_origin_multilocation - .reanchor(&Location::parent(), &ancestry.interior) - .unwrap(); - - let derived = xcm_builder::Account32Hash::< - relay_chain::KusamaNetwork, - relay_chain::AccountId, - >::convert_location(&descend_origin_multilocation) - .unwrap(); - - Relay::execute_with(|| { - // free execution, full amount received - assert_ok!(RelayBalances::transfer_allow_death( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - derived.clone(), - 4000004100u128, - )); - // derived account has all funds - assert!(RelayBalances::free_balance(&derived) == 4000004100); - // sovereign account has 0 funds - assert!(RelayBalances::free_balance(¶_a_account()) == 0); - }); - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = ::PalletInfo::index::< - relay_chain::Balances, - >() - .unwrap() as u8; - - encoded.push(index); - - // Then call bytes - let mut call_bytes = pallet_balances::Call::::transfer_allow_death { - // 100 to sovereign - dest: para_a_account(), - value: 100u32.into(), - } - .encode(); - encoded.append(&mut call_bytes); - - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::transact_through_signed( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(xcm::VersionedLocation::from(Location::parent())), - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - Location::parent() - ))), - fee_amount: None - }, - encoded, - // 4000000000 for transfer + 4000 for XCM - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: None - }, - false - )); - }); - - Relay::execute_with(|| { - assert!(RelayBalances::free_balance(¶_a_account()) == 100); - - assert!(RelayBalances::free_balance(&derived) == 0); - }); -} - -#[test] -fn transact_through_signed_multilocation_custom_fee_and_weight() { - MockNet::reset(); - let mut ancestry = Location::parent(); - - ParaA::execute_with(|| { - ancestry = parachain::UniversalLocation::get().into(); - }); - - // Let's construct the Junction that we will append with DescendOrigin - let signed_origin: Junctions = [AccountKey20 { - network: None, - key: PARAALICE, - }] - .into(); - - let mut descend_origin_multilocation = parachain::SelfLocation::get(); - descend_origin_multilocation - .append_with(signed_origin) - .unwrap(); - - // To convert it to what the relay will see instead of us - descend_origin_multilocation - .reanchor(&Location::parent(), &ancestry.interior) - .unwrap(); - - let derived = xcm_builder::Account32Hash::< - relay_chain::KusamaNetwork, - relay_chain::AccountId, - >::convert_location(&descend_origin_multilocation) - .unwrap(); - - Relay::execute_with(|| { - // free execution, full amount received - assert_ok!(RelayBalances::transfer_allow_death( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - derived.clone(), - 4000004100u128, - )); - // derived account has all funds - assert!(RelayBalances::free_balance(&derived) == 4000004100); - // sovereign account has 0 funds - assert!(RelayBalances::free_balance(¶_a_account()) == 0); - }); - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = ::PalletInfo::index::< - relay_chain::Balances, - >() - .unwrap() as u8; - - encoded.push(index); - - // Then call bytes - let mut call_bytes = pallet_balances::Call::::transfer_allow_death { - // 100 to sovereign - dest: para_a_account(), - value: 100u32.into(), - } - .encode(); - encoded.append(&mut call_bytes); - - let total_weight = 4000004000u64; - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::transact_through_signed( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(xcm::VersionedLocation::from(Location::parent())), - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - Location::parent() - ))), - fee_amount: Some(total_weight as u128) - }, - encoded, - // 4000000000 for transfer + 4000 for XCM - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: Some(Limited(total_weight.into())) - }, - false - )); - }); - - Relay::execute_with(|| { - assert!(RelayBalances::free_balance(¶_a_account()) == 100); - - assert!(RelayBalances::free_balance(&derived) == 0); - }); -} - -#[test] -fn transact_through_signed_multilocation_custom_fee_and_weight_refund() { - MockNet::reset(); - let mut ancestry = Location::parent(); - - ParaA::execute_with(|| { - ancestry = parachain::UniversalLocation::get().into(); - }); - - // Let's construct the Junction that we will append with DescendOrigin - let signed_origin: Junctions = [AccountKey20 { - network: None, - key: PARAALICE, - }] - .into(); - - let mut descend_origin_multilocation = parachain::SelfLocation::get(); - descend_origin_multilocation - .append_with(signed_origin) - .unwrap(); - - // To convert it to what the relay will see instead of us - descend_origin_multilocation - .reanchor(&Location::parent(), &ancestry.interior) - .unwrap(); - - let derived = xcm_builder::Account32Hash::< - relay_chain::KusamaNetwork, - relay_chain::AccountId, - >::convert_location(&descend_origin_multilocation) - .unwrap(); - - Relay::execute_with(|| { - // free execution, full amount received - assert_ok!(RelayBalances::transfer_allow_death( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - derived.clone(), - 4000009100u128, - )); - // derived account has all funds - assert!(RelayBalances::free_balance(&derived) == 4000009100); - // sovereign account has 0 funds - assert!(RelayBalances::free_balance(¶_a_account()) == 0); - }); - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = ::PalletInfo::index::< - relay_chain::Balances, - >() - .unwrap() as u8; - - encoded.push(index); - - // Then call bytes - let mut call_bytes = pallet_balances::Call::::transfer_allow_death { - // 100 to sovereign - dest: para_a_account(), - value: 100u32.into(), - } - .encode(); - encoded.append(&mut call_bytes); - - let total_weight = 4000009000u64; - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::transact_through_signed( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(xcm::VersionedLocation::from(Location::parent())), - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - Location::parent() - ))), - fee_amount: Some(total_weight as u128) - }, - encoded, - // 4000000000 for transfer + 9000 for XCM - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: Some(Limited(total_weight.into())) - }, - true - )); - }); - - Relay::execute_with(|| { - // 100 transferred - assert_eq!(RelayBalances::free_balance(¶_a_account()), 100); - - // 4000009000 refunded - assert_eq!(RelayBalances::free_balance(&derived), 4000009000); - }); -} - -#[test] -fn transact_through_signed_multilocation_para_to_para() { - frame_support::__private::sp_tracing::init_for_tests(); - MockNet::reset(); - let mut ancestry = Location::parent(); - - let para_b_location = Location::new(1, [Parachain(2)]); - - let para_b_balances = Location::new(1, [Parachain(2), PalletInstance(1u8)]); - - ParaA::execute_with(|| { - // Root can set transact info - assert_ok!(XcmTransactor::set_transact_info( - parachain::RuntimeOrigin::root(), - // ParaB - Box::new(xcm::VersionedLocation::from(para_b_location.clone())), - // Para charges 1000 for every instruction, and we have 3, so 3 - 3.into(), - 20000000000.into(), - // 4 instructions in transact through signed - Some(4.into()) - )); - // Root can set transact info - // Set fee per second using weight-trader (replaces old set_fee_per_second) - set_fee_per_second_for_location( - para_b_balances.clone(), - parachain::ParaTokensPerSecond::get(), - ) - .expect("must succeed"); - ancestry = parachain::UniversalLocation::get().into(); - }); - - // Let's construct the Junction that we will append with DescendOrigin - let signed_origin: Junctions = [AccountKey20 { - network: None, - key: PARAALICE, - }] - .into(); - - let mut descend_origin_multilocation = parachain::SelfLocation::get(); - descend_origin_multilocation - .append_with(signed_origin) - .unwrap(); - - // To convert it to what the paraB will see instead of us - descend_origin_multilocation - .reanchor(¶_b_location, &ancestry.interior) - .unwrap(); - - let derived = xcm_builder::HashedDescription::< - parachain::AccountId, - xcm_builder::DescribeFamily, - >::convert_location(&descend_origin_multilocation) - .unwrap(); - - ParaB::execute_with(|| { - // free execution, full amount received - assert_ok!(ParaBalances::transfer_allow_death( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - derived.clone(), - 4000000104u128, - )); - // derived account has all funds - assert!(ParaBalances::free_balance(&derived) == 4000000104); - // sovereign account has 0 funds - assert!(ParaBalances::free_balance(¶_a_account_20()) == 0); - }); - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = - ::PalletInfo::index::() - .unwrap() as u8; - - encoded.push(index); - - // Then call bytes - let mut call_bytes = pallet_balances::Call::::transfer_allow_death { - // 100 to sovereign - dest: para_a_account_20(), - value: 100u32.into(), - } - .encode(); - encoded.append(&mut call_bytes); - - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::transact_through_signed( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(xcm::VersionedLocation::from(para_b_location)), - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - para_b_balances - ))), - fee_amount: None - }, - encoded, - // 4000000000 for transfer + 4000 for XCM - // 1-1 to fee - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: None - }, - false - )); - }); - - ParaB::execute_with(|| { - assert_eq!(ParaBalances::free_balance(&derived), 0); - - assert_eq!(ParaBalances::free_balance(¶_a_account_20()), 100); - }); -} - -#[test] -fn transact_through_signed_multilocation_para_to_para_refund() { - MockNet::reset(); - let mut ancestry = Location::parent(); - - let para_b_location = Location::new(1, [Parachain(2)]); - - let para_b_balances = Location::new(1, [Parachain(2), PalletInstance(1u8)]); - - ParaA::execute_with(|| { - // Set fee per second using weight-trader (replaces old set_fee_per_second) - set_fee_per_second_for_location( - para_b_balances.clone(), - parachain::ParaTokensPerSecond::get(), - ) - .expect("must succeed"); - ancestry = parachain::UniversalLocation::get().into(); - }); - - // Let's construct the Junction that we will append with DescendOrigin - let signed_origin: Junctions = [AccountKey20 { - network: None, - key: PARAALICE, - }] - .into(); - - let mut descend_origin_multilocation = parachain::SelfLocation::get(); - descend_origin_multilocation - .append_with(signed_origin) - .unwrap(); - - // To convert it to what the paraB will see instead of us - descend_origin_multilocation - .reanchor(¶_b_location, &ancestry.interior) - .unwrap(); - - let derived = xcm_builder::HashedDescription::< - parachain::AccountId, - xcm_builder::DescribeFamily, - >::convert_location(&descend_origin_multilocation) - .unwrap(); - - ParaB::execute_with(|| { - // free execution, full amount received - assert_ok!(ParaBalances::transfer_allow_death( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - derived.clone(), - 4000009100u128, - )); - // derived account has all funds - assert!(ParaBalances::free_balance(&derived) == 4000009100); - // sovereign account has 0 funds - assert!(ParaBalances::free_balance(¶_a_account_20()) == 0); - }); - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = - ::PalletInfo::index::() - .unwrap() as u8; - - encoded.push(index); - - // Then call bytes - let mut call_bytes = pallet_balances::Call::::transfer_allow_death { - // 100 to sovereign - dest: para_a_account_20(), - value: 100u32.into(), - } - .encode(); - encoded.append(&mut call_bytes); - - let overall_weight = 4000009000u64; - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::transact_through_signed( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(xcm::VersionedLocation::from(para_b_location)), - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - para_b_balances - ))), - fee_amount: Some(overall_weight as u128) - }, - encoded, - // 4000000000 for transfer + 9000 for XCM - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: Some(Limited(overall_weight.into())) - }, - true - )); - }); - - ParaB::execute_with(|| { - // Check the derived account was refunded - assert_eq!(ParaBalances::free_balance(&derived), 3826174993); - - // Check the transfer was executed - assert_eq!(ParaBalances::free_balance(¶_a_account_20()), 100); - }); -} - -#[test] -fn transact_through_signed_multilocation_para_to_para_ethereum() { - MockNet::reset(); - let mut ancestry = Location::parent(); - - let para_b_location = Location::new(1, [Parachain(2)]); - - let para_b_balances = Location::new(1, [Parachain(2), PalletInstance(1u8)]); - - ParaA::execute_with(|| { - // Root can set transact info - assert_ok!(XcmTransactor::set_transact_info( - parachain::RuntimeOrigin::root(), - // ParaB - Box::new(xcm::VersionedLocation::from(para_b_location.clone())), - // Para charges 1000 for every instruction, and we have 3, so 3 - 3.into(), - 20000000000.into(), - // 4 instructions in transact through signed - Some(4.into()) - )); - // Root can set transact info - // Set fee per second using weight-trader (replaces old set_fee_per_second) - set_fee_per_second_for_location( - para_b_balances.clone(), - parachain::ParaTokensPerSecond::get(), - ) - .expect("must succeed"); - ancestry = parachain::UniversalLocation::get().into(); - }); - - // Let's construct the Junction that we will append with DescendOrigin - let signed_origin: Junctions = [AccountKey20 { - network: None, - key: PARAALICE, - }] - .into(); - - let mut descend_origin_multilocation = parachain::SelfLocation::get(); - descend_origin_multilocation - .append_with(signed_origin) - .unwrap(); - - // To convert it to what the paraB will see instead of us - descend_origin_multilocation - .reanchor(¶_b_location, &ancestry.interior) - .unwrap(); - - let derived = xcm_builder::HashedDescription::< - parachain::AccountId, - xcm_builder::DescribeFamily, - >::convert_location(&descend_origin_multilocation) - .unwrap(); - - let mut parachain_b_alice_balances_before = 0; - ParaB::execute_with(|| { - assert_ok!(ParaBalances::transfer_allow_death( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - derived.clone(), - 4000000104u128, - )); - // derived account has all funds - assert!(ParaBalances::free_balance(&derived) == 4000000104); - // sovereign account has 0 funds - assert!(ParaBalances::free_balance(¶_a_account_20()) == 0); - - parachain_b_alice_balances_before = ParaBalances::free_balance(&PARAALICE.into()) - }); - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = - ::PalletInfo::index::() - .unwrap() as u8; - - encoded.push(index); - - use sp_core::U256; - // Let's do a EVM transfer - let eth_tx = - xcm_primitives::EthereumXcmTransaction::V1(xcm_primitives::EthereumXcmTransactionV1 { - gas_limit: U256::from(21000), - fee_payment: xcm_primitives::EthereumXcmFee::Auto, - action: pallet_ethereum::TransactionAction::Call(PARAALICE.into()), - value: U256::from(100), - input: BoundedVec::< - u8, - ConstU32<{ xcm_primitives::MAX_ETHEREUM_XCM_INPUT_SIZE }> - >::try_from(vec![]).unwrap(), - access_list: None, - }); - - // Then call bytes - let mut call_bytes = pallet_ethereum_xcm::Call::::transact { - xcm_transaction: eth_tx, - } - .encode(); - encoded.append(&mut call_bytes); - - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::transact_through_signed( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(xcm::VersionedLocation::from(para_b_location)), - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - para_b_balances - ))), - fee_amount: None - }, - encoded, - // 4000000000 for transfer + 4000 for XCM - // 1-1 to fee - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: None - }, - false - )); - }); - - ParaB::execute_with(|| { - // Make sure the EVM transfer went through - assert!( - ParaBalances::free_balance(&PARAALICE.into()) - == parachain_b_alice_balances_before + 100 - ); - }); -} - -#[test] -fn transact_through_signed_multilocation_para_to_para_ethereum_no_proxy_fails() { - MockNet::reset(); - let mut ancestry = Location::parent(); - - let para_b_location = Location::new(1, [Parachain(2)]); - - let para_b_balances = Location::new(1, [Parachain(2), PalletInstance(1u8)]); - - ParaA::execute_with(|| { - // Root can set transact info - assert_ok!(XcmTransactor::set_transact_info( - parachain::RuntimeOrigin::root(), - // ParaB - Box::new(xcm::VersionedLocation::from(para_b_location.clone())), - // Para charges 1000 for every instruction, and we have 3, so 3 - 3.into(), - 20000000000.into(), - // 4 instructions in transact through signed - Some(4.into()) - )); - // Root can set transact info - // Set fee per second using weight-trader (replaces old set_fee_per_second) - set_fee_per_second_for_location( - para_b_balances.clone(), - parachain::ParaTokensPerSecond::get(), - ) - .expect("must succeed"); - ancestry = parachain::UniversalLocation::get().into(); - }); - - // Let's construct the Junction that we will append with DescendOrigin - let signed_origin: Junctions = [AccountKey20 { - network: None, - key: PARAALICE, - }] - .into(); - - let mut descend_origin_multilocation = parachain::SelfLocation::get(); - descend_origin_multilocation - .append_with(signed_origin) - .unwrap(); - - // To convert it to what the paraB will see instead of us - descend_origin_multilocation - .reanchor(¶_b_location, &ancestry.interior) - .unwrap(); - - let derived = xcm_builder::HashedDescription::< - parachain::AccountId, - xcm_builder::DescribeFamily, - >::convert_location(&descend_origin_multilocation) - .unwrap(); - - let mut parachain_b_alice_balances_before = 0; - ParaB::execute_with(|| { - assert_ok!(ParaBalances::transfer_allow_death( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - derived.clone(), - 4000000104u128, - )); - // derived account has all funds - assert!(ParaBalances::free_balance(&derived) == 4000000104); - // sovereign account has 0 funds - assert!(ParaBalances::free_balance(¶_a_account_20()) == 0); - - parachain_b_alice_balances_before = ParaBalances::free_balance(&PARAALICE.into()) - }); - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = - ::PalletInfo::index::() - .unwrap() as u8; - - encoded.push(index); - - use sp_core::U256; - // Let's do a EVM transfer - let eth_tx = - xcm_primitives::EthereumXcmTransaction::V1(xcm_primitives::EthereumXcmTransactionV1 { - gas_limit: U256::from(21000), - fee_payment: xcm_primitives::EthereumXcmFee::Auto, - action: pallet_ethereum::TransactionAction::Call(PARAALICE.into()), - value: U256::from(100), - input: BoundedVec::< - u8, - ConstU32<{ xcm_primitives::MAX_ETHEREUM_XCM_INPUT_SIZE }> - >::try_from(vec![]).unwrap(), - access_list: None, - }); - - // Then call bytes - let mut call_bytes = pallet_ethereum_xcm::Call::::transact_through_proxy { - transact_as: PARAALICE.into(), - xcm_transaction: eth_tx, - } - .encode(); - encoded.append(&mut call_bytes); - - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::transact_through_signed( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(xcm::VersionedLocation::from(para_b_location)), - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - para_b_balances - ))), - fee_amount: None - }, - encoded, - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: None - }, - false - )); - }); - - ParaB::execute_with(|| { - // Make sure the EVM transfer wasn't executed - assert!(ParaBalances::free_balance(&PARAALICE.into()) == parachain_b_alice_balances_before); - }); -} - -#[test] -fn transact_through_signed_multilocation_para_to_para_ethereum_proxy_succeeds() { - MockNet::reset(); - let mut ancestry = Location::parent(); - - let para_b_location = Location::new(1, [Parachain(2)]); - - let para_b_balances = Location::new(1, [Parachain(2), PalletInstance(1u8)]); - - ParaA::execute_with(|| { - // Root can set transact info - assert_ok!(XcmTransactor::set_transact_info( - parachain::RuntimeOrigin::root(), - // ParaB - Box::new(xcm::VersionedLocation::from(para_b_location.clone())), - // Para charges 1000 for every instruction, and we have 3, so 3 - 3.into(), - 20000000000.into(), - // 4 instructions in transact through signed - Some(4.into()) - )); - // Root can set transact info - // Set fee per second using weight-trader (replaces old set_fee_per_second) - set_fee_per_second_for_location( - para_b_balances.clone(), - parachain::ParaTokensPerSecond::get(), - ) - .expect("must succeed"); - ancestry = parachain::UniversalLocation::get().into(); - }); - - // Let's construct the Junction that we will append with DescendOrigin - let signed_origin: Junctions = [AccountKey20 { - network: None, - key: PARAALICE, - }] - .into(); - - let mut descend_origin_multilocation = parachain::SelfLocation::get(); - descend_origin_multilocation - .append_with(signed_origin) - .unwrap(); - - // To convert it to what the paraB will see instead of us - descend_origin_multilocation - .reanchor(¶_b_location, &ancestry.interior) - .unwrap(); - - let derived = xcm_builder::HashedDescription::< - parachain::AccountId, - xcm_builder::DescribeFamily, - >::convert_location(&descend_origin_multilocation) - .unwrap(); - - let transfer_recipient = evm_account(); - let mut transfer_recipient_balance_before = 0; - ParaB::execute_with(|| { - assert_ok!(ParaBalances::transfer_allow_death( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - derived.clone(), - 4000000104u128, - )); - // derived account has all funds - assert!(ParaBalances::free_balance(&derived) == 4000000104); - // sovereign account has 0 funds - assert!(ParaBalances::free_balance(¶_a_account_20()) == 0); - - transfer_recipient_balance_before = ParaBalances::free_balance(&transfer_recipient.into()); - - // Add proxy ALICE -> derived - let _ = parachain::Proxy::add_proxy_delegate( - &PARAALICE.into(), - derived, - parachain::ProxyType::Any, - 0, - ); - }); - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = - ::PalletInfo::index::() - .unwrap() as u8; - - encoded.push(index); - - use sp_core::U256; - // Let's do a EVM transfer - let eth_tx = - xcm_primitives::EthereumXcmTransaction::V2(xcm_primitives::EthereumXcmTransactionV2 { - gas_limit: U256::from(21000), - action: pallet_ethereum::TransactionAction::Call(transfer_recipient.into()), - value: U256::from(100), - input: BoundedVec::< - u8, - ConstU32<{ xcm_primitives::MAX_ETHEREUM_XCM_INPUT_SIZE }> - >::try_from(vec![]).unwrap(), - access_list: None, - }); - - // Then call bytes - let mut call_bytes = pallet_ethereum_xcm::Call::::transact_through_proxy { - transact_as: PARAALICE.into(), - xcm_transaction: eth_tx, - } - .encode(); - encoded.append(&mut call_bytes); - - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::transact_through_signed( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(xcm::VersionedLocation::from(para_b_location)), - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - para_b_balances - ))), - fee_amount: None - }, - encoded, - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: None - }, - false - )); - }); - - ParaB::execute_with(|| { - // Make sure the EVM transfer was executed - assert!( - ParaBalances::free_balance(&transfer_recipient.into()) - == transfer_recipient_balance_before + 100 - ); - }); -} - -#[test] -fn hrmp_init_accept_through_root() { - MockNet::reset(); - - Relay::execute_with(|| { - assert_ok!(RelayBalances::transfer_allow_death( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - para_a_account(), - 1000u128 - )); - assert_ok!(RelayBalances::transfer_allow_death( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - para_b_account(), - 1000u128 - )); - }); - - ParaA::execute_with(|| { - let total_fee = 1_000u128; - let total_weight: u64 = 1_000_000_000; - let tx_weight: u64 = 500_000_000; - // Root can send hrmp init channel - assert_ok!(XcmTransactor::hrmp_manage( - parachain::RuntimeOrigin::root(), - HrmpOperation::InitOpen(HrmpInitParams { - para_id: 2u32.into(), - proposed_max_capacity: 1, - proposed_max_message_size: 1 - }), - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - Location::parent() - ))), - fee_amount: Some(total_fee) - }, - TransactWeights { - transact_required_weight_at_most: tx_weight.into(), - overall_weight: Some(Limited(total_weight.into())) - } - )); - }); - Relay::execute_with(|| { - let expected_event: relay_chain::RuntimeEvent = - polkadot_runtime_parachains::hrmp::Event::OpenChannelRequested { - sender: 1u32.into(), - recipient: 2u32.into(), - proposed_max_capacity: 1u32, - proposed_max_message_size: 1u32, - } - .into(); - assert!(relay_chain::relay_events().contains(&expected_event)); - }); - ParaB::execute_with(|| { - let total_fee = 1_000u128; - let total_weight: u64 = 1_000_000_000; - let tx_weight: u64 = 500_000_000; - // Root can send hrmp accept channel - assert_ok!(XcmTransactor::hrmp_manage( - parachain::RuntimeOrigin::root(), - HrmpOperation::Accept { - para_id: 1u32.into() - }, - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - Location::parent() - ))), - fee_amount: Some(total_fee) - }, - TransactWeights { - transact_required_weight_at_most: tx_weight.into(), - overall_weight: Some(Limited(total_weight.into())) - } - )); - }); - - Relay::execute_with(|| { - let expected_event: relay_chain::RuntimeEvent = - polkadot_runtime_parachains::hrmp::Event::OpenChannelAccepted { - sender: 1u32.into(), - recipient: 2u32.into(), - } - .into(); - assert!(relay_chain::relay_events().contains(&expected_event)); - }); -} - -#[test] -fn hrmp_close_works() { - MockNet::reset(); - - Relay::execute_with(|| { - assert_ok!(RelayBalances::transfer_allow_death( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - para_a_account(), - 1000u128 - )); - assert_ok!(Hrmp::force_open_hrmp_channel( - relay_chain::RuntimeOrigin::root(), - 1u32.into(), - 2u32.into(), - 1u32, - 1u32 - )); - assert_ok!(Hrmp::force_process_hrmp_open( - relay_chain::RuntimeOrigin::root(), - 1u32 - )); - }); - - ParaA::execute_with(|| { - let total_fee = 1_000u128; - let total_weight: u64 = 1_000_000_000; - let tx_weight: u64 = 500_000_000; - assert_ok!(XcmTransactor::hrmp_manage( - parachain::RuntimeOrigin::root(), - HrmpOperation::Close(HrmpChannelId { - sender: 1u32.into(), - recipient: 2u32.into() - }), - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - Location::parent() - ))), - fee_amount: Some(total_fee) - }, - TransactWeights { - transact_required_weight_at_most: tx_weight.into(), - overall_weight: Some(Limited(total_weight.into())) - } - )); - }); - Relay::execute_with(|| { - let expected_event: relay_chain::RuntimeEvent = - polkadot_runtime_parachains::hrmp::Event::ChannelClosed { - by_parachain: 1u32.into(), - channel_id: HrmpChannelId { - sender: 1u32.into(), - recipient: 2u32.into(), - }, - } - .into(); - assert!(relay_chain::relay_events().contains(&expected_event)); - }); -} - -use crate::xcm_mock::parachain::{EvmForeignAssets, MockTransactors, Treasury, XcmWeightTrader}; -use parity_scale_codec::{Decode, Encode}; -use sp_core::U256; -use sp_io::hashing::blake2_256; - -// Helper to derive accountIds -pub fn derivative_account_id(who: sp_runtime::AccountId32, index: u16) -> sp_runtime::AccountId32 { - let entropy = (b"modlpy/utilisuba", who, index).using_encoded(blake2_256); - sp_runtime::AccountId32::decode(&mut &entropy[..]).expect("valid account id") -} diff --git a/runtime/moonbeam/Cargo.toml b/runtime/moonbeam/Cargo.toml index c92315304a4..6da7b917ee8 100644 --- a/runtime/moonbeam/Cargo.toml +++ b/runtime/moonbeam/Cargo.toml @@ -202,8 +202,12 @@ cumulus-primitives-parachain-inherent = { workspace = true } cumulus-test-relay-sproof-builder = { workspace = true } sp-timestamp = { workspace = true } +parachains-common = { workspace = true, features = ["std"] } polkadot-runtime-parachains = { workspace = true } -xcm-simulator = { workspace = true } +pallet-delegated-staking = { workspace = true } +asset-hub-westend-runtime = { workspace = true } +westend-runtime = { workspace = true } +xcm-emulator = { workspace = true } precompile-utils = { workspace = true, features = ["std", "testing"] } moonbeam-tests-primitives = { workspace = true } @@ -473,7 +477,9 @@ runtime-benchmarks = [ "pallet-evm-precompile-xcm-transactor/runtime-benchmarks", "pallet-evm-precompile-xcm-utils/runtime-benchmarks", "pallet-evm-precompile-xtokens/runtime-benchmarks", - "xcm-primitives/runtime-benchmarks" + "xcm-primitives/runtime-benchmarks", + "pallet-delegated-staking/runtime-benchmarks", + "westend-runtime/runtime-benchmarks" ] try-runtime = [ diff --git a/runtime/moonbeam/tests/xcm_config/barriers.rs b/runtime/moonbeam/tests/xcm_config/barriers.rs new file mode 100644 index 00000000000..c248ffaed3d --- /dev/null +++ b/runtime/moonbeam/tests/xcm_config/barriers.rs @@ -0,0 +1,408 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Tests for XcmBarrier configuration. +//! +//! The barrier determines which XCM messages are allowed to execute. +//! Moonbeam's barrier allows: +//! - TakeWeightCredit: Messages that consume credited weight +//! - AllowKnownQueryResponses: Expected query responses +//! - AllowTopLevelPaidExecutionFrom: Paid execution from any origin +//! - AllowSubscriptionsFrom: Version subscription messages + +use crate::xcm_common::*; +use moonbeam_runtime::{Runtime, RuntimeCall}; +use parity_scale_codec::Encode; +use xcm::latest::prelude::*; +use xcm_executor::traits::QueryHandler; + +#[test] +fn barrier_allows_paid_execution_from_relay() { + ExtBuilder::default().build().execute_with(|| { + let origin = Location::parent(); + let message: Xcm = Xcm(vec![ + WithdrawAsset((Location::parent(), ONE_DOT).into()), + BuyExecution { + fees: (Location::parent(), ONE_DOT).into(), + weight_limit: WeightLimit::Unlimited, + }, + DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: ALICE, + }], + ), + }, + ]); + + let outcome = execute_xcm(origin, message); + // Should not be blocked by barrier (may fail later due to no funds, but not barrier) + assert!(!is_barrier_error(&outcome)); + }); +} + +#[test] +fn barrier_allows_paid_execution_from_sibling() { + ExtBuilder::default().build().execute_with(|| { + let origin = Location::new(1, [Parachain(2000)]); + let message: Xcm = Xcm(vec![ + WithdrawAsset((Location::parent(), ONE_DOT).into()), + BuyExecution { + fees: (Location::parent(), ONE_DOT).into(), + weight_limit: WeightLimit::Unlimited, + }, + DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: BOB, + }], + ), + }, + ]); + + let outcome = execute_xcm(origin, message); + assert!(!is_barrier_error(&outcome)); + }); +} + +#[test] +fn barrier_passes_unpaid_with_weight_credit() { + ExtBuilder::default().build().execute_with(|| { + // TakeWeightCredit is the first barrier and passes when the message + // weight is within the pre-credited amount. `execute_xcm` passes + // Weight::zero() as credit, so unpaid messages are rejected there. + // Use `execute_xcm_with_credit` with a generous credit instead. + let origin = Location::parent(); + let message: Xcm = Xcm(vec![DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: ALICE, + }], + ), + }]); + + let outcome = execute_xcm_with_credit(origin, message, Weight::MAX); + // TakeWeightCredit allows this to pass the barrier (may fail later for other reasons) + assert!( + !is_barrier_error(&outcome), + "TakeWeightCredit should allow messages with credited weight" + ); + }); +} + +#[test] +fn barrier_allows_subscription_messages() { + ExtBuilder::default().build().execute_with(|| { + let origin = Location::parent(); + // SubscribeVersion is allowed by AllowSubscriptionsFrom + let message: Xcm = Xcm(vec![SubscribeVersion { + query_id: 0, + max_response_weight: Weight::from_parts(1_000_000, 64 * 1024), + }]); + + let outcome = execute_xcm(origin, message); + // Should not be a barrier error - subscriptions are allowed + assert!(!is_barrier_error(&outcome)); + }); +} + +#[test] +fn barrier_allows_unsubscribe_messages() { + ExtBuilder::default().build().execute_with(|| { + let origin = Location::parent(); + let message: Xcm = Xcm(vec![UnsubscribeVersion]); + + let outcome = execute_xcm(origin, message); + assert!(!is_barrier_error(&outcome)); + }); +} + +#[test] +fn barrier_allows_paid_execution_with_descend_origin() { + ExtBuilder::default().build().execute_with(|| { + // Test that WithComputedOrigin allows descending origin + let origin = Location::parent(); + let message: Xcm = Xcm(vec![ + DescendOrigin( + [AccountId32 { + network: None, + id: [1u8; 32], + }] + .into(), + ), + WithdrawAsset((Location::parent(), ONE_DOT).into()), + BuyExecution { + fees: (Location::parent(), ONE_DOT).into(), + weight_limit: WeightLimit::Unlimited, + }, + DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: ALICE, + }], + ), + }, + ]); + + let outcome = execute_xcm(origin, message); + assert!(!is_barrier_error(&outcome)); + }); +} + +#[test] +fn barrier_allows_set_topic() { + ExtBuilder::default().build().execute_with(|| { + // SetTopic is wrapped by TrailingSetTopicAsId so should be handled + let origin = Location::parent(); + let message: Xcm = Xcm(vec![ + WithdrawAsset((Location::parent(), ONE_DOT).into()), + BuyExecution { + fees: (Location::parent(), ONE_DOT).into(), + weight_limit: WeightLimit::Unlimited, + }, + SetTopic([0u8; 32]), + ]); + + let outcome = execute_xcm(origin, message); + assert!(!is_barrier_error(&outcome)); + }); +} + +#[test] +fn barrier_with_computed_origin_rejects_when_depth_limit_exceeded() { + ExtBuilder::default().build().execute_with(|| { + // WithComputedOrigin is configured with ConstU32<8>, meaning it will skip at most 8 + // DescendOrigin (or similar) instructions when computing the origin. If the message + // contains more than 8 such instructions, WithComputedOrigin cannot reach the inner + // barriers (AllowTopLevelPaidExecutionFrom, AllowSubscriptionsFrom) and the message + // is rejected. + let origin = Location::parent(); + + // Build a message with 9 DescendOrigin instructions, exceeding the limit of 8 + let mut instructions: Vec> = Vec::new(); + for i in 0..9 { + instructions.push(DescendOrigin( + [AccountId32 { + network: None, + id: [i as u8; 32], + }] + .into(), + )); + } + instructions.push(WithdrawAsset((Location::parent(), ONE_DOT).into())); + instructions.push(BuyExecution { + fees: (Location::parent(), ONE_DOT).into(), + weight_limit: WeightLimit::Unlimited, + }); + + let message: Xcm = Xcm(instructions); + let outcome = execute_xcm(origin, message); + assert!( + is_barrier_error(&outcome), + "Message exceeding WithComputedOrigin depth limit of 8 should be rejected" + ); + }); +} + +#[test] +fn barrier_with_computed_origin_allows_at_depth_limit() { + ExtBuilder::default().build().execute_with(|| { + // WithComputedOrigin is configured with ConstU32<8>. A message with exactly 8 + // DescendOrigin instructions should still be processed by the inner barriers. + let origin = Location::parent(); + + let mut instructions: Vec> = Vec::new(); + for i in 0..8 { + instructions.push(DescendOrigin( + [AccountId32 { + network: None, + id: [i as u8; 32], + }] + .into(), + )); + } + instructions.push(WithdrawAsset((Location::parent(), ONE_DOT).into())); + instructions.push(BuyExecution { + fees: (Location::parent(), ONE_DOT).into(), + weight_limit: WeightLimit::Unlimited, + }); + + let message: Xcm = Xcm(instructions); + let outcome = execute_xcm(origin, message); + // Should pass the barrier (may fail later for other reasons like no funds) + assert!( + !is_barrier_error(&outcome), + "Message within WithComputedOrigin depth limit of 8 should pass the barrier" + ); + }); +} + +#[test] +fn barrier_allows_paid_execution_from_account_key20() { + ExtBuilder::default().build().execute_with(|| { + let origin = Location::new( + 0, + [AccountKey20 { + network: Some(NetworkId::Polkadot), + key: ALICE, + }], + ); + let message: Xcm = Xcm(vec![ + WithdrawAsset((Location::parent(), ONE_DOT).into()), + BuyExecution { + fees: (Location::parent(), ONE_DOT).into(), + weight_limit: WeightLimit::Unlimited, + }, + DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: BOB, + }], + ), + }, + ]); + + let outcome = execute_xcm(origin, message); + assert!( + !is_barrier_error(&outcome), + "Paid execution from AccountKey20 should pass the barrier" + ); + }); +} + +#[test] +fn barrier_rejects_unpaid_execution_from_sibling() { + ExtBuilder::default().build().execute_with(|| { + let origin = Location::new(1, [Parachain(2000)]); + // No WithdrawAsset / BuyExecution — purely unpaid + let message: Xcm = Xcm(vec![ + UnpaidExecution { + weight_limit: Unlimited, + check_origin: None, + }, + DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: ALICE, + }], + ), + }, + ]); + + let outcome = execute_xcm(origin, message); + assert!( + is_barrier_error(&outcome), + "UnpaidExecution from sibling should be rejected by barrier" + ); + }); +} + +#[test] +fn barrier_rejects_unpaid_transact_from_sibling() { + ExtBuilder::default().build().execute_with(|| { + let origin = Location::new(1, [Parachain(2000)]); + let encoded_call = RuntimeCall::System(frame_system::Call::remark_with_event { + remark: vec![1, 2, 3], + }) + .encode(); + + let message: Xcm = Xcm(vec![ + UnpaidExecution { + weight_limit: Unlimited, + check_origin: None, + }, + Transact { + origin_kind: OriginKind::SovereignAccount, + call: encoded_call.into(), + fallback_max_weight: Some(Weight::from_parts(100_000_000, 10_000)), + }, + ]); + + let outcome = execute_xcm(origin, message); + assert!( + is_barrier_error(&outcome), + "UnpaidExecution + Transact from sibling should be rejected" + ); + }); +} + +#[test] +fn barrier_allows_known_query_response() { + ExtBuilder::default().build().execute_with(|| { + let relay_origin = Location::parent(); + + // Register an expected query with PolkadotXcm + let query_id = pallet_xcm::Pallet::::new_query( + relay_origin.clone(), + 100u32.into(), // timeout + Location::here(), + ); + + let message: Xcm = Xcm(vec![QueryResponse { + query_id, + response: Response::Null, + max_weight: Weight::from_parts(1_000_000, 64 * 1024), + querier: Some(Location::here()), + }]); + + let outcome = execute_xcm(relay_origin, message); + assert!( + !is_barrier_error(&outcome), + "Known query response should pass AllowKnownQueryResponses barrier" + ); + }); +} + +#[test] +fn barrier_rejects_unknown_query_response() { + ExtBuilder::default().build().execute_with(|| { + let origin = Location::parent(); + + // Use a query_id that was never registered + let unknown_query_id = 999_999u64; + + let message: Xcm = Xcm(vec![QueryResponse { + query_id: unknown_query_id, + response: Response::Null, + max_weight: Weight::from_parts(1_000_000, 64 * 1024), + querier: Some(Location::here()), + }]); + + let outcome = execute_xcm(origin, message); + assert!( + is_barrier_error(&outcome), + "Unknown query response should be rejected by barrier" + ); + }); +} diff --git a/runtime/moonbeam/tests/xcm_config/location.rs b/runtime/moonbeam/tests/xcm_config/location.rs new file mode 100644 index 00000000000..34cfc0d6a5e --- /dev/null +++ b/runtime/moonbeam/tests/xcm_config/location.rs @@ -0,0 +1,181 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Tests for LocationToAccountId configuration. +//! +//! LocationToAccountId converts XCM Locations to AccountIds. Moonbeam uses: +//! - ParentIsPreset: Relay chain maps to a preset account +//! - SiblingParachainConvertsVia: Sibling parachains map to sovereign accounts +//! - AccountKey20Aliases: AccountKey20 junctions map directly to AccountId +//! - HashedDescription: Other locations map via a hashed description +//! - ExternalConsensusLocationsConverterFor: Bridged locations map to accounts + +use crate::xcm_common::*; +use moonbeam_runtime::{ + xcm_config::{LocationToAccountId, LocationToH160}, + AccountId, +}; +use polkadot_parachain::primitives::Sibling; +use sp_core::H160; +use sp_runtime::traits::AccountIdConversion; +use xcm::latest::prelude::*; +use xcm_executor::traits::ConvertLocation; + +#[test] +fn location_converts_relay_to_account() { + ExtBuilder::default().build().execute_with(|| { + let relay_location = Location::parent(); + let account = LocationToAccountId::convert_location(&relay_location); + + assert!( + account.is_some(), + "Relay location should convert to account" + ); + + // ParentIsPreset decodes b"Parent" padded with zeros into AccountId (H160). + let relay_account = account.unwrap(); + let expected: [u8; 20] = { + let mut buf = [0u8; 20]; + buf[..6].copy_from_slice(b"Parent"); + buf + }; + assert_eq!( + relay_account, + AccountId::from(expected), + "Relay sovereign should be derived from b\"Parent\" via ParentIsPreset" + ); + }); +} + +#[test] +fn location_converts_sibling_parachain_to_sovereign_account() { + ExtBuilder::default().build().execute_with(|| { + // SiblingParachainConvertsVia derives a sovereign account from the parachain ID + // using Sibling's AccountIdConversion::into_account_truncating. + let sibling_para_id = 2000u32; + let sibling_location = Location::new(1, [Parachain(sibling_para_id)]); + let account = LocationToAccountId::convert_location(&sibling_location); + + let expected: AccountId = Sibling::from(sibling_para_id).into_account_truncating(); + assert_eq!( + account, + Some(expected), + "Sibling parachain should convert to sovereign account derived from para ID" + ); + }); +} + +#[test] +fn location_converts_different_siblings_to_different_accounts() { + ExtBuilder::default().build().execute_with(|| { + let account_a = LocationToAccountId::convert_location(&Location::new(1, [Parachain(2000)])); + let account_b = LocationToAccountId::convert_location(&Location::new(1, [Parachain(3000)])); + + assert!( + account_a.is_some(), + "Sibling 2000 should convert to account" + ); + assert!( + account_b.is_some(), + "Sibling 3000 should convert to account" + ); + assert_ne!( + account_a, account_b, + "Different sibling parachains should have different sovereign accounts" + ); + }); +} + +#[test] +fn location_converts_account_key20_directly() { + ExtBuilder::default().build().execute_with(|| { + let expected_account = ALICE; + let location = Location::new( + 0, + [AccountKey20 { + network: Some(NetworkId::Polkadot), + key: expected_account, + }], + ); + + let account = LocationToAccountId::convert_location(&location); + + assert!(account.is_some(), "AccountKey20 should convert to account"); + assert_eq!( + account.unwrap(), + AccountId::from(expected_account), + "AccountKey20 should map directly to the same account" + ); + }); +} + +#[test] +fn location_rejects_unsupported_multi_junction_patterns() { + ExtBuilder::default().build().execute_with(|| { + // HashedDescription<_, DescribeFamily> only describes + // locations whose tail (after the family prefix) is a single terminal junction. + // A sibling with multiple interior junctions beyond Parachain is not describable + // by DescribeAllTerminal and no other converter handles it, so it must return None. + let complex_location = + Location::new(1, [Parachain(2000), PalletInstance(10), GeneralIndex(42)]); + + assert_eq!( + LocationToAccountId::convert_location(&complex_location), + None, + "Multi-junction sibling location should not convert to an account" + ); + }); +} + +#[test] +fn location_converts_bridged_parachain() { + ExtBuilder::default().build().execute_with(|| { + // A parachain from another consensus (bridged) + let bridged_location = + Location::new(2, [GlobalConsensus(NetworkId::Kusama), Parachain(1000)]); + + let account = LocationToAccountId::convert_location(&bridged_location); + + // ExternalConsensusLocationsConverterFor should handle this + assert!( + account.is_some(), + "Bridged parachain should convert to account" + ); + }); +} + +#[test] +fn location_to_h160_converts_account_key20() { + ExtBuilder::default().build().execute_with(|| { + // LocationToH160 wraps LocationToAccountId and converts to H160, + // used by EVM-related XCM operations. + let location = Location::new( + 0, + [AccountKey20 { + network: Some(NetworkId::Polkadot), + key: ALICE, + }], + ); + + let h160 = LocationToH160::convert_location(&location); + + assert_eq!( + h160, + Some(H160::from(ALICE)), + "LocationToH160 should convert AccountKey20 to the matching H160" + ); + }); +} diff --git a/runtime/moonbeam/tests/xcm_config/main.rs b/runtime/moonbeam/tests/xcm_config/main.rs new file mode 100644 index 00000000000..16f2cad2544 --- /dev/null +++ b/runtime/moonbeam/tests/xcm_config/main.rs @@ -0,0 +1,42 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! XCM Configuration Tests (Level 1) +//! +//! These tests verify the XCM configuration components in isolation using +//! the real Moonbeam runtime configuration. They test: +//! +//! - Barriers: Which XCM messages are allowed to execute +//! - Reserves: Which assets are recognized as reserve assets +//! - Traders: How XCM fees are charged +//! - Transactors: How assets are deposited/withdrawn +//! - Location converters: How locations map to accounts +//! - Weigher: How XCM messages are weighed + +#![cfg(test)] + +#[path = "../common/mod.rs"] +mod common; + +mod xcm_common; + +mod barriers; +mod location; +mod origin; +mod reserves; +mod traders; +mod transactors; +mod weigher; diff --git a/runtime/moonbeam/tests/xcm_config/origin.rs b/runtime/moonbeam/tests/xcm_config/origin.rs new file mode 100644 index 00000000000..f7d25cd874b --- /dev/null +++ b/runtime/moonbeam/tests/xcm_config/origin.rs @@ -0,0 +1,177 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Tests for XcmOriginToTransactDispatchOrigin and SafeCallFilter configuration. +//! +//! XcmOriginToTransactDispatchOrigin converts XCM locations + OriginKind into +//! dispatch origins for Transact. Moonbeam uses: +//! - SovereignSignedViaLocation: SovereignAccount kind → Signed origin +//! - RelayChainAsNative: Native kind from relay → relay origin +//! - SiblingParachainAsNative: Native kind from sibling → sibling origin +//! - XcmPassthrough: Xcm kind → pallet_xcm origin +//! - SignedAccountKey20AsNative: Native kind from AccountKey20 → Signed origin +//! +//! SafeCallFilter determines which calls are allowed via XCM Transact. + +use crate::xcm_common::*; +use moonbeam_runtime::{ + xcm_config::{SafeCallFilter, XcmOriginToTransactDispatchOrigin}, + RuntimeCall, RuntimeOrigin, +}; +use xcm::latest::prelude::*; +use xcm_executor::traits::ConvertOrigin; + +#[test] +fn origin_converts_relay_with_sovereign_kind() { + ExtBuilder::default().build().execute_with(|| { + // SovereignSignedViaLocation converts relay location + SovereignAccount kind + // into a Signed origin using the relay's sovereign account. + let relay = Location::parent(); + + let result = + >::convert_origin( + relay, + OriginKind::SovereignAccount, + ); + + assert!( + result.is_ok(), + "Relay + SovereignAccount should convert to dispatch origin" + ); + }); +} + +#[test] +fn origin_converts_sibling_with_sovereign_kind() { + ExtBuilder::default().build().execute_with(|| { + let sibling = Location::new(1, [Parachain(2000)]); + + let result = + >::convert_origin( + sibling, + OriginKind::SovereignAccount, + ); + + assert!( + result.is_ok(), + "Sibling + SovereignAccount should convert to dispatch origin" + ); + }); +} + +#[test] +fn origin_converts_relay_with_native_kind() { + ExtBuilder::default().build().execute_with(|| { + // RelayChainAsNative converts relay location + Native kind into the + // relay chain origin (used for governance-like calls). + let relay = Location::parent(); + + let result = + >::convert_origin( + relay, + OriginKind::Native, + ); + + assert!( + result.is_ok(), + "Relay + Native should convert via RelayChainAsNative" + ); + }); +} + +#[test] +fn origin_converts_relay_with_xcm_kind() { + ExtBuilder::default().build().execute_with(|| { + // XcmPassthrough converts any location + Xcm kind into a pallet_xcm origin. + let relay = Location::parent(); + + let result = + >::convert_origin( + relay, + OriginKind::Xcm, + ); + + assert!( + result.is_ok(), + "Relay + Xcm should convert via XcmPassthrough" + ); + }); +} + +#[test] +fn origin_converts_account_key20_with_native_kind() { + ExtBuilder::default().build().execute_with(|| { + // SignedAccountKey20AsNative converts AccountKey20 + Native kind into a + // Signed origin with the 20-byte account. + let account_location = Location::new( + 0, + [AccountKey20 { + network: Some(NetworkId::Polkadot), + key: ALICE, + }], + ); + + let result = + >::convert_origin( + account_location, + OriginKind::Native, + ); + + assert!( + result.is_ok(), + "AccountKey20 + Native should convert via SignedAccountKey20AsNative" + ); + }); +} + +#[test] +fn origin_rejects_superuser_kind() { + ExtBuilder::default().build().execute_with(|| { + // No converter handles Superuser kind, so it should be rejected. + let relay = Location::parent(); + + let result = + >::convert_origin( + relay, + OriginKind::Superuser, + ); + + assert!(result.is_err(), "Superuser kind should not be convertible"); + }); +} + +#[test] +fn safe_call_filter_allows_all_calls() { + ExtBuilder::default().build().execute_with(|| { + use frame_support::traits::Contains; + + // Moonbeam's SafeCallFilter currently allows all calls (returns true). + // This is intentional — filtering is handled at the EVM level. + // Verify with calls from two distinct pallets to confirm the filter + // is truly permissive, not a pallet-specific whitelist. + let remark_call = RuntimeCall::System(frame_system::Call::remark { remark: vec![] }); + assert!( + SafeCallFilter::contains(&remark_call), + "SafeCallFilter should allow System::remark" + ); + + let utility_call = RuntimeCall::Utility(pallet_utility::Call::batch { calls: vec![] }); + assert!( + SafeCallFilter::contains(&utility_call), + "SafeCallFilter should allow Utility::batch" + ); + }); +} diff --git a/runtime/moonbeam/tests/xcm_config/reserves.rs b/runtime/moonbeam/tests/xcm_config/reserves.rs new file mode 100644 index 00000000000..71122af23a7 --- /dev/null +++ b/runtime/moonbeam/tests/xcm_config/reserves.rs @@ -0,0 +1,270 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Tests for IsReserve (Reserves) configuration. +//! +//! The Reserves type determines which assets are recognized as reserve assets +//! and which origin is allowed to act as reserve for those assets. +//! +//! Moonbeam's Reserves configuration allows: +//! - IsBridgedConcreteAssetFrom: Bridged assets from Asset Hub +//! - IsBridgedConcreteAssetFrom: Assets from Moonriver +//! - Case: DOT from Asset Hub +//! - MultiNativeAsset>: Self-reserve + +use crate::xcm_common::*; +use frame_support::traits::ContainsPair; +use moonbeam_runtime::xcm_config::{AssetHubLocation, XcmExecutorConfig}; +use xcm::latest::prelude::*; +use xcm_primitives::IsBridgedConcreteAssetFrom; + +/// The actual `IsReserve` type wired into the XCM executor. +type IsReserve = ::IsReserve; + +const ASSET_HUB_PARA_ID: u32 = 1000; + +#[test] +fn reserves_accepts_dot_from_asset_hub() { + ExtBuilder::default().build().execute_with(|| { + // DOT asset coming from Asset Hub should be accepted + let dot_asset = Asset { + id: AssetId(Location::parent()), + fun: Fungible(ONE_DOT), + }; + let asset_hub_origin = Location::new(1, [Parachain(ASSET_HUB_PARA_ID)]); + + // RelayChainNativeAssetFromAssetHub case should match this + type RelayFromAssetHub = + xcm_builder::Case; + + assert!( + RelayFromAssetHub::contains(&dot_asset, &asset_hub_origin), + "DOT from Asset Hub should be accepted as reserve" + ); + assert!( + IsReserve::contains(&dot_asset, &asset_hub_origin), + "IsReserve must accept DOT from Asset Hub (runtime wiring)" + ); + }); +} + +#[test] +fn reserves_accepts_bridged_assets_from_asset_hub() { + ExtBuilder::default().build().execute_with(|| { + // Bridged asset from another consensus (parents: 2) + let bridged_asset = Asset { + id: AssetId(Location::new( + 2, + [GlobalConsensus(NetworkId::Kusama), Parachain(1000)], + )), + fun: Fungible(1_000_000), + }; + let asset_hub_origin = AssetHubLocation::get(); + + // IsBridgedConcreteAssetFrom should match + assert!( + IsBridgedConcreteAssetFrom::::contains( + &bridged_asset, + &asset_hub_origin + ), + "Bridged assets from Asset Hub should be accepted" + ); + assert!( + IsReserve::contains(&bridged_asset, &asset_hub_origin), + "IsReserve must accept bridged assets from Asset Hub (runtime wiring)" + ); + }); +} + +#[test] +fn reserves_rejects_bridged_assets_from_wrong_origin() { + ExtBuilder::default().build().execute_with(|| { + // Bridged asset from another consensus + let bridged_asset = Asset { + id: AssetId(Location::new( + 2, + [GlobalConsensus(NetworkId::Kusama), Parachain(1000)], + )), + fun: Fungible(1_000_000), + }; + // Wrong origin - not Asset Hub + let wrong_origin = Location::new(1, [Parachain(2000)]); + + assert!( + !IsBridgedConcreteAssetFrom::::contains( + &bridged_asset, + &wrong_origin + ), + "Bridged assets from wrong origin should be rejected" + ); + }); +} + +#[test] +fn reserves_rejects_non_bridged_assets_via_bridged_filter() { + ExtBuilder::default().build().execute_with(|| { + // Non-bridged asset (parents: 1, not 2) + let local_asset = Asset { + id: AssetId(Location::new(1, [Parachain(1000)])), + fun: Fungible(1_000_000), + }; + let asset_hub_origin = AssetHubLocation::get(); + + // IsBridgedConcreteAssetFrom requires parents > 1 + assert!( + !IsBridgedConcreteAssetFrom::::contains( + &local_asset, + &asset_hub_origin + ), + "Non-bridged assets should not match bridged asset filter" + ); + }); +} + +#[test] +fn reserves_accepts_self_reserve() { + ExtBuilder::default().build().execute_with(|| { + use frame_support::traits::PalletInfoAccess; + use moonbeam_runtime::xcm_config::SelfLocationAbsolute; + use moonbeam_runtime::Balances; + use xcm_primitives::{AbsoluteAndRelativeReserve, MultiNativeAsset}; + + let self_reserve = Location::new(0, [PalletInstance(Balances::index() as u8)]); + + let native_asset = Asset { + id: AssetId(self_reserve), + fun: Fungible(1_000_000_000_000_000_000), // 1 GLMR + }; + + // MultiNativeAsset accepts an asset when the origin matches the asset's + // reserve. For our own native token the reserve is ourselves + // (Location::here()), so origin = here() must pass. + let self_origin = Location::here(); + + assert!( + MultiNativeAsset::>::contains( + &native_asset, + &self_origin + ), + "Self reserve asset should be accepted when origin is here()" + ); + assert!( + IsReserve::contains(&native_asset, &self_origin), + "IsReserve must accept self-reserve (runtime wiring)" + ); + }); +} + +#[test] +fn reserves_accepts_sibling_native_asset() { + ExtBuilder::default().build().execute_with(|| { + // Native asset from a sibling parachain + let sibling_asset = Asset { + id: AssetId(Location::new(1, [Parachain(2000), PalletInstance(10)])), + fun: Fungible(1_000_000), + }; + let sibling_origin = Location::new(1, [Parachain(2000)]); + + // MultiNativeAsset should accept this - the origin matches the asset's reserve + // This is checked by matching asset's reserve location to the origin + use moonbeam_runtime::xcm_config::SelfLocationAbsolute; + use xcm_primitives::{AbsoluteAndRelativeReserve, MultiNativeAsset}; + + // The reserve of sibling asset is the sibling chain itself + // MultiNativeAsset will check if origin matches reserve + assert!( + MultiNativeAsset::>::contains( + &sibling_asset, + &sibling_origin + ), + "Sibling native asset should be accepted when origin matches reserve" + ); + assert!( + IsReserve::contains(&sibling_asset, &sibling_origin), + "IsReserve must accept sibling native asset (runtime wiring)" + ); + }); +} + +#[test] +fn reserves_rejects_asset_with_mismatched_origin() { + ExtBuilder::default().build().execute_with(|| { + // Asset claims to be from parachain 2000 + let asset = Asset { + id: AssetId(Location::new(1, [Parachain(2000), PalletInstance(10)])), + fun: Fungible(1_000_000), + }; + // But origin is from parachain 3000 + let wrong_origin = Location::new(1, [Parachain(3000)]); + + use moonbeam_runtime::xcm_config::SelfLocationAbsolute; + use xcm_primitives::{AbsoluteAndRelativeReserve, MultiNativeAsset}; + + assert!( + !MultiNativeAsset::>::contains( + &asset, + &wrong_origin + ), + "Asset from mismatched origin should be rejected" + ); + }); +} + +#[test] +fn reserves_accepts_dot_from_relay() { + ExtBuilder::default().build().execute_with(|| { + // DOT (Location::parent()) coming directly from the relay chain + let dot_asset = Asset { + id: AssetId(Location::parent()), + fun: Fungible(ONE_DOT), + }; + let relay_origin = Location::parent(); + + // MultiNativeAsset should accept this: the relay is the reserve for DOT + use moonbeam_runtime::xcm_config::SelfLocationAbsolute; + use xcm_primitives::{AbsoluteAndRelativeReserve, MultiNativeAsset}; + + assert!( + MultiNativeAsset::>::contains( + &dot_asset, + &relay_origin + ), + "DOT from relay should be accepted as reserve" + ); + assert!( + IsReserve::contains(&dot_asset, &relay_origin), + "IsReserve must accept DOT from relay (runtime wiring)" + ); + }); +} + +#[test] +fn teleport_always_rejected() { + ExtBuilder::default().build().execute_with(|| { + type IsTeleporter = ::IsTeleporter; + + let dot = Asset { + id: AssetId(Location::parent()), + fun: Fungible(ONE_DOT), + }; + let relay_origin = Location::parent(); + + assert!( + !IsTeleporter::contains(&dot, &relay_origin), + "IsTeleporter should reject every asset/origin pair" + ); + }); +} diff --git a/runtime/moonbeam/tests/xcm_config/traders.rs b/runtime/moonbeam/tests/xcm_config/traders.rs new file mode 100644 index 00000000000..f6f73600ee3 --- /dev/null +++ b/runtime/moonbeam/tests/xcm_config/traders.rs @@ -0,0 +1,286 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Tests for the XCM Trader (pallet_xcm_weight_trader::Trader) configuration. +//! +//! The trader is responsible for converting weight to fee and accepting payment +//! in different assets. Moonbeam uses a custom trader that: +//! - Accepts the native token (GLMR) at standard rates +//! - Accepts registered foreign assets at configured relative prices + +use crate::xcm_common::*; +use frame_support::traits::PalletInfoAccess; +use moonbeam_runtime::{Balances, Runtime, Treasury}; +use pallet_xcm_weight_trader::{Pallet as XcmWeightTrader, Trader}; +use sp_weights::Weight; +use xcm::latest::prelude::*; +use xcm::VersionedAssetId; +use xcm_executor::traits::WeightTrader; +use xcm_executor::AssetsInHolding; + +fn native_location() -> Location { + Location::new(0, [PalletInstance(Balances::index() as u8)]) +} + +#[test] +fn trader_accepts_native_token() { + ExtBuilder::default().build().execute_with(|| { + let mut trader = Trader::::new(); + let weight_to_buy = Weight::from_parts(1_000_000_000, 64 * 1024); + + // Create payment in native token + let mut payment = AssetsInHolding::new(); + payment.subsume(Asset { + id: AssetId(native_location()), + fun: Fungible(ONE_GLMR), + }); + + let context = XcmContext::with_message_id([0u8; 32]); + let result = trader.buy_weight(weight_to_buy, payment.clone(), &context); + + // Should succeed - native token is always accepted + assert!(result.is_ok(), "Native token should be accepted for fees"); + }); +} + +#[test] +fn trader_computes_native_fee_correctly() { + ExtBuilder::default().build().execute_with(|| { + let weight = Weight::from_parts(1_000_000_000, 64 * 1024); + let native_loc = native_location(); + + // Compute fee for native token using public API + let versioned_asset_id = VersionedAssetId::V5(AssetId(native_loc)); + let fee_result = + XcmWeightTrader::::query_weight_to_asset_fee(weight, versioned_asset_id); + + assert!(fee_result.is_ok(), "Should compute fee for native token"); + let fee = fee_result.unwrap(); + assert!(fee > 0, "Fee should be non-zero"); + }); +} + +#[test] +fn trader_rejects_unsupported_asset() { + ExtBuilder::default().build().execute_with(|| { + let mut trader = Trader::::new(); + let weight_to_buy = Weight::from_parts(1_000_000_000, 64 * 1024); + + // Try to pay with unsupported asset + let unsupported_asset_location = Location::new(1, [Parachain(9999), PalletInstance(99)]); + let mut payment = AssetsInHolding::new(); + payment.subsume(Asset { + id: AssetId(unsupported_asset_location.clone()), + fun: Fungible(1_000_000_000_000), + }); + + let context = XcmContext::with_message_id([0u8; 32]); + let result = trader.buy_weight(weight_to_buy, payment, &context); + + // Should fail - asset not registered + assert!(result.is_err(), "Unsupported asset should be rejected"); + assert_eq!(result.unwrap_err(), XcmError::AssetNotFound); + }); +} + +#[test] +fn trader_accepts_registered_foreign_asset() { + // Register DOT as supported asset + let dot_location = Location::parent(); + + ExtBuilder::default() + .with_xcm_assets(vec![XcmAssetInitialization { + asset_id: 1, + xcm_location: dot_location.clone(), + decimals: 10, + name: "Polkadot", + symbol: "DOT", + balances: vec![], + }]) + .build() + .execute_with(|| { + // First verify the asset is registered and queryable + let versioned_asset_id = VersionedAssetId::V5(AssetId(dot_location.clone())); + let weight = Weight::from_parts(1_000_000_000, 64 * 1024); + let fee_result = + XcmWeightTrader::::query_weight_to_asset_fee(weight, versioned_asset_id); + + // If the query succeeds, the asset is properly registered + assert!( + fee_result.is_ok(), + "Registered foreign asset should be queryable" + ); + + // Now test the trader directly + let mut trader = Trader::::new(); + let weight_to_buy = Weight::from_parts(1_000_000_000, 64 * 1024); + + // Pay with DOT - need sufficient amount to cover the computed fee + let fee = fee_result.unwrap(); + let mut payment = AssetsInHolding::new(); + payment.subsume(Asset { + id: AssetId(dot_location.clone()), + fun: Fungible(fee * 2), // Double to ensure enough + }); + + let context = XcmContext::with_message_id([0u8; 32]); + let result = trader.buy_weight(weight_to_buy, payment, &context); + + // Should succeed - DOT is registered in XcmWeightTrader + assert!( + result.is_ok(), + "Registered foreign asset should be accepted: {:?}", + result + ); + }); +} + +#[test] +fn trader_computes_foreign_asset_fee_with_relative_price() { + let dot_location = Location::parent(); + + ExtBuilder::default() + .with_xcm_assets(vec![XcmAssetInitialization { + asset_id: 1, + xcm_location: dot_location.clone(), + decimals: 10, + name: "Polkadot", + symbol: "DOT", + balances: vec![], + }]) + .build() + .execute_with(|| { + let weight = Weight::from_parts(1_000_000_000, 64 * 1024); + + // Compute fee for DOT using public API + let versioned_asset_id = VersionedAssetId::V5(AssetId(dot_location.clone())); + let fee_result = + XcmWeightTrader::::query_weight_to_asset_fee(weight, versioned_asset_id); + + assert!( + fee_result.is_ok(), + "Should compute fee for registered asset" + ); + let fee = fee_result.unwrap(); + // Fee should be computed based on relative price + assert!(fee > 0, "Fee should be non-zero"); + }); +} + +#[test] +fn trader_cannot_buy_weight_twice() { + ExtBuilder::default().build().execute_with(|| { + let mut trader = Trader::::new(); + let weight_to_buy = Weight::from_parts(1_000_000_000, 64 * 1024); + let context = XcmContext::with_message_id([0u8; 32]); + + // First purchase + let mut payment1 = AssetsInHolding::new(); + payment1.subsume(Asset { + id: AssetId(native_location()), + fun: Fungible(ONE_GLMR), + }); + let first_result = trader.buy_weight(weight_to_buy, payment1, &context); + assert!( + first_result.is_ok(), + "First buy_weight must succeed for the second-purchase test to be meaningful" + ); + + // Second purchase should fail + let mut payment2 = AssetsInHolding::new(); + payment2.subsume(Asset { + id: AssetId(native_location()), + fun: Fungible(ONE_GLMR), + }); + let result = trader.buy_weight(weight_to_buy, payment2, &context); + + assert!(result.is_err(), "Second buy_weight should fail"); + assert_eq!(result.unwrap_err(), XcmError::NotWithdrawable); + }); +} + +#[test] +fn trader_refunds_unused_weight() { + ExtBuilder::default().build().execute_with(|| { + let mut trader = Trader::::new(); + let weight_bought = Weight::from_parts(2_000_000_000, 128 * 1024); + let weight_used = Weight::from_parts(1_000_000_000, 64 * 1024); + let context = XcmContext::with_message_id([0u8; 32]); + + // Buy more weight than needed + let mut payment = AssetsInHolding::new(); + payment.subsume(Asset { + id: AssetId(native_location()), + fun: Fungible(ONE_GLMR * 10), // Plenty of funds + }); + + let buy_result = trader.buy_weight(weight_bought, payment, &context); + assert!(buy_result.is_ok()); + + // Refund unused weight + let unused_weight = weight_bought.saturating_sub(weight_used); + let refund = trader.refund_weight(unused_weight, &context); + + // Must get a refund + let refunded_asset = refund.expect("refund_weight must return Some for unused weight"); + assert_eq!( + refunded_asset.id, + AssetId(native_location()), + "Refunded asset must be the native token" + ); + match refunded_asset.fun { + Fungible(amount) => { + assert!(amount > 0, "Refunded amount must be non-zero"); + } + _ => panic!("Expected fungible refund"), + } + }); +} + +#[test] +fn trader_handles_insufficient_payment() { + ExtBuilder::default().build().execute_with(|| { + let mut trader = Trader::::new(); + let weight_to_buy = Weight::from_parts(1_000_000_000_000, 64 * 1024); // Very large weight + let context = XcmContext::with_message_id([0u8; 32]); + + // Try to pay with very small amount + let mut payment = AssetsInHolding::new(); + payment.subsume(Asset { + id: AssetId(native_location()), + fun: Fungible(1), // Tiny amount + }); + + let result = trader.buy_weight(weight_to_buy, payment, &context); + + // Should fail - insufficient payment + assert!(result.is_err(), "Insufficient payment should be rejected"); + assert_eq!(result.unwrap_err(), XcmError::TooExpensive); + }); +} + +#[test] +fn xcm_fees_go_to_treasury() { + ExtBuilder::default().build().execute_with(|| { + use moonbeam_runtime::xcm_config::XcmFeesAccount; + + assert_eq!( + XcmFeesAccount::get(), + Treasury::account_id(), + "XCM fee destination should be the treasury account" + ); + }); +} diff --git a/runtime/moonbeam/tests/xcm_config/transactors.rs b/runtime/moonbeam/tests/xcm_config/transactors.rs new file mode 100644 index 00000000000..791a5ce005f --- /dev/null +++ b/runtime/moonbeam/tests/xcm_config/transactors.rs @@ -0,0 +1,367 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Tests for AssetTransactors configuration. +//! +//! AssetTransactors handle the deposit and withdrawal of assets via XCM. +//! Moonbeam uses a tuple of transactors: +//! - LocalAssetTransactor: Handles native token (GLMR) using pallet_balances +//! - EvmForeignAssets: Handles registered foreign assets +//! - Erc20XcmBridge: Handles ERC20 tokens via the bridge + +use crate::xcm_common::*; +use frame_support::traits::{Currency, PalletInfoAccess}; +use moonbeam_runtime::{AccountId, Balances}; +use sp_core::U256; +use xcm::latest::prelude::*; +use xcm_executor::traits::TransactAsset; + +fn alice_account() -> AccountId { + AccountId::from(ALICE) +} + +fn bob_account() -> AccountId { + AccountId::from(BOB) +} + +fn native_asset_location() -> Location { + Location::new(0, [PalletInstance(Balances::index() as u8)]) +} + +#[test] +fn local_transactor_deposits_native_token() { + ExtBuilder::default() + .with_balances(vec![(alice_account(), ONE_GLMR * 100)]) + .build() + .execute_with(|| { + use moonbeam_runtime::xcm_config::AssetTransactors; + + let initial_balance = Balances::free_balance(bob_account()); + + let asset = Asset { + id: AssetId(native_asset_location()), + fun: Fungible(ONE_GLMR), + }; + let destination = Location::new( + 0, + [AccountKey20 { + network: None, + key: BOB, + }], + ); + + // Deposit native asset to Bob + let result = + ::deposit_asset(&asset, &destination, None); + + assert!(result.is_ok(), "Deposit should succeed"); + let final_balance = Balances::free_balance(bob_account()); + assert_eq!( + final_balance, + initial_balance + ONE_GLMR, + "Balance should increase by deposited amount" + ); + }); +} + +#[test] +fn local_transactor_withdraws_native_token() { + ExtBuilder::default() + .with_balances(vec![(alice_account(), ONE_GLMR * 100)]) + .build() + .execute_with(|| { + use moonbeam_runtime::xcm_config::AssetTransactors; + + let initial_balance = Balances::free_balance(alice_account()); + + let asset = Asset { + id: AssetId(native_asset_location()), + fun: Fungible(ONE_GLMR), + }; + let source = Location::new( + 0, + [AccountKey20 { + network: None, + key: ALICE, + }], + ); + + // Withdraw native asset from Alice + let result = ::withdraw_asset(&asset, &source, None); + + assert!(result.is_ok(), "Withdraw should succeed"); + let final_balance = Balances::free_balance(alice_account()); + assert_eq!( + final_balance, + initial_balance - ONE_GLMR, + "Balance should decrease by withdrawn amount" + ); + }); +} + +#[test] +fn local_transactor_fails_withdraw_insufficient_balance() { + ExtBuilder::default() + .with_balances(vec![(alice_account(), ONE_GLMR)]) // Only 1 GLMR + .build() + .execute_with(|| { + use moonbeam_runtime::xcm_config::AssetTransactors; + + let asset = Asset { + id: AssetId(native_asset_location()), + fun: Fungible(ONE_GLMR * 100), // Try to withdraw 100 GLMR + }; + let source = Location::new( + 0, + [AccountKey20 { + network: None, + key: ALICE, + }], + ); + + let result = ::withdraw_asset(&asset, &source, None); + + assert!( + result.is_err(), + "Withdraw should fail with insufficient balance" + ); + }); +} + +#[test] +fn foreign_asset_transactor_deposits_registered_asset() { + let dot_location = Location::parent(); + + ExtBuilder::default() + .with_xcm_assets(vec![XcmAssetInitialization { + asset_id: 1, + xcm_location: dot_location.clone(), + decimals: 10, + name: "Polkadot", + symbol: "DOT", + balances: vec![], + }]) + .build() + .execute_with(|| { + use moonbeam_runtime::xcm_config::AssetTransactors; + + let balance_before = moonbeam_runtime::EvmForeignAssets::balance(1, bob_account()) + .unwrap_or(U256::zero()); + + let asset = Asset { + id: AssetId(dot_location.clone()), + fun: Fungible(ONE_DOT), + }; + let destination = Location::new( + 0, + [AccountKey20 { + network: None, + key: BOB, + }], + ); + + // Deposit DOT to Bob + let result = + ::deposit_asset(&asset, &destination, None); + + assert!( + result.is_ok(), + "Deposit of registered foreign asset should succeed" + ); + + let balance_after = moonbeam_runtime::EvmForeignAssets::balance(1, bob_account()) + .expect("balance query should succeed after deposit"); + assert_eq!( + balance_after - balance_before, + U256::from(ONE_DOT), + "Bob's foreign asset balance should increase by the deposited amount" + ); + }); +} + +#[test] +fn transactor_fails_for_unregistered_asset() { + ExtBuilder::default().build().execute_with(|| { + use moonbeam_runtime::xcm_config::AssetTransactors; + + // Unregistered asset location + let unknown_asset = Asset { + id: AssetId(Location::new(1, [Parachain(9999), PalletInstance(99)])), + fun: Fungible(1_000_000), + }; + let destination = Location::new( + 0, + [AccountKey20 { + network: None, + key: BOB, + }], + ); + + let result = + ::deposit_asset(&unknown_asset, &destination, None); + + // Should fail - asset not registered + assert!(result.is_err(), "Deposit of unregistered asset should fail"); + }); +} + +#[test] +fn transactor_withdraws_registered_foreign_asset() { + let dot_location = Location::parent(); + + ExtBuilder::default() + .with_xcm_assets(vec![XcmAssetInitialization { + asset_id: 1, + xcm_location: dot_location.clone(), + decimals: 10, + name: "Polkadot", + symbol: "DOT", + balances: vec![(bob_account(), ONE_DOT * 10)], + }]) + .build() + .execute_with(|| { + use moonbeam_runtime::xcm_config::AssetTransactors; + + let balance_before = moonbeam_runtime::EvmForeignAssets::balance(1, bob_account()) + .expect("balance query should succeed"); + + let asset = Asset { + id: AssetId(dot_location.clone()), + fun: Fungible(ONE_DOT), + }; + let source = Location::new( + 0, + [AccountKey20 { + network: None, + key: BOB, + }], + ); + + // Withdraw DOT from Bob + let result = ::withdraw_asset(&asset, &source, None); + + assert!( + result.is_ok(), + "Withdraw of registered foreign asset should succeed: {:?}", + result + ); + + let balance_after = moonbeam_runtime::EvmForeignAssets::balance(1, bob_account()) + .expect("balance query should succeed after withdraw"); + assert_eq!( + balance_before - balance_after, + U256::from(ONE_DOT), + "Bob's foreign asset balance should decrease by the withdrawn amount" + ); + }); +} + +#[test] +fn transactor_handles_erc20_bridge_asset() { + ExtBuilder::default() + .with_balances(vec![(alice_account(), ONE_GLMR * 100)]) + .build() + .execute_with(|| { + use moonbeam_runtime::xcm_config::AssetTransactors; + use moonbeam_runtime::Erc20XcmBridge; + + // ERC20 bridge assets are identified by the bridge pallet location + + // AccountKey20(contract_address). + let bridge_pallet_location = { + use frame_support::traits::PalletInfoAccess; + Location::new( + 0, + [PalletInstance( + ::index() as u8, + )], + ) + }; + + // Deposit of an ERC20 bridge asset with a non-existent contract + // should fail gracefully (no panic). + let fake_contract: [u8; 20] = [0xAA; 20]; + let erc20_asset_location = Location::new( + 0, + [ + PalletInstance(bridge_pallet_location.first_interior().map_or(0, |j| { + if let Junction::PalletInstance(idx) = j { + *idx + } else { + 0 + } + })), + AccountKey20 { + network: None, + key: fake_contract, + }, + ], + ); + + let asset = Asset { + id: AssetId(erc20_asset_location), + fun: Fungible(1_000), + }; + let destination = Location::new( + 0, + [AccountKey20 { + network: None, + key: BOB, + }], + ); + + // Attempting deposit of an ERC20 bridge asset with no deployed + // contract should return an error (asset not handled or EVM + // revert), not panic. + let result = + ::deposit_asset(&asset, &destination, None); + + // Either Ok (if the bridge gracefully handles missing contracts) + // or Err — the important thing is no panic. + let _ = result; + }); +} + +#[test] +fn transactor_handles_relay_sovereign_account() { + ExtBuilder::default() + .with_balances(vec![(alice_account(), ONE_GLMR * 100)]) + .build() + .execute_with(|| { + use moonbeam_runtime::xcm_config::{AssetTransactors, LocationToAccountId}; + use xcm_executor::traits::ConvertLocation; + + // The relay chain's sovereign account + let relay_location = Location::parent(); + let sovereign_account = LocationToAccountId::convert_location(&relay_location).unwrap(); + + // Give the sovereign account some funds + let _ = Balances::deposit_creating(&sovereign_account, ONE_GLMR * 10); + + let asset = Asset { + id: AssetId(native_asset_location()), + fun: Fungible(ONE_GLMR), + }; + + // Withdraw from relay sovereign account + let result = + ::withdraw_asset(&asset, &relay_location, None); + + assert!( + result.is_ok(), + "Should withdraw from relay sovereign account" + ); + }); +} diff --git a/runtime/moonbeam/tests/xcm_config/weigher.rs b/runtime/moonbeam/tests/xcm_config/weigher.rs new file mode 100644 index 00000000000..a0ff3d39057 --- /dev/null +++ b/runtime/moonbeam/tests/xcm_config/weigher.rs @@ -0,0 +1,154 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Tests for XcmWeigher configuration. +//! +//! The weigher calculates the weight of XCM messages based on the instructions +//! they contain. Moonbeam uses WeightInfoBounds with custom XcmWeight implementation. + +use crate::xcm_common::*; +use moonbeam_runtime::{xcm_config::XcmWeigher, RuntimeCall}; +use parity_scale_codec::Encode; +use sp_runtime::traits::Zero; +use sp_weights::Weight; +use xcm::latest::prelude::*; +use xcm_executor::traits::WeightBounds; + +#[test] +fn weigher_calculates_weight_for_simple_message() { + ExtBuilder::default().build().execute_with(|| { + let message: Xcm = Xcm(vec![ClearOrigin]); + + let weight = XcmWeigher::weight(&mut message.clone(), Weight::MAX); + + assert!(weight.is_ok(), "Should calculate weight for simple message"); + let w = weight.unwrap(); + assert!(!w.is_zero(), "Weight should be non-zero"); + }); +} + +#[test] +fn weigher_calculates_weight_for_transfer_message() { + ExtBuilder::default().build().execute_with(|| { + let message: Xcm = Xcm(vec![ + WithdrawAsset((Location::parent(), 1_000_000_000_000u128).into()), + BuyExecution { + fees: (Location::parent(), 1_000_000_000_000u128).into(), + weight_limit: WeightLimit::Unlimited, + }, + DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: ALICE, + }], + ), + }, + ]); + + let weight = XcmWeigher::weight(&mut message.clone(), Weight::MAX); + + assert!( + weight.is_ok(), + "Should calculate weight for transfer message" + ); + let w = weight.unwrap(); + assert!(w.ref_time() > 0, "Weight ref_time should be positive"); + }); +} + +#[test] +fn weigher_weight_increases_with_more_instructions() { + ExtBuilder::default().build().execute_with(|| { + let simple_message: Xcm = Xcm(vec![ClearOrigin]); + + let complex_message: Xcm = Xcm(vec![ + ClearOrigin, + ClearOrigin, + ClearOrigin, + ClearOrigin, + ClearOrigin, + ]); + + let simple_weight = XcmWeigher::weight(&mut simple_message.clone(), Weight::MAX).unwrap(); + let complex_weight = XcmWeigher::weight(&mut complex_message.clone(), Weight::MAX).unwrap(); + + assert!( + complex_weight.ref_time() > simple_weight.ref_time(), + "More instructions should result in higher weight" + ); + }); +} + +#[test] +fn weigher_respects_max_instructions() { + ExtBuilder::default().build().execute_with(|| { + // MaxInstructions is 100 in xcm_config + // Create a message with more than 100 instructions + let instructions: Vec> = (0..150).map(|_| ClearOrigin).collect(); + let message: Xcm = Xcm(instructions); + + let weight = XcmWeigher::weight(&mut message.clone(), Weight::MAX); + + // Should fail because it exceeds MaxInstructions + assert!( + weight.is_err(), + "Message exceeding MaxInstructions should fail weighing" + ); + }); +} + +#[test] +fn weigher_handles_transact_instruction() { + ExtBuilder::default().build().execute_with(|| { + // Transact instruction has variable weight based on the encoded call + let encoded_call = RuntimeCall::System(frame_system::Call::remark { + remark: vec![1, 2, 3], + }) + .encode(); + let message: Xcm = Xcm(vec![Transact { + origin_kind: OriginKind::Xcm, + call: encoded_call.into(), + fallback_max_weight: Some(Weight::from_parts(100_000_000, 10_000)), + }]); + + let weight = XcmWeigher::weight(&mut message.clone(), Weight::MAX); + + assert!( + weight.is_ok(), + "Should calculate weight for Transact instruction" + ); + }); +} + +#[test] +fn message_queue_heap_size_sufficient_for_xcm() { + ExtBuilder::default().build().execute_with(|| { + // MessageQueueHeapSize is used as MaxPageSize and HeapSize for pallet_message_queue. + // It must be large enough to hold any valid XCM message, including those + // received via HRMP. A minimum of 100KB ensures compatibility. + use moonbeam_runtime::xcm_config::MessageQueueHeapSize; + + let heap_size = MessageQueueHeapSize::get(); + + assert!( + heap_size >= 100 * 1024, + "MessageQueueHeapSize ({heap_size}) should be at least 100KB for HRMP compatibility" + ); + }); +} diff --git a/runtime/moonbeam/tests/xcm_config/xcm_common.rs b/runtime/moonbeam/tests/xcm_config/xcm_common.rs new file mode 100644 index 00000000000..da730777f19 --- /dev/null +++ b/runtime/moonbeam/tests/xcm_config/xcm_common.rs @@ -0,0 +1,152 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Common test utilities for XCM configuration tests. + +#![allow(dead_code)] + +pub use crate::common::*; + +use moonbeam_runtime::{currency::GLMR, xcm_config::XcmExecutorConfig, RuntimeCall}; +use parity_scale_codec::Encode; +use sp_weights::Weight; +use xcm::latest::prelude::*; +use xcm_executor::XcmExecutor; + +pub const ONE_DOT: u128 = 10_000_000_000; // DOT has 10 decimals +pub const ONE_GLMR: u128 = GLMR; + +/// Execute an XCM message and return the outcome. +/// +/// This uses the real Moonbeam XcmExecutorConfig to test XCM behavior. +pub fn execute_xcm(origin: Location, message: Xcm) -> Outcome { + let hash = message.using_encoded(sp_io::hashing::blake2_256); + XcmExecutor::::prepare_and_execute( + origin, + message, + &mut hash.clone(), + Weight::MAX, + Weight::zero(), + ) +} + +/// Execute an XCM message with a weight limit and return the outcome. +pub fn execute_xcm_with_weight( + origin: Location, + message: Xcm, + weight_limit: Weight, +) -> Outcome { + let hash = message.using_encoded(sp_io::hashing::blake2_256); + XcmExecutor::::prepare_and_execute( + origin, + message, + &mut hash.clone(), + weight_limit, + Weight::zero(), + ) +} + +/// Execute an XCM message with pre-credited weight. +/// +/// `TakeWeightCredit` barrier passes when the message weight is within the +/// credited amount; use this to test that barrier path. +pub fn execute_xcm_with_credit( + origin: Location, + message: Xcm, + weight_credit: Weight, +) -> Outcome { + let hash = message.using_encoded(sp_io::hashing::blake2_256); + XcmExecutor::::prepare_and_execute( + origin, + message, + &mut hash.clone(), + Weight::MAX, + weight_credit, + ) +} + +/// Helper to check if an outcome is a barrier error. +/// +/// Barrier rejections can surface as either `Outcome::Error` or +/// `Outcome::Incomplete` (when the executor begins processing before the +/// barrier rejects at instruction index 0), so both variants are matched. +pub fn is_barrier_error(outcome: &Outcome) -> bool { + matches!( + outcome, + Outcome::Error(ref e) if e.error == XcmError::Barrier + ) || matches!( + outcome, + Outcome::Incomplete { ref error, .. } if error.error == XcmError::Barrier + ) +} + +/// Helper to check if execution completed successfully +pub fn is_complete(outcome: &Outcome) -> bool { + matches!(outcome, Outcome::Complete { .. }) +} + +/// Helper to check if execution is incomplete (partially executed) +pub fn is_incomplete(outcome: &Outcome) -> bool { + matches!(outcome, Outcome::Incomplete { .. }) +} + +/// Create a simple asset from location and amount +pub fn asset(location: Location, amount: u128) -> Asset { + Asset { + id: AssetId(location), + fun: Fungible(amount), + } +} + +/// Relay chain native asset (DOT) +pub fn relay_asset(amount: u128) -> Asset { + asset(Location::parent(), amount) +} + +/// Asset from a sibling parachain's native token +pub fn sibling_asset(para_id: u32, pallet_index: u8, amount: u128) -> Asset { + asset( + Location::new(1, [Parachain(para_id), PalletInstance(pallet_index)]), + amount, + ) +} + +/// Build a message with paid execution +pub fn paid_message(fees: Asset, instructions: Vec>) -> Xcm { + let mut all_instructions = vec![ + WithdrawAsset(fees.clone().into()), + BuyExecution { + fees, + weight_limit: WeightLimit::Unlimited, + }, + ]; + all_instructions.extend(instructions); + Xcm(all_instructions) +} + +/// Build a deposit asset instruction to an account +pub fn deposit_to_account(account: [u8; 20]) -> Instruction { + DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: account, + }], + ), + } +} diff --git a/runtime/moonbeam/tests/xcm_emulator/asset_hub.rs b/runtime/moonbeam/tests/xcm_emulator/asset_hub.rs new file mode 100644 index 00000000000..40b119109fc --- /dev/null +++ b/runtime/moonbeam/tests/xcm_emulator/asset_hub.rs @@ -0,0 +1,306 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Asset Hub ↔ Moonbeam transfer tests using xcm-emulator. +//! +//! These tests exercise cross-chain transfers between the real +//! `asset-hub-westend-runtime` (para 1000) and the real `moonbeam-runtime` +//! (para 2004), with Westend as relay. + +use crate::network::*; +use frame_support::{assert_ok, traits::fungible::Inspect}; +use sp_core::U256; +use xcm::latest::prelude::*; +use xcm_emulator::TestExt; + +// =========================================================================== +// Setup helpers +// =========================================================================== + +/// Register DOT on Moonbeam, open HRMP between Asset Hub and Moonbeam. +fn setup_asset_hub_and_moonbeam() { + init_network(); + + moonbeam_execute_with(|| register_dot_asset(DOT_ASSET_ID)); + + WestendRelay::::execute_with(|| { + open_hrmp_channels(ASSET_HUB_PARA_ID, MOONBEAM_PARA_ID); + }); +} + +/// Fund ALITH on Moonbeam with DOT from the relay. +fn fund_moonbeam_alith_with_dot(amount: u128) { + WestendRelay::::execute_with(|| { + assert_ok!( + westend_runtime::XcmPallet::transfer_assets_using_type_and_then( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE.clone()), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(MOONBEAM_PARA_ID)] + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(amount), + }]))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedAssetId::from(AssetId(Location::here()))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: ALITH, + }], + ), + }]))), + WeightLimit::Unlimited, + ) + ); + }); +} + +// =========================================================================== +// Tests +// =========================================================================== + +/// Transfer DOT from the relay to Asset Hub, confirming the real +/// asset-hub-westend-runtime processes DMP correctly. +#[test] +fn transfer_dot_from_relay_to_asset_hub() { + init_network(); + + let recipient = sp_runtime::AccountId32::new([2u8; 32]); + + let balance_before = asset_hub_execute_with(|| { + >::balance(&recipient) + }); + + // Send DOT from relay to Asset Hub (DOT is the native token on both). + WestendRelay::::execute_with(|| { + assert_ok!(westend_runtime::XcmPallet::limited_teleport_assets( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE.clone()), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(ASSET_HUB_PARA_ID)], + ))), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [AccountId32 { + network: None, + id: recipient.clone().into(), + }], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(ONE_DOT * 10), + }]))), + 0, + WeightLimit::Unlimited, + )); + }); + + let balance_after = asset_hub_execute_with(|| { + >::balance(&recipient) + }); + assert!( + balance_after > balance_before, + "Asset Hub account should have received DOT: before={balance_before}, after={balance_after}" + ); +} + +/// Transfer DOT from the relay to both Asset Hub (teleport) and Moonbeam +/// (reserve), confirming both chains can hold DOT originated from the +/// same relay in the same network. +#[test] +fn relay_funds_both_asset_hub_and_moonbeam() { + setup_asset_hub_and_moonbeam(); + + let ah_recipient = sp_runtime::AccountId32::new([2u8; 32]); + + // Fund Asset Hub via teleport. + WestendRelay::::execute_with(|| { + assert_ok!(westend_runtime::XcmPallet::limited_teleport_assets( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE.clone()), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(ASSET_HUB_PARA_ID)], + ))), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [AccountId32 { + network: None, + id: ah_recipient.clone().into(), + }], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(ONE_DOT * 10), + }]))), + 0, + WeightLimit::Unlimited, + )); + }); + + // Fund Moonbeam via reserve. + fund_moonbeam_alith_with_dot(ONE_DOT * 10); + + // Both chains should have DOT. + let ah_balance = asset_hub_execute_with(|| { + >::balance(&ah_recipient) + }); + assert!( + ah_balance > 0, + "Asset Hub should have DOT (got {ah_balance})" + ); + + let moonbeam_balance = moonbeam_execute_with(|| { + moonbeam_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonbeam_runtime::AccountId::from(ALITH), + ) + .unwrap_or_default() + }); + assert!( + moonbeam_balance > U256::zero(), + "Moonbeam should have DOT (got {moonbeam_balance})" + ); +} + +/// Transfer a trust-backed asset (e.g. USDT) from Asset Hub to Moonbeam. +/// Asset Hub is the reserve for trust-backed assets, so this is a +/// reserve-backed transfer. +#[test] +fn transfer_trust_backed_asset_from_asset_hub_to_moonbeam() { + setup_asset_hub_and_moonbeam(); + + // Create and mint a trust-backed asset (id=1984, "USDT") on Asset Hub. + let asset_id: u32 = 1984; + let asset_owner = sp_runtime::AccountId32::new([1u8; 32]); + let mint_amount: u128 = 1_000_000_000; // 1000 USDT (6 decimals) + + asset_hub_execute_with(|| { + assert_ok!(asset_hub_westend_runtime::Assets::force_create( + asset_hub_westend_runtime::RuntimeOrigin::root(), + asset_id.into(), + asset_owner.clone().into(), + true, + 1_000, // min_balance + )); + assert_ok!(asset_hub_westend_runtime::Assets::mint( + asset_hub_westend_runtime::RuntimeOrigin::signed(asset_owner.clone()), + asset_id.into(), + asset_owner.clone().into(), + mint_amount, + )); + }); + + // Register this asset on Moonbeam as a foreign asset. + // From Moonbeam's perspective: ../Parachain(1000)/PalletInstance(50)/GeneralIndex(1984) + const USDT_FOREIGN_ID: u128 = 10; + moonbeam_execute_with(|| { + let usdt_location = xcm::latest::Location::new( + 1, + [ + Parachain(ASSET_HUB_PARA_ID), + PalletInstance(50u8), // pallet_assets instance 1 + GeneralIndex(asset_id as u128), + ], + ); + + frame_support::assert_ok!(moonbeam_runtime::EvmForeignAssets::create_foreign_asset( + moonbeam_runtime::RuntimeOrigin::root(), + USDT_FOREIGN_ID, + usdt_location.clone(), + 6, // USDT decimals + b"USDT".to_vec().try_into().unwrap(), + b"Tether USD".to_vec().try_into().unwrap(), + )); + + frame_support::assert_ok!(moonbeam_runtime::XcmWeightTrader::add_asset( + moonbeam_runtime::RuntimeOrigin::root(), + usdt_location, + 10_000_000_000_000_000_000_000_000_000u128, + )); + }); + + // Also need DOT on Asset Hub sender for fees. Fund via relay teleport. + WestendRelay::::execute_with(|| { + assert_ok!(westend_runtime::XcmPallet::limited_teleport_assets( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE.clone()), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(ASSET_HUB_PARA_ID)], + ))), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [AccountId32 { + network: None, + id: asset_owner.clone().into(), + }], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(ONE_DOT * 100), + }]))), + 0, + WeightLimit::Unlimited, + )); + }); + + // Transfer USDT from Asset Hub to Moonbeam. + // Asset Hub is the reserve for this trust-backed asset. + let transfer_amount: u128 = 500_000_000; // 500 USDT + + asset_hub_execute_with(|| { + let usdt_on_ah = Location::new(0, [PalletInstance(50u8), GeneralIndex(asset_id as u128)]); + + assert_ok!(asset_hub_westend_runtime::PolkadotXcm::transfer_assets( + asset_hub_westend_runtime::RuntimeOrigin::signed(asset_owner.clone()), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(MOONBEAM_PARA_ID)], + ))), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [AccountKey20 { + network: None, + key: ALITH, + }], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(usdt_on_ah), + fun: Fungible(transfer_amount), + }]))), + 0, + WeightLimit::Unlimited, + )); + }); + + // ALITH on Moonbeam should have received USDT as a foreign asset. + let alith_usdt = moonbeam_execute_with(|| { + moonbeam_runtime::EvmForeignAssets::balance( + USDT_FOREIGN_ID, + moonbeam_runtime::AccountId::from(ALITH), + ) + .unwrap_or_default() + }); + assert!( + alith_usdt > U256::zero(), + "ALITH should have received USDT on Moonbeam (got {alith_usdt})" + ); +} diff --git a/runtime/moonbeam/tests/xcm_emulator/main.rs b/runtime/moonbeam/tests/xcm_emulator/main.rs new file mode 100644 index 00000000000..05659adef85 --- /dev/null +++ b/runtime/moonbeam/tests/xcm_emulator/main.rs @@ -0,0 +1,35 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! XCM Emulator Integration Tests (Level 2A) +//! +//! Uses the real `moonbeam_runtime` connected to `westend_runtime` as relay +//! and a sibling `moonbeam_runtime` instance. Tests exercise: +//! +//! - Transfers: relay→para, para→relay, para→para (DMP/UMP/XCMP) +//! - Fee collection: treasury receives execution fees, insufficient fees fail +//! - Transact: sovereign transact to relay +//! - HRMP: open, accept, close channels via `pallet_xcm_transactor` +//! - Account sufficiency: fresh accounts receive foreign assets + +#![cfg(test)] + +mod asset_hub; +mod network; +mod relay; +mod transact; +mod transfers; +mod versioning; diff --git a/runtime/moonbeam/tests/xcm_emulator/network.rs b/runtime/moonbeam/tests/xcm_emulator/network.rs new file mode 100644 index 00000000000..2dc60f7b236 --- /dev/null +++ b/runtime/moonbeam/tests/xcm_emulator/network.rs @@ -0,0 +1,461 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Network declaration for xcm-emulator. +//! +//! Wires a Westend relay chain, the real Moonbeam runtime (para 2004), +//! and a sibling Moonbeam instance (para 2005) into a single test network. + +use crate::relay; + +use frame_support::traits::OnInitialize; +use nimbus_primitives::{NimbusId, NIMBUS_ENGINE_ID}; +use parity_scale_codec::Encode; +use sp_runtime::{Digest, DigestItem}; +use xcm_emulator::decl_test_networks; +use xcm_emulator::decl_test_parachains; +use xcm_emulator::decl_test_relay_chains; +use xcm_emulator::BlockProducer; +use xcm_emulator::TestExt; + +/// `BlockProducer` for Moonbeam's Nimbus-based consensus. +/// +/// xcm-emulator defaults to an Aura-backed producer; Moonbeam does not use +/// pallet-aura, so we plug in a Nimbus pre-runtime digest with a 6s block +/// interval (matching `moonbeam_runtime::MILLISECS_PER_BLOCK`). +pub struct MoonbeamBlockProducer; + +impl BlockProducer for MoonbeamBlockProducer { + fn slot_duration() -> u64 { + moonbeam_runtime::MILLISECS_PER_BLOCK + } + + fn pre_runtime_digest(_relay_block_number: u32) -> Digest { + let author = NimbusId::from(sp_core::sr25519::Public::from_raw([1u8; 32])); + Digest { + logs: vec![DigestItem::PreRuntime(NIMBUS_ENGINE_ID, author.encode())], + } + } +} + +pub const ASSET_HUB_PARA_ID: u32 = 1000; +pub const MOONBEAM_PARA_ID: u32 = 2004; +pub const SIBLING_PARA_ID: u32 = 2005; + +// ---- Well-known test accounts (20-byte) ------------------------------------ +pub const ALITH: [u8; 20] = [1u8; 20]; +pub const BALTATHAR: [u8; 20] = [2u8; 20]; +// ---- Well-known relay accounts (32-byte) ----------------------------------- +pub const RELAY_ALICE: sp_runtime::AccountId32 = sp_runtime::AccountId32::new([1u8; 32]); + +// ---- Asset ID constants ---------------------------------------------------- +pub const DOT_ASSET_ID: u128 = 1; +pub const GLMR_ASSET_ID: u128 = 2; + +// ---- DOT constants --------------------------------------------------------- +pub const ONE_DOT: u128 = 10_000_000_000; // 10 decimals + +// --------------------------------------------------------------------------- +// Relay chain declaration (Westend runtime) +// --------------------------------------------------------------------------- +decl_test_relay_chains! { + #[api_version(15)] + pub struct WestendRelay { + genesis = relay::relay_genesis(), + on_init = (), + runtime = westend_runtime, + core = { + SovereignAccountOf: westend_runtime::xcm_config::LocationConverter, + }, + pallets = { + XcmPallet: westend_runtime::XcmPallet, + Balances: westend_runtime::Balances, + Hrmp: westend_runtime::Hrmp, + Utility: westend_runtime::Utility, + } + } +} + +// --------------------------------------------------------------------------- +// Moonbeam parachain declaration (para 2004) +// --------------------------------------------------------------------------- +decl_test_parachains! { + pub struct MoonbeamPara { + genesis = moonbeam_genesis(MOONBEAM_PARA_ID), + on_init = { + crate::network::satisfy_moonbeam_inherents(); + }, + runtime = moonbeam_runtime, + core = { + XcmpMessageHandler: moonbeam_runtime::XcmpQueue, + LocationToAccountId: moonbeam_runtime::xcm_config::LocationToAccountId, + ParachainInfo: moonbeam_runtime::ParachainInfo, + MessageOrigin: cumulus_primitives_core::AggregateMessageOrigin, + BlockProducer: crate::network::MoonbeamBlockProducer, + }, + pallets = { + PolkadotXcm: moonbeam_runtime::PolkadotXcm, + Balances: moonbeam_runtime::Balances, + EvmForeignAssets: moonbeam_runtime::EvmForeignAssets, + XcmWeightTrader: moonbeam_runtime::XcmWeightTrader, + XcmTransactor: moonbeam_runtime::XcmTransactor, + Treasury: moonbeam_runtime::Treasury, + EthereumXcm: moonbeam_runtime::EthereumXcm, + Proxy: moonbeam_runtime::Proxy, + EVM: moonbeam_runtime::EVM, + } + } +} + +// --------------------------------------------------------------------------- +// Sibling parachain declaration (para 2005) — another Moonbeam instance +// --------------------------------------------------------------------------- +decl_test_parachains! { + pub struct SiblingPara { + genesis = moonbeam_genesis(SIBLING_PARA_ID), + on_init = { + crate::network::satisfy_moonbeam_inherents(); + }, + runtime = moonbeam_runtime, + core = { + XcmpMessageHandler: moonbeam_runtime::XcmpQueue, + LocationToAccountId: moonbeam_runtime::xcm_config::LocationToAccountId, + ParachainInfo: moonbeam_runtime::ParachainInfo, + MessageOrigin: cumulus_primitives_core::AggregateMessageOrigin, + BlockProducer: crate::network::MoonbeamBlockProducer, + }, + pallets = { + PolkadotXcm: moonbeam_runtime::PolkadotXcm, + Balances: moonbeam_runtime::Balances, + EvmForeignAssets: moonbeam_runtime::EvmForeignAssets, + XcmWeightTrader: moonbeam_runtime::XcmWeightTrader, + XcmTransactor: moonbeam_runtime::XcmTransactor, + Treasury: moonbeam_runtime::Treasury, + EthereumXcm: moonbeam_runtime::EthereumXcm, + Proxy: moonbeam_runtime::Proxy, + EVM: moonbeam_runtime::EVM, + } + } +} + +// --------------------------------------------------------------------------- +// Asset Hub Westend declaration (para 1000, real asset-hub-westend-runtime) +// --------------------------------------------------------------------------- +decl_test_parachains! { + pub struct AssetHubPara { + genesis = asset_hub_genesis(), + on_init = { + asset_hub_westend_runtime::AuraExt::on_initialize(1); + }, + runtime = asset_hub_westend_runtime, + core = { + XcmpMessageHandler: asset_hub_westend_runtime::XcmpQueue, + LocationToAccountId: asset_hub_westend_runtime::xcm_config::LocationToAccountId, + ParachainInfo: asset_hub_westend_runtime::ParachainInfo, + MessageOrigin: cumulus_primitives_core::AggregateMessageOrigin, + }, + pallets = { + PolkadotXcm: asset_hub_westend_runtime::PolkadotXcm, + Balances: asset_hub_westend_runtime::Balances, + Assets: asset_hub_westend_runtime::Assets, + ForeignAssets: asset_hub_westend_runtime::ForeignAssets, + } + } +} + +// --------------------------------------------------------------------------- +// Network declaration +// --------------------------------------------------------------------------- +decl_test_networks! { + pub struct PolkadotMoonbeamNet { + relay_chain = WestendRelay, + parachains = vec![ + AssetHubPara, + MoonbeamPara, + SiblingPara, + ], + bridge = () + } +} + +// =========================================================================== +// Helpers +// =========================================================================== + +/// Execute a closure on the Moonbeam parachain (para 2004), automatically +/// satisfying mandatory inherent checks. +pub fn moonbeam_execute_with(f: impl FnOnce() -> R) -> R { + MoonbeamPara::::execute_with(|| { + satisfy_moonbeam_inherents(); + f() + }) +} + +/// Execute a closure on the Sibling parachain (para 2005), automatically +/// satisfying mandatory inherent checks. +pub fn sibling_execute_with(f: impl FnOnce() -> R) -> R { + SiblingPara::::execute_with(|| { + satisfy_moonbeam_inherents(); + f() + }) +} + +/// Execute a closure on Asset Hub (para 1000). +pub fn asset_hub_execute_with(f: impl FnOnce() -> R) -> R { + AssetHubPara::::execute_with(f) +} + +/// Patch storage to satisfy Moonbeam's mandatory inherent checks. +/// Called automatically by [`moonbeam_execute_with`] / [`sibling_execute_with`]. +pub(crate) fn satisfy_moonbeam_inherents() { + pallet_author_inherent::Author::::put( + moonbeam_runtime::AccountId::from([1u8; 20]), + ); + pallet_author_inherent::InherentIncluded::::put(true); + + frame_support::storage::unhashed::put( + &frame_support::storage::storage_prefix(b"Randomness", b"InherentIncluded"), + &(), + ); + frame_support::storage::unhashed::kill(&frame_support::storage::storage_prefix( + b"Randomness", + b"NotFirstBlock", + )); +} + +/// Initialise network and clear `NotFirstBlock` on all parachains. +pub fn init_network() { + // Trigger `Parachain::init()` on every chain by executing on relay. + WestendRelay::::execute_with(|| {}); + + // Clear NotFirstBlock so VRF verification is skipped in subsequent blocks. + MoonbeamPara::::ext_wrapper(|| { + frame_support::storage::unhashed::kill(&frame_support::storage::storage_prefix( + b"Randomness", + b"NotFirstBlock", + )); + }); + SiblingPara::::ext_wrapper(|| { + frame_support::storage::unhashed::kill(&frame_support::storage::storage_prefix( + b"Randomness", + b"NotFirstBlock", + )); + }); +} + +/// Register DOT as a foreign asset on a Moonbeam-runtime chain and configure +/// its price in the XCM weight trader. Call inside `moonbeam_execute_with` or +/// `sibling_execute_with`. +pub fn register_dot_asset(asset_id: u128) { + let dot_location = xcm::latest::Location::parent(); + + frame_support::assert_ok!(moonbeam_runtime::EvmForeignAssets::create_foreign_asset( + moonbeam_runtime::RuntimeOrigin::root(), + asset_id, + dot_location.clone(), + 10, + b"DOT".to_vec().try_into().unwrap(), + b"Polkadot".to_vec().try_into().unwrap(), + )); + + // relative_price large enough so that 10 DOT covers XCM execution fees. + frame_support::assert_ok!(moonbeam_runtime::XcmWeightTrader::add_asset( + moonbeam_runtime::RuntimeOrigin::root(), + dot_location, + 10_000_000_000_000_000_000_000_000_000u128, // 10^28 + )); +} + +/// Configure `pallet_xcm_transactor` relay indices for Westend. +/// Call inside `moonbeam_execute_with` or `sibling_execute_with`. +pub fn set_westend_relay_indices() { + use frame_support::traits::PalletInfoAccess; + use pallet_xcm_transactor::relay_indices::RelayChainIndices; + + // Validate pallet indices against the Westend runtime so we fail fast if + // they drift after a relay upgrade. + let staking_idx = westend_runtime::Staking::index() as u8; + let utility_idx = westend_runtime::Utility::index() as u8; + let hrmp_idx = westend_runtime::Hrmp::index() as u8; + + assert_eq!(staking_idx, 6u8, "Westend Staking pallet index has changed"); + assert_eq!( + utility_idx, 16u8, + "Westend Utility pallet index has changed" + ); + assert_eq!(hrmp_idx, 51u8, "Westend Hrmp pallet index has changed"); + + let indices = RelayChainIndices { + staking: staking_idx, + utility: utility_idx, + hrmp: hrmp_idx, + // Call indices within staking pallet: + bond: 0u8, + bond_extra: 1u8, + unbond: 2u8, + withdraw_unbonded: 3u8, + validate: 4u8, + nominate: 5u8, + chill: 6u8, + set_payee: 7u8, + set_controller: 8u8, + rebond: 19u8, + // Utility::as_derivative + as_derivative: 1u8, + // HRMP call indices: + init_open_channel: 0u8, + accept_open_channel: 1u8, + close_channel: 2u8, + cancel_open_request: 6u8, + }; + + pallet_xcm_transactor::RelayIndices::::put(indices); +} + +/// Open HRMP channels between two parachains on the relay. +/// Must be called inside `WestendRelay::execute_with`. +pub fn open_hrmp_channels(sender: u32, recipient: u32) { + use frame_support::assert_ok; + + assert_ok!(westend_runtime::Hrmp::force_open_hrmp_channel( + westend_runtime::RuntimeOrigin::root(), + sender.into(), + recipient.into(), + 8, // max_capacity + 1024, // max_message_size + )); + assert_ok!(westend_runtime::Hrmp::force_open_hrmp_channel( + westend_runtime::RuntimeOrigin::root(), + recipient.into(), + sender.into(), + 8, + 1024, + )); + assert_ok!(westend_runtime::Hrmp::force_process_hrmp_open( + westend_runtime::RuntimeOrigin::root(), + 2, + )); +} + +// --------------------------------------------------------------------------- +// Moonbeam genesis helper +// --------------------------------------------------------------------------- + +fn asset_hub_genesis() -> sp_core::storage::Storage { + use sp_runtime::BuildStorage; + + let endowment: u128 = 1_000_000_000_000_000; // 1 000 WND (12 decimals) + + let mut t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + + parachain_info::GenesisConfig:: { + parachain_id: ASSET_HUB_PARA_ID.into(), + _config: Default::default(), + } + .assimilate_storage(&mut t) + .unwrap(); + + // Pre-fund Asset Hub's XCM checking account so relay→AH teleports of the + // native (WND) asset don't fail with `NotWithdrawable`. Asset Hub tracks + // teleported-out WND in this account; in a fresh test genesis it starts + // at zero, so an incoming teleport cannot withdraw against it. + let checking_account: sp_runtime::AccountId32 = + sp_runtime::traits::AccountIdConversion::into_account_truncating(&frame_support::PalletId( + *b"py/xcmch", + )); + + pallet_balances::GenesisConfig:: { + balances: vec![ + (sp_runtime::AccountId32::new([1u8; 32]), endowment), + (sp_runtime::AccountId32::new([2u8; 32]), endowment), + (checking_account, endowment), + ], + ..Default::default() + } + .assimilate_storage(&mut t) + .unwrap(); + + // Populate at least one Aura authority. Asset Hub runs on pallet-aura + // and `FindAuthor` panics on `slot % authorities_len()` if it's empty. + xcm_emulator::pallet_aura::GenesisConfig:: { + authorities: vec![parachains_common::AuraId::from( + sp_core::sr25519::Public::from_raw([1u8; 32]), + )], + } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_xcm::GenesisConfig:: { + safe_xcm_version: Some(xcm::latest::VERSION), + ..Default::default() + } + .assimilate_storage(&mut t) + .unwrap(); + + t +} + +fn moonbeam_genesis(para_id: u32) -> sp_core::storage::Storage { + use moonbeam_runtime::{currency::GLMR, AccountId, Runtime}; + use sp_runtime::BuildStorage; + + let mut t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + + parachain_info::GenesisConfig:: { + parachain_id: para_id.into(), + _config: Default::default(), + } + .assimilate_storage(&mut t) + .unwrap(); + + // Endow generously: `pallet_author_mapping` reserves a deposit of + // `100 GLMR * SUPPLY_FACTOR` (10_000 GLMR on Moonbeam) from ALITH when + // the genesis mapping is registered, so 10_000 GLMR would leave ALITH + // with zero free balance and break XCM transfers in later tests. + pallet_balances::GenesisConfig:: { + balances: vec![ + (AccountId::from(ALITH), GLMR * 1_000_000), + (AccountId::from(BALTATHAR), GLMR * 1_000_000), + ], + dev_accounts: None, + } + .assimilate_storage(&mut t) + .unwrap(); + + // Map the NimbusId emitted by `MoonbeamBlockProducer` to ALITH so + // `pallet_author_inherent::FindAuthor` can resolve a block author. + pallet_author_mapping::GenesisConfig:: { + mappings: vec![( + NimbusId::from(sp_core::sr25519::Public::from_raw([1u8; 32])), + AccountId::from(ALITH), + )], + } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_xcm::GenesisConfig:: { + safe_xcm_version: Some(xcm::latest::VERSION), + ..Default::default() + } + .assimilate_storage(&mut t) + .unwrap(); + + t +} diff --git a/runtime/moonbeam/tests/xcm_emulator/relay.rs b/runtime/moonbeam/tests/xcm_emulator/relay.rs new file mode 100644 index 00000000000..15d8395d70d --- /dev/null +++ b/runtime/moonbeam/tests/xcm_emulator/relay.rs @@ -0,0 +1,94 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Relay chain genesis for xcm-emulator tests. +//! +//! Uses the full `westend_runtime` so we get real DMP routing, HRMP, and +//! the `ParachainHost` runtime API the emulator requires. + +pub use westend_runtime; + +use parity_scale_codec::Encode; +use sp_core::storage::Storage; +use sp_runtime::{traits::AccountIdConversion, AccountId32, BuildStorage}; + +use crate::network::{ASSET_HUB_PARA_ID, MOONBEAM_PARA_ID, SIBLING_PARA_ID}; + +/// Build relay `Storage` with both parachains registered and funded. +pub fn relay_genesis() -> Storage { + let asset_hub_sovereign: AccountId32 = + polkadot_parachain::primitives::Id::from(ASSET_HUB_PARA_ID).into_account_truncating(); + let moonbeam_sovereign: AccountId32 = + polkadot_parachain::primitives::Id::from(MOONBEAM_PARA_ID).into_account_truncating(); + let sibling_sovereign: AccountId32 = + polkadot_parachain::primitives::Id::from(SIBLING_PARA_ID).into_account_truncating(); + let endowment: u128 = 1_000_000_000_000_000; // 1 000 WND (12 decimals) + + let mut host_config = polkadot_runtime_parachains::configuration::HostConfiguration::default(); + host_config.max_downward_message_size = 1 << 20; + host_config.max_upward_message_size = 1 << 16; + host_config.max_upward_queue_count = 100; + host_config.max_upward_message_num_per_candidate = 10; + host_config.hrmp_max_message_num_per_candidate = 10; + host_config.hrmp_channel_max_capacity = 8; + host_config.hrmp_channel_max_total_size = 8 * 1024; + host_config.hrmp_channel_max_message_size = 1024; + host_config.hrmp_max_parachain_outbound_channels = 10; + host_config.hrmp_max_parachain_inbound_channels = 10; + + let genesis_config = westend_runtime::RuntimeGenesisConfig { + balances: westend_runtime::BalancesConfig { + balances: vec![ + (AccountId32::new([1u8; 32]), endowment), + (AccountId32::new([2u8; 32]), endowment), + (asset_hub_sovereign, endowment), + (moonbeam_sovereign, endowment), + (sibling_sovereign, endowment), + ], + ..Default::default() + }, + configuration: westend_runtime::ConfigurationConfig { + config: host_config, + }, + ..Default::default() + }; + + let mut storage = genesis_config + .build_storage() + .expect("Should build relay genesis storage"); + + // Register both parachains so DMP and HRMP consider them valid. + use frame_support::storage::generator::StorageMap; + for para_id in [ASSET_HUB_PARA_ID, MOONBEAM_PARA_ID, SIBLING_PARA_ID] { + let pid = polkadot_parachain::primitives::Id::from(para_id); + let head_data = polkadot_parachain::primitives::HeadData(vec![0u8; 32]); + let key = polkadot_runtime_parachains::paras::Heads::::storage_map_final_key(pid); + storage.top.insert(key, head_data.encode()); + + // Also register the ParaLifecycle as Parachain so HRMP considers them valid. + // ParaLifecycles is pub(super) so we cannot use storage_map_final_key(); + // reconstruct the key manually (Twox64Concat hasher on ParaId). + let prefix = frame_support::storage::storage_prefix(b"Paras", b"ParaLifecycles"); + let encoded_id = pid.encode(); + let mut full_key = prefix.to_vec(); + full_key.extend(&sp_io::hashing::twox_64(&encoded_id)); + full_key.extend(&encoded_id); + let lifecycle = polkadot_runtime_parachains::paras::ParaLifecycle::Parachain; + storage.top.insert(full_key, lifecycle.encode()); + } + + storage +} diff --git a/runtime/moonbeam/tests/xcm_emulator/transact.rs b/runtime/moonbeam/tests/xcm_emulator/transact.rs new file mode 100644 index 00000000000..148a0590e9c --- /dev/null +++ b/runtime/moonbeam/tests/xcm_emulator/transact.rs @@ -0,0 +1,1201 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! XcmTransactor tests using the **real** Moonbeam runtime against Westend relay. +//! +//! Covers: +//! - transact_through_sovereign (relay) — basic, fee_payer=None, custom fee/weight, refund +//! - transact_through_derivative (relay) — basic, custom fee/weight, refund +//! - transact_through_signed (relay) — basic, custom fee/weight, refund +//! - HRMP channel management (init/accept/close) + +use crate::network::*; +use frame_support::{ + assert_ok, + traits::fungible::{Inspect, Mutate}, +}; +use pallet_xcm_transactor::{Currency, CurrencyPayment, HrmpOperation, TransactWeights}; +use parity_scale_codec::Encode; +use sp_core::U256; +use xcm::latest::prelude::*; +use xcm_emulator::{RelayChain, TestExt}; +use xcm_executor::traits::ConvertLocation; + +// =========================================================================== +// Setup +// =========================================================================== + +fn setup_transactor() { + init_network(); + + moonbeam_execute_with(|| { + register_dot_asset(DOT_ASSET_ID); + set_westend_relay_indices(); + + // Configure transact info for the relay destination. + assert_ok!(moonbeam_runtime::XcmTransactor::set_transact_info( + moonbeam_runtime::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::from(Location::parent())), + 3_000u64.into(), // extra_weight (relay charges per instruction) + 20_000_000_000u64.into(), // max_weight + // 4 instructions in transact_through_signed + Some(4_000u64.into()), + )); + }); + + // Fund Moonbeam's sovereign on relay so it can pay fees for UMP transacts. + WestendRelay::::execute_with(|| { + // The sovereign is already funded via relay genesis (endowment). + }); +} + +/// Encode a `system::remark_with_event` call for the Westend relay. +fn relay_remark_call() -> Vec { + westend_runtime::RuntimeCall::System( + frame_system::Call::::remark_with_event { + remark: b"hello from Moonbeam".to_vec(), + }, + ) + .encode() +} + +/// Derive the relay account for a signed XCM origin from a parachain user. +/// The XCM `DescendOrigin(AccountKey20(key))` shifts the origin to +/// `Parachain(para_id)/AccountKey20(key)`, which the relay's `LocationConverter` +/// hashes into a 32-byte account. +fn relay_derived_account(para_id: u32, key: [u8; 20]) -> sp_runtime::AccountId32 { + let location = Location::new(0, [Parachain(para_id), AccountKey20 { network: None, key }]); + westend_runtime::xcm_config::LocationConverter::convert_location(&location) + .expect("Should derive relay account from parachain signed origin") +} + +/// Assert that the relay processed a UMP message and emitted a Remarked event. +fn assert_relay_remark_executed() { + WestendRelay::::execute_with(|| { + let events = westend_runtime::System::events(); + + let was_processed = events.iter().any(|e| { + matches!( + &e.event, + westend_runtime::RuntimeEvent::MessageQueue( + pallet_message_queue::Event::Processed { success: true, .. } + ) + ) + }); + assert!( + was_processed, + "Relay should have successfully processed the UMP transact" + ); + + let has_remark = events.iter().any(|e| { + matches!( + &e.event, + westend_runtime::RuntimeEvent::System(frame_system::Event::Remarked { .. }) + ) + }); + assert!(has_remark, "Relay should have emitted a Remarked event"); + }); +} + +/// Send DOT from relay to Moonbeam ALITH. +fn fund_moonbeam_alith_with_dot(amount: u128) { + WestendRelay::::execute_with(|| { + assert_ok!( + westend_runtime::XcmPallet::transfer_assets_using_type_and_then( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(MOONBEAM_PARA_ID)] + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(amount), + }]))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedAssetId::from(AssetId(Location::here()))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: ALITH + }], + ), + }]))), + WeightLimit::Unlimited, + ) + ); + }); +} + +// =========================================================================== +// Transact through sovereign (para → relay) +// =========================================================================== + +#[test] +fn transact_through_sovereign_to_relay() { + setup_transactor(); + fund_moonbeam_alith_with_dot(ONE_DOT * 1000); + + // Check the sovereign account balance on relay before transact. + let sovereign = WestendRelay::::execute_with(|| { + WestendRelay::::sovereign_account_id_of(Location::new( + 0, + [Parachain(MOONBEAM_PARA_ID)], + )) + }); + let sovereign_before = WestendRelay::::execute_with(|| { + >::balance(&sovereign) + }); + assert!( + sovereign_before > 0, + "Sovereign should be funded from genesis" + ); + + moonbeam_execute_with(|| { + assert_ok!(moonbeam_runtime::XcmTransactor::transact_through_sovereign( + moonbeam_runtime::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::from(Location::parent())), + Some(moonbeam_runtime::AccountId::from(ALITH)), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent() + ))), + fee_amount: Some(ONE_DOT), // explicit fee + }, + relay_remark_call(), + OriginKind::SovereignAccount, + TransactWeights { + transact_required_weight_at_most: 1_000_000_000u64.into(), + overall_weight: Some(Limited(2_000_000_000u64.into())), + }, + false, + )); + }); + + assert_relay_remark_executed(); + + // Verify the sovereign paid fees for the XCM execution. + let sovereign_after = WestendRelay::::execute_with(|| { + >::balance(&sovereign) + }); + assert!( + sovereign_after <= sovereign_before, + "Sovereign should have spent DOT: before={sovereign_before}, after={sovereign_after}" + ); +} + +// =========================================================================== +// HRMP: open and close channels via XcmTransactor +// =========================================================================== + +#[test] +fn hrmp_init_accept_close_via_xcm_transactor() { + init_network(); + + moonbeam_execute_with(|| { + register_dot_asset(DOT_ASSET_ID); + set_westend_relay_indices(); + }); + sibling_execute_with(|| { + register_dot_asset(DOT_ASSET_ID); + set_westend_relay_indices(); + }); + + use pallet_xcm_transactor::{HrmpInitParams, HrmpOperation}; + + // Step 1: Moonbeam requests to open channel to sibling. + moonbeam_execute_with(|| { + assert_ok!(moonbeam_runtime::XcmTransactor::hrmp_manage( + moonbeam_runtime::RuntimeOrigin::root(), + HrmpOperation::InitOpen(HrmpInitParams { + para_id: SIBLING_PARA_ID.into(), + proposed_max_capacity: 8, + proposed_max_message_size: 1024, + }), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent() + ))), + fee_amount: Some(ONE_DOT * 100), + }, + TransactWeights { + transact_required_weight_at_most: 5_000_000_000u64.into(), + overall_weight: Some(Limited(10_000_000_000u64.into())), + }, + )); + }); + + // Verify the open-channel request arrived on relay. + WestendRelay::::execute_with(|| { + let events = westend_runtime::System::events(); + let has_open_request = events.iter().any(|e| { + matches!( + &e.event, + westend_runtime::RuntimeEvent::Hrmp( + polkadot_runtime_parachains::hrmp::Event::OpenChannelRequested { .. } + ) + ) + }); + assert!( + has_open_request, + "Relay should have emitted OpenChannelRequested" + ); + }); + + // Step 2: Sibling accepts the channel. + sibling_execute_with(|| { + assert_ok!(moonbeam_runtime::XcmTransactor::hrmp_manage( + moonbeam_runtime::RuntimeOrigin::root(), + HrmpOperation::Accept { + para_id: MOONBEAM_PARA_ID.into(), + }, + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent() + ))), + fee_amount: Some(ONE_DOT * 100), + }, + TransactWeights { + transact_required_weight_at_most: 5_000_000_000u64.into(), + overall_weight: Some(Limited(10_000_000_000u64.into())), + }, + )); + }); + + WestendRelay::::execute_with(|| { + let events = westend_runtime::System::events(); + let has_accept = events.iter().any(|e| { + matches!( + &e.event, + westend_runtime::RuntimeEvent::Hrmp( + polkadot_runtime_parachains::hrmp::Event::OpenChannelAccepted { .. } + ) + ) + }); + assert!(has_accept, "Relay should have emitted OpenChannelAccepted"); + }); + + // Step 3: Process the pending open requests and verify the channel is established. + WestendRelay::::execute_with(|| { + assert_ok!(westend_runtime::Hrmp::force_process_hrmp_open( + westend_runtime::RuntimeOrigin::root(), + 1, + )); + + use polkadot_runtime_parachains::hrmp; + let channel = + hrmp::HrmpChannels::::get(xcm_emulator::HrmpChannelId { + sender: MOONBEAM_PARA_ID.into(), + recipient: SIBLING_PARA_ID.into(), + }); + assert!( + channel.is_some(), + "HRMP channel Moonbeam → Sibling should be established" + ); + }); +} + +// =========================================================================== +// HRMP: close channel via XcmTransactor +// =========================================================================== + +#[test] +fn hrmp_close_via_xcm_transactor() { + init_network(); + + moonbeam_execute_with(|| { + register_dot_asset(DOT_ASSET_ID); + set_westend_relay_indices(); + }); + + // Force-open a channel so we can close it. + WestendRelay::::execute_with(|| { + open_hrmp_channels(MOONBEAM_PARA_ID, SIBLING_PARA_ID); + }); + + // Close the channel from Moonbeam side via XcmTransactor. + moonbeam_execute_with(|| { + assert_ok!(moonbeam_runtime::XcmTransactor::hrmp_manage( + moonbeam_runtime::RuntimeOrigin::root(), + HrmpOperation::Close(xcm_emulator::HrmpChannelId { + sender: MOONBEAM_PARA_ID.into(), + recipient: SIBLING_PARA_ID.into(), + }), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent(), + ))), + fee_amount: Some(ONE_DOT * 100), + }, + TransactWeights { + transact_required_weight_at_most: 5_000_000_000u64.into(), + overall_weight: Some(Limited(10_000_000_000u64.into())), + }, + )); + }); + + // Verify the close event on relay. + WestendRelay::::execute_with(|| { + let events = westend_runtime::System::events(); + let has_close = events.iter().any(|e| { + matches!( + &e.event, + westend_runtime::RuntimeEvent::Hrmp( + polkadot_runtime_parachains::hrmp::Event::ChannelClosed { .. } + ) + ) + }); + assert!(has_close, "Relay should have emitted ChannelClosed"); + }); +} + +// =========================================================================== +// Transact through sovereign: fee_payer = None +// =========================================================================== + +#[test] +fn transact_through_sovereign_fee_payer_none() { + setup_transactor(); + + // With fee_payer = None, no local withdraw happens — only the sovereign on + // relay pays. The sovereign must be funded from genesis. + moonbeam_execute_with(|| { + assert_ok!(moonbeam_runtime::XcmTransactor::transact_through_sovereign( + moonbeam_runtime::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::from(Location::parent())), + None, // no fee payer — no local withdraw + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent(), + ))), + fee_amount: Some(ONE_DOT), + }, + relay_remark_call(), + OriginKind::SovereignAccount, + TransactWeights { + transact_required_weight_at_most: 1_000_000_000u64.into(), + overall_weight: Some(Limited(2_000_000_000u64.into())), + }, + false, + )); + }); + + assert_relay_remark_executed(); +} + +// =========================================================================== +// Transact through sovereign: custom fee & weight (no refund) +// =========================================================================== + +#[test] +fn transact_through_sovereign_custom_fee_weight() { + setup_transactor(); + fund_moonbeam_alith_with_dot(ONE_DOT * 1000); + + moonbeam_execute_with(|| { + assert_ok!(moonbeam_runtime::XcmTransactor::transact_through_sovereign( + moonbeam_runtime::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::from(Location::parent())), + Some(moonbeam_runtime::AccountId::from(ALITH)), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent(), + ))), + fee_amount: Some(ONE_DOT * 5), // explicit larger fee + }, + relay_remark_call(), + OriginKind::SovereignAccount, + TransactWeights { + transact_required_weight_at_most: 2_000_000_000u64.into(), + overall_weight: Some(Limited(4_000_000_000u64.into())), + }, + false, + )); + }); + + assert_relay_remark_executed(); +} + +// =========================================================================== +// Transact through sovereign: custom fee, weight & refund +// =========================================================================== + +#[test] +fn transact_through_sovereign_custom_fee_weight_refund() { + setup_transactor(); + fund_moonbeam_alith_with_dot(ONE_DOT * 1000); + + let sovereign_before = WestendRelay::::execute_with(|| { + let sovereign = WestendRelay::::sovereign_account_id_of( + Location::new(0, [Parachain(MOONBEAM_PARA_ID)]), + ); + >::balance(&sovereign) + }); + + moonbeam_execute_with(|| { + assert_ok!(moonbeam_runtime::XcmTransactor::transact_through_sovereign( + moonbeam_runtime::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::from(Location::parent())), + Some(moonbeam_runtime::AccountId::from(ALITH)), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent(), + ))), + fee_amount: Some(ONE_DOT * 10), // overpay to test refund + }, + relay_remark_call(), + OriginKind::SovereignAccount, + TransactWeights { + transact_required_weight_at_most: 2_000_000_000u64.into(), + overall_weight: Some(Limited(4_000_000_000u64.into())), + }, + true, // refund = true + )); + }); + + assert_relay_remark_executed(); + + // With refund=true, leftover fees are deposited back to the sovereign. + // The sovereign should have lost less than the full 10 DOT fee. + let sovereign_after = WestendRelay::::execute_with(|| { + let sovereign = WestendRelay::::sovereign_account_id_of( + Location::new(0, [Parachain(MOONBEAM_PARA_ID)]), + ); + >::balance(&sovereign) + }); + let fee_spent = sovereign_before.saturating_sub(sovereign_after); + assert!( + fee_spent < ONE_DOT * 10, + "With refund, sovereign should spend less than the full fee: spent={fee_spent}" + ); +} + +// =========================================================================== +// Transact through signed (para → relay) +// =========================================================================== + +#[test] +fn transact_through_signed_to_relay() { + setup_transactor(); + fund_moonbeam_alith_with_dot(ONE_DOT * 1000); + + let derived_account = relay_derived_account(MOONBEAM_PARA_ID, ALITH); + + // Fund the derived account on relay so it can pay XCM fees. + WestendRelay::::execute_with(|| { + assert_ok!(westend_runtime::Balances::transfer_allow_death( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE), + derived_account.clone().into(), + ONE_DOT * 100, + )); + }); + + moonbeam_execute_with(|| { + assert_ok!(moonbeam_runtime::XcmTransactor::transact_through_signed( + moonbeam_runtime::RuntimeOrigin::signed(moonbeam_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::parent())), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent(), + ))), + fee_amount: Some(ONE_DOT * 10), + }, + relay_remark_call(), + TransactWeights { + transact_required_weight_at_most: 1_000_000_000u64.into(), + overall_weight: Some(Limited(4_000_000_000u64.into())), + }, + false, + )); + }); + + assert_relay_remark_executed(); +} + +// =========================================================================== +// Transact through signed: custom fee & weight +// =========================================================================== + +#[test] +fn transact_through_signed_custom_fee_weight() { + setup_transactor(); + fund_moonbeam_alith_with_dot(ONE_DOT * 1000); + + let derived_account = relay_derived_account(MOONBEAM_PARA_ID, ALITH); + + WestendRelay::::execute_with(|| { + assert_ok!(westend_runtime::Balances::transfer_allow_death( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE), + derived_account.clone().into(), + ONE_DOT * 100, + )); + }); + + moonbeam_execute_with(|| { + assert_ok!(moonbeam_runtime::XcmTransactor::transact_through_signed( + moonbeam_runtime::RuntimeOrigin::signed(moonbeam_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::parent())), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent(), + ))), + fee_amount: Some(ONE_DOT * 5), + }, + relay_remark_call(), + TransactWeights { + transact_required_weight_at_most: 2_000_000_000u64.into(), + overall_weight: Some(Limited(6_000_000_000u64.into())), + }, + false, + )); + }); + + assert_relay_remark_executed(); +} + +// =========================================================================== +// Transact through signed: custom fee, weight & refund +// =========================================================================== + +#[test] +fn transact_through_signed_custom_fee_weight_refund() { + setup_transactor(); + fund_moonbeam_alith_with_dot(ONE_DOT * 1000); + + let derived_account = relay_derived_account(MOONBEAM_PARA_ID, ALITH); + + WestendRelay::::execute_with(|| { + assert_ok!(westend_runtime::Balances::transfer_allow_death( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE), + derived_account.clone().into(), + ONE_DOT * 100, + )); + }); + + let derived_before = WestendRelay::::execute_with(|| { + >::balance(&derived_account) + }); + + moonbeam_execute_with(|| { + assert_ok!(moonbeam_runtime::XcmTransactor::transact_through_signed( + moonbeam_runtime::RuntimeOrigin::signed(moonbeam_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::parent())), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent(), + ))), + fee_amount: Some(ONE_DOT * 20), // overpay + }, + relay_remark_call(), + TransactWeights { + transact_required_weight_at_most: 2_000_000_000u64.into(), + overall_weight: Some(Limited(6_000_000_000u64.into())), + }, + true, // refund = true + )); + }); + + assert_relay_remark_executed(); + + // With refund, the derived account should get surplus back. + let derived_after = WestendRelay::::execute_with(|| { + >::balance(&derived_account) + }); + let fee_spent = derived_before.saturating_sub(derived_after); + assert!( + fee_spent < ONE_DOT * 20, + "With refund, derived account should spend less than the full fee: spent={fee_spent}" + ); +} + +// =========================================================================== +// Transact through derivative +// =========================================================================== + +/// Setup for derivative transact tests. +/// Registers ALITH as the owner of derivative index 0 and funds the derivative +/// sub-account on the relay. +fn setup_derivative() { + setup_transactor(); + fund_moonbeam_alith_with_dot(ONE_DOT * 1000); + + let derivative_index: u16 = 0; + + // Register ALITH as the owner of index 0. + moonbeam_execute_with(|| { + assert_ok!(moonbeam_runtime::XcmTransactor::register( + moonbeam_runtime::RuntimeOrigin::root(), + moonbeam_runtime::AccountId::from(ALITH), + derivative_index, + )); + }); + + // Fund the derivative account on relay. + // The derivative is computed from the sovereign account of Moonbeam parachain. + WestendRelay::::execute_with(|| { + let sovereign = WestendRelay::::sovereign_account_id_of( + Location::new(0, [Parachain(MOONBEAM_PARA_ID)]), + ); + let derivative = pallet_utility::derivative_account_id(sovereign, derivative_index); + assert_ok!(westend_runtime::Balances::transfer_allow_death( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE), + derivative.into(), + ONE_DOT * 100, + )); + }); +} + +#[test] +fn transact_through_derivative_to_relay() { + setup_derivative(); + + moonbeam_execute_with(|| { + assert_ok!( + moonbeam_runtime::XcmTransactor::transact_through_derivative( + moonbeam_runtime::RuntimeOrigin::signed(moonbeam_runtime::AccountId::from(ALITH),), + moonbeam_runtime::xcm_config::Transactors::Relay, + 0u16, // derivative index + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent() + ),)), + fee_amount: Some(ONE_DOT * 10), + }, + // Inner call (unwrapped — the pallet wraps it in as_derivative). + relay_remark_call(), + TransactWeights { + transact_required_weight_at_most: 2_000_000_000u64.into(), + overall_weight: Some(Limited(4_000_000_000u64.into())), + }, + false, + ) + ); + }); + + assert_relay_remark_executed(); +} + +#[test] +fn transact_through_derivative_custom_fee_weight() { + setup_derivative(); + + moonbeam_execute_with(|| { + assert_ok!( + moonbeam_runtime::XcmTransactor::transact_through_derivative( + moonbeam_runtime::RuntimeOrigin::signed(moonbeam_runtime::AccountId::from(ALITH),), + moonbeam_runtime::xcm_config::Transactors::Relay, + 0u16, + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent() + ),)), + fee_amount: Some(ONE_DOT * 5), + }, + relay_remark_call(), + TransactWeights { + transact_required_weight_at_most: 3_000_000_000u64.into(), + overall_weight: Some(Limited(6_000_000_000u64.into())), + }, + false, + ) + ); + }); + + assert_relay_remark_executed(); +} + +#[test] +fn transact_through_derivative_custom_fee_weight_refund() { + setup_derivative(); + + let sovereign_before = WestendRelay::::execute_with(|| { + let sovereign = WestendRelay::::sovereign_account_id_of( + Location::new(0, [Parachain(MOONBEAM_PARA_ID)]), + ); + >::balance(&sovereign) + }); + + moonbeam_execute_with(|| { + assert_ok!( + moonbeam_runtime::XcmTransactor::transact_through_derivative( + moonbeam_runtime::RuntimeOrigin::signed(moonbeam_runtime::AccountId::from(ALITH),), + moonbeam_runtime::xcm_config::Transactors::Relay, + 0u16, + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent() + ),)), + fee_amount: Some(ONE_DOT * 20), // overpay + }, + relay_remark_call(), + TransactWeights { + transact_required_weight_at_most: 2_000_000_000u64.into(), + overall_weight: Some(Limited(4_000_000_000u64.into())), + }, + true, // refund + ) + ); + }); + + assert_relay_remark_executed(); + + // With refund, surplus should be deposited back to the sovereign (SelfLocation). + let sovereign_after = WestendRelay::::execute_with(|| { + let sovereign = WestendRelay::::sovereign_account_id_of( + Location::new(0, [Parachain(MOONBEAM_PARA_ID)]), + ); + >::balance(&sovereign) + }); + let fee_spent = sovereign_before.saturating_sub(sovereign_after); + assert!( + fee_spent < ONE_DOT * 20, + "With refund, sovereign should spend less than the full fee: spent={fee_spent}" + ); +} + +// =========================================================================== +// Transact through signed: para → para +// =========================================================================== + +/// Setup for para-to-para transact tests via signed origin. +/// Opens HRMP channels between Moonbeam and Sibling, registers DOT on both, +/// and funds the derived account on the sibling. +fn setup_para_to_para_signed() -> moonbeam_runtime::AccountId { + init_network(); + + // Register DOT + relay indices on Moonbeam. + moonbeam_execute_with(|| { + register_dot_asset(DOT_ASSET_ID); + set_westend_relay_indices(); + }); + + // Open HRMP channels. + WestendRelay::::execute_with(|| { + open_hrmp_channels(MOONBEAM_PARA_ID, SIBLING_PARA_ID); + }); + + // Register DOT on sibling so it can accept DOT as XCM fee. + sibling_execute_with(|| { + register_dot_asset(DOT_ASSET_ID); + }); + + // Compute the derived account on the sibling for ALITH's signed origin from Moonbeam. + // After DescendOrigin(AccountKey20(ALITH)), the sibling sees origin + // Location::new(1, [Parachain(2004), AccountKey20(ALITH)]). + let derived_on_sibling: moonbeam_runtime::AccountId = sibling_execute_with(|| { + >::convert_location(&Location::new( + 1, + [ + Parachain(MOONBEAM_PARA_ID), + AccountKey20 { + network: None, + key: ALITH, + }, + ], + )) + .expect("Should derive sibling account for Moonbeam ALITH") + }); + + // Fund the derived account on sibling with DOT (relay → sibling DMP). + WestendRelay::::execute_with(|| { + assert_ok!( + westend_runtime::XcmPallet::transfer_assets_using_type_and_then( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(SIBLING_PARA_ID)], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(ONE_DOT * 100), + }]))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedAssetId::from(AssetId(Location::here()))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: derived_on_sibling.into(), + }], + ), + }]))), + WeightLimit::Unlimited, + ) + ); + }); + + // Verify the derived account received DOT. + sibling_execute_with(|| { + let balance = + moonbeam_runtime::EvmForeignAssets::balance(DOT_ASSET_ID, derived_on_sibling).unwrap(); + assert!( + balance > sp_core::U256::zero(), + "Derived account on sibling should have DOT" + ); + }); + + derived_on_sibling +} + +/// Encode a `system::remark_with_event` call for the sibling (Moonbeam runtime). +fn sibling_remark_call() -> Vec { + moonbeam_runtime::RuntimeCall::System( + frame_system::Call::::remark_with_event { + remark: b"hello from Moonbeam to sibling".to_vec(), + }, + ) + .encode() +} + +/// Assert that the sibling processed the HRMP transact and emitted a Remarked event. +fn assert_sibling_remark_executed() { + sibling_execute_with(|| { + let events = moonbeam_runtime::System::events(); + + let has_remark = events.iter().any(|e| { + matches!( + &e.event, + moonbeam_runtime::RuntimeEvent::System(frame_system::Event::Remarked { .. }) + ) + }); + assert!( + has_remark, + "Sibling should have emitted Remarked event from transact" + ); + }); +} + +#[test] +fn transact_through_signed_para_to_para() { + setup_para_to_para_signed(); + + moonbeam_execute_with(|| { + assert_ok!(moonbeam_runtime::XcmTransactor::transact_through_signed( + moonbeam_runtime::RuntimeOrigin::signed(moonbeam_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(SIBLING_PARA_ID)], + ))), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent(), + ))), + fee_amount: Some(ONE_DOT * 10), + }, + sibling_remark_call(), + TransactWeights { + transact_required_weight_at_most: 1_000_000_000u64.into(), + overall_weight: Some(Limited(4_000_000_000u64.into())), + }, + false, + )); + }); + + assert_sibling_remark_executed(); +} + +#[test] +fn transact_through_signed_para_to_para_refund() { + let derived_on_sibling = setup_para_to_para_signed(); + + let dot_before = sibling_execute_with(|| { + moonbeam_runtime::EvmForeignAssets::balance(DOT_ASSET_ID, derived_on_sibling).unwrap() + }); + + moonbeam_execute_with(|| { + assert_ok!(moonbeam_runtime::XcmTransactor::transact_through_signed( + moonbeam_runtime::RuntimeOrigin::signed(moonbeam_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(SIBLING_PARA_ID)], + ))), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent(), + ))), + fee_amount: Some(ONE_DOT * 20), // overpay + }, + sibling_remark_call(), + TransactWeights { + transact_required_weight_at_most: 1_000_000_000u64.into(), + // Refund appendix (RefundSurplus + DepositAsset) needs extra weight. + overall_weight: Some(Limited(8_000_000_000u64.into())), + }, + true, // refund = true + )); + }); + + assert_sibling_remark_executed(); + + // With refund, the derived account should get surplus back. + let dot_after = sibling_execute_with(|| { + moonbeam_runtime::EvmForeignAssets::balance(DOT_ASSET_ID, derived_on_sibling).unwrap() + }); + let spent = dot_before.saturating_sub(dot_after); + assert!( + spent < sp_core::U256::from(ONE_DOT * 20), + "With refund, derived account should spend less than the full 20 DOT fee: spent={spent}" + ); +} + +// =========================================================================== +// Transact through signed: para → para (EthereumXcm) +// =========================================================================== + +/// Common setup for Ethereum XCM transact tests. +/// Returns the derived account on the sibling. +fn setup_para_to_para_ethereum() -> moonbeam_runtime::AccountId { + let derived_on_sibling = setup_para_to_para_signed(); + + // The derived account needs GLMR on the sibling for EVM value transfers. + sibling_execute_with(|| { + >::mint_into( + &derived_on_sibling, + moonbeam_runtime::currency::GLMR * 10, + ) + .expect("Should mint GLMR for derived account on sibling"); + }); + + derived_on_sibling +} + +/// Encode an `EthereumXcm::transact` call that does an EVM value transfer. +fn ethereum_xcm_transfer_call(recipient: sp_core::H160, value: u128) -> Vec { + use sp_runtime::BoundedVec; + + let eth_tx = + xcm_primitives::EthereumXcmTransaction::V1(xcm_primitives::EthereumXcmTransactionV1 { + gas_limit: U256::from(21000), + fee_payment: xcm_primitives::EthereumXcmFee::Auto, + action: pallet_ethereum::TransactionAction::Call(recipient), + value: U256::from(value), + input: BoundedVec::< + u8, + sp_core::ConstU32<{ xcm_primitives::MAX_ETHEREUM_XCM_INPUT_SIZE }>, + >::try_from(vec![]) + .unwrap(), + access_list: None, + }); + + moonbeam_runtime::RuntimeCall::EthereumXcm(pallet_ethereum_xcm::Call::< + moonbeam_runtime::Runtime, + >::transact { + xcm_transaction: eth_tx, + }) + .encode() +} + +/// EVM transfer to ALITH on sibling via EthereumXcm::transact. +#[test] +fn transact_through_signed_para_to_para_ethereum() { + let _derived = setup_para_to_para_ethereum(); + + let transfer_value = 100u128; + let alith_h160 = sp_core::H160::from(ALITH); + + let alith_balance_before = sibling_execute_with(|| { + >::balance(&moonbeam_runtime::AccountId::from( + ALITH, + )) + }); + + moonbeam_execute_with(|| { + assert_ok!(moonbeam_runtime::XcmTransactor::transact_through_signed( + moonbeam_runtime::RuntimeOrigin::signed(moonbeam_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(SIBLING_PARA_ID)], + ))), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent(), + ))), + fee_amount: Some(ONE_DOT * 10), + }, + ethereum_xcm_transfer_call(alith_h160, transfer_value), + TransactWeights { + transact_required_weight_at_most: 4_000_000_000u64.into(), + overall_weight: Some(Limited(8_000_000_000u64.into())), + }, + false, + )); + }); + + let alith_balance_after = sibling_execute_with(|| { + >::balance(&moonbeam_runtime::AccountId::from( + ALITH, + )) + }); + assert_eq!( + alith_balance_after - alith_balance_before, + transfer_value, + "ALITH should receive {transfer_value} WEI on sibling via EthereumXcm transact" + ); +} + +/// EthereumXcm::transact_through_proxy fails without a proxy set up. +#[test] +fn transact_through_signed_para_to_para_ethereum_no_proxy_fails() { + let _derived = setup_para_to_para_ethereum(); + + let alith_h160 = sp_core::H160::from(ALITH); + // Use a distinct recipient so a self-transfer cannot mask proxy rejection. + let recipient: [u8; 20] = [42u8; 20]; + let recipient_h160 = sp_core::H160::from(recipient); + let transfer_value = 100u128; + + // Encode a transact_through_proxy call without any proxy being set. + let eth_tx = + xcm_primitives::EthereumXcmTransaction::V2(xcm_primitives::EthereumXcmTransactionV2 { + gas_limit: U256::from(21000), + action: pallet_ethereum::TransactionAction::Call(recipient_h160), + value: U256::from(transfer_value), + input: sp_runtime::BoundedVec::try_from(vec![]).unwrap(), + access_list: None, + }); + + let proxy_call = moonbeam_runtime::RuntimeCall::EthereumXcm(pallet_ethereum_xcm::Call::< + moonbeam_runtime::Runtime, + >::transact_through_proxy { + transact_as: alith_h160, + xcm_transaction: eth_tx, + }) + .encode(); + + let recipient_balance_before = sibling_execute_with(|| { + >::balance(&moonbeam_runtime::AccountId::from( + recipient, + )) + }); + + moonbeam_execute_with(|| { + assert_ok!(moonbeam_runtime::XcmTransactor::transact_through_signed( + moonbeam_runtime::RuntimeOrigin::signed(moonbeam_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(SIBLING_PARA_ID)], + ))), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent(), + ))), + fee_amount: Some(ONE_DOT * 10), + }, + proxy_call, + TransactWeights { + transact_required_weight_at_most: 4_000_000_000u64.into(), + overall_weight: Some(Limited(8_000_000_000u64.into())), + }, + false, + )); + }); + + // The EVM transfer should NOT have happened (proxy not set). + let recipient_balance_after = sibling_execute_with(|| { + >::balance(&moonbeam_runtime::AccountId::from( + recipient, + )) + }); + assert_eq!( + recipient_balance_after, recipient_balance_before, + "Recipient balance should be unchanged — transact_through_proxy should fail without proxy" + ); +} + +/// EthereumXcm::transact_through_proxy succeeds with a proxy set up. +#[test] +fn transact_through_signed_para_to_para_ethereum_proxy_succeeds() { + let derived = setup_para_to_para_ethereum(); + + let recipient: [u8; 20] = [42u8; 20]; + let transfer_value = 100u128; + + // Set up proxy: ALITH delegates to the derived account on the sibling. + sibling_execute_with(|| { + assert_ok!(moonbeam_runtime::Proxy::add_proxy( + moonbeam_runtime::RuntimeOrigin::signed(moonbeam_runtime::AccountId::from(ALITH)), + derived, + moonbeam_runtime::ProxyType::Any, + 0, + )); + }); + + let recipient_balance_before = sibling_execute_with(|| { + >::balance(&moonbeam_runtime::AccountId::from( + recipient, + )) + }); + + // Encode a transact_through_proxy call targeting ALITH as proxy principal, + // EVM transfer to `recipient`. + let eth_tx = + xcm_primitives::EthereumXcmTransaction::V2(xcm_primitives::EthereumXcmTransactionV2 { + gas_limit: U256::from(21000), + action: pallet_ethereum::TransactionAction::Call(sp_core::H160::from(recipient)), + value: U256::from(transfer_value), + input: sp_runtime::BoundedVec::try_from(vec![]).unwrap(), + access_list: None, + }); + + let proxy_call = moonbeam_runtime::RuntimeCall::EthereumXcm(pallet_ethereum_xcm::Call::< + moonbeam_runtime::Runtime, + >::transact_through_proxy { + transact_as: sp_core::H160::from(ALITH), + xcm_transaction: eth_tx, + }) + .encode(); + + moonbeam_execute_with(|| { + assert_ok!(moonbeam_runtime::XcmTransactor::transact_through_signed( + moonbeam_runtime::RuntimeOrigin::signed(moonbeam_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(SIBLING_PARA_ID)], + ))), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent(), + ))), + fee_amount: Some(ONE_DOT * 10), + }, + proxy_call, + TransactWeights { + transact_required_weight_at_most: 4_000_000_000u64.into(), + overall_weight: Some(Limited(8_000_000_000u64.into())), + }, + false, + )); + }); + + let recipient_balance_after = sibling_execute_with(|| { + >::balance(&moonbeam_runtime::AccountId::from( + recipient, + )) + }); + assert_eq!( + recipient_balance_after - recipient_balance_before, + transfer_value, + "Recipient should receive {transfer_value} WEI via EthereumXcm proxy transact" + ); +} diff --git a/runtime/moonbeam/tests/xcm_emulator/transfers.rs b/runtime/moonbeam/tests/xcm_emulator/transfers.rs new file mode 100644 index 00000000000..f05a57505df --- /dev/null +++ b/runtime/moonbeam/tests/xcm_emulator/transfers.rs @@ -0,0 +1,1275 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Transfer tests using xcm-emulator with the **real** Moonbeam runtime. +//! +//! Covers: relay→para, para→relay, para→para transfers, fee behaviour, +//! account sufficiency, and error cases. + +use crate::network::*; +use frame_support::{ + assert_ok, + traits::{fungible::Inspect, tokens::fungible::Mutate}, +}; +use sp_core::U256; +use xcm::latest::prelude::*; +use xcm_emulator::TestExt; + +// =========================================================================== +// Setup helper +// =========================================================================== + +/// Full network init: register DOT on Moonbeam, configure weight trader. +fn setup_relay_to_moonbeam() { + init_network(); + moonbeam_execute_with(|| { + register_dot_asset(DOT_ASSET_ID); + }); +} + +/// Full network init with sibling: register DOT on both paras, open HRMP channels. +fn setup_with_sibling() { + init_network(); + + moonbeam_execute_with(|| { + register_dot_asset(DOT_ASSET_ID); + }); + sibling_execute_with(|| { + register_dot_asset(DOT_ASSET_ID); + }); + + // Open bi-directional HRMP channels between Moonbeam (2004) and Sibling (2005). + WestendRelay::::execute_with(|| { + open_hrmp_channels(MOONBEAM_PARA_ID, SIBLING_PARA_ID); + }); +} + +// =========================================================================== +// Transfer: Relay → Moonbeam (DMP) +// =========================================================================== + +#[test] +fn transfer_dot_from_relay_to_moonbeam() { + setup_relay_to_moonbeam(); + + let sender = RELAY_ALICE; + let beneficiary_key = ALITH; + + WestendRelay::::execute_with(|| { + let balance_before = >::balance(&sender); + + assert_ok!( + westend_runtime::XcmPallet::transfer_assets_using_type_and_then( + westend_runtime::RuntimeOrigin::signed(sender.clone()), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(MOONBEAM_PARA_ID)] + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(ONE_DOT * 10), + }]))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedAssetId::from(AssetId(Location::here()))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: beneficiary_key, + }], + ), + }]))), + WeightLimit::Unlimited, + ) + ); + + let balance_after = >::balance(&sender); + assert!( + balance_after < balance_before, + "Sender balance should decrease" + ); + }); + + moonbeam_execute_with(|| { + let beneficiary = moonbeam_runtime::AccountId::from(beneficiary_key); + let balance = moonbeam_runtime::EvmForeignAssets::balance(DOT_ASSET_ID, beneficiary) + .expect("balance query should succeed"); + assert!( + balance > U256::zero(), + "Beneficiary should have DOT on Moonbeam, got {balance}" + ); + }); +} + +// =========================================================================== +// Transfer: Moonbeam → Relay (UMP) +// =========================================================================== + +#[test] +fn transfer_dot_from_moonbeam_to_relay() { + setup_relay_to_moonbeam(); + + // First: send DOT from relay to Moonbeam so ALITH has some DOT. + WestendRelay::::execute_with(|| { + assert_ok!( + westend_runtime::XcmPallet::transfer_assets_using_type_and_then( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(MOONBEAM_PARA_ID)] + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(ONE_DOT * 100), + }]))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedAssetId::from(AssetId(Location::here()))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: ALITH + }], + ), + }]))), + WeightLimit::Unlimited, + ) + ); + }); + + let alith_dot_before = moonbeam_execute_with(|| { + moonbeam_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonbeam_runtime::AccountId::from(ALITH), + ) + .unwrap() + }); + assert!(alith_dot_before > U256::zero(), "ALITH should have DOT"); + + // Record relay-side balance of a relay account before the return transfer. + let relay_bob = sp_runtime::AccountId32::new([2u8; 32]); + let relay_bob_before = WestendRelay::::execute_with(|| { + >::balance(&relay_bob) + }); + + // Now send DOT back from Moonbeam to relay via PolkadotXcm. + // DOT's reserve is the relay, so we use DestinationReserve transfer type. + moonbeam_execute_with(|| { + let dot_location = Location::parent(); + let dest = Location::parent(); + let beneficiary = Location::new( + 0, + [AccountId32 { + network: None, + id: relay_bob.clone().into(), + }], + ); + let amount = ONE_DOT * 5; + + assert_ok!( + moonbeam_runtime::PolkadotXcm::transfer_assets_using_type_and_then( + moonbeam_runtime::RuntimeOrigin::signed(moonbeam_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(dest)), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(dot_location.clone()), + fun: Fungible(amount), + }]))), + Box::new(xcm_executor::traits::TransferType::DestinationReserve), + Box::new(xcm::VersionedAssetId::from(AssetId(dot_location))), + Box::new(xcm_executor::traits::TransferType::DestinationReserve), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary, + }]))), + WeightLimit::Unlimited, + ) + ); + }); + + // Verify relay account received DOT (minus fees). + let relay_bob_after = WestendRelay::::execute_with(|| { + >::balance(&relay_bob) + }); + assert!( + relay_bob_after > relay_bob_before, + "Relay Bob should have more DOT: before={relay_bob_before}, after={relay_bob_after}" + ); +} + +// =========================================================================== +// Fee behaviour: insufficient fees +// =========================================================================== + +#[test] +fn error_when_not_paying_enough_fees() { + setup_relay_to_moonbeam(); + + // Send a tiny amount (1 unit) from relay — should fail to pay Moonbeam execution fees. + WestendRelay::::execute_with(|| { + assert_ok!( + westend_runtime::XcmPallet::transfer_assets_using_type_and_then( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(MOONBEAM_PARA_ID)] + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(1), // way too little for fees + }]))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedAssetId::from(AssetId(Location::here()))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: ALITH + }], + ), + }]))), + WeightLimit::Unlimited, + ) + ); + }); + + // ALITH should NOT have received the token (execution failed). + moonbeam_execute_with(|| { + let balance = moonbeam_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonbeam_runtime::AccountId::from(ALITH), + ) + .unwrap(); + assert_eq!( + balance, + U256::zero(), + "Should not receive DOT when fees are insufficient" + ); + }); +} + +// =========================================================================== +// Fee behaviour: fees go to treasury +// =========================================================================== + +#[test] +fn fees_collected_by_treasury() { + setup_relay_to_moonbeam(); + + let treasury_dot_before = moonbeam_execute_with(|| { + let treasury = moonbeam_runtime::Treasury::account_id(); + moonbeam_runtime::EvmForeignAssets::balance(DOT_ASSET_ID, treasury).unwrap_or(U256::zero()) + }); + + // Send DOT from relay to Moonbeam (fees will be charged). + WestendRelay::::execute_with(|| { + assert_ok!( + westend_runtime::XcmPallet::transfer_assets_using_type_and_then( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(MOONBEAM_PARA_ID)] + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(ONE_DOT * 10), + }]))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedAssetId::from(AssetId(Location::here()))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: BALTATHAR + }], + ), + }]))), + WeightLimit::Unlimited, + ) + ); + }); + + moonbeam_execute_with(|| { + let treasury = moonbeam_runtime::Treasury::account_id(); + let treasury_dot_after = + moonbeam_runtime::EvmForeignAssets::balance(DOT_ASSET_ID, treasury) + .unwrap_or(U256::zero()); + assert!( + treasury_dot_after > treasury_dot_before, + "Treasury should collect fees: before={treasury_dot_before}, after={treasury_dot_after}" + ); + + // And beneficiary should have gotten the rest (not the full amount). + let beneficiary_balance = moonbeam_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonbeam_runtime::AccountId::from(BALTATHAR), + ) + .unwrap(); + assert!( + beneficiary_balance > U256::zero(), + "Beneficiary received DOT" + ); + assert!( + beneficiary_balance < U256::from(ONE_DOT * 10), + "Beneficiary received less than sent (fees deducted)" + ); + }); +} + +// =========================================================================== +// Account sufficiency: non-existent account receives foreign asset +// =========================================================================== + +#[test] +fn receive_asset_for_non_existent_account() { + setup_relay_to_moonbeam(); + + let fresh_account: [u8; 20] = [42u8; 20]; + + WestendRelay::::execute_with(|| { + assert_ok!( + westend_runtime::XcmPallet::transfer_assets_using_type_and_then( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(MOONBEAM_PARA_ID)] + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(ONE_DOT * 10), + }]))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedAssetId::from(AssetId(Location::here()))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: fresh_account, + }], + ), + }]))), + WeightLimit::Unlimited, + ) + ); + }); + + moonbeam_execute_with(|| { + let balance = moonbeam_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonbeam_runtime::AccountId::from(fresh_account), + ) + .unwrap(); + assert!( + balance > U256::zero(), + "Fresh (non-existent) account should receive DOT via XCM" + ); + }); +} + +// =========================================================================== +// Transfer: Para → Para via relay (XCMP/HRMP) +// =========================================================================== + +#[test] +fn transfer_dot_from_moonbeam_to_sibling() { + setup_with_sibling(); + + // First fund Moonbeam ALITH with DOT from relay. + WestendRelay::::execute_with(|| { + assert_ok!( + westend_runtime::XcmPallet::transfer_assets_using_type_and_then( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(MOONBEAM_PARA_ID)] + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(ONE_DOT * 100), + }]))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedAssetId::from(AssetId(Location::here()))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: ALITH + }], + ), + }]))), + WeightLimit::Unlimited, + ) + ); + }); + + // Verify ALITH got DOT on Moonbeam. + let alith_dot = moonbeam_execute_with(|| { + moonbeam_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonbeam_runtime::AccountId::from(ALITH), + ) + .unwrap() + }); + assert!(alith_dot > U256::zero(), "ALITH should have DOT"); + + // Now send DOT from Moonbeam to Sibling via reserve transfer through relay. + // DOT's reserve is the relay (parent), so we use RemoteReserve. + // The custom_xcm_on_dest must include BuyExecution since the sibling's + // barrier requires paid execution. + moonbeam_execute_with(|| { + let dest = Location::new(1, [Parachain(SIBLING_PARA_ID)]); + let beneficiary = Location::new( + 0, + [AccountKey20 { + network: None, + key: BALTATHAR, + }], + ); + let dot_location = Location::parent(); + // Send a large amount so enough survives relay fees for the sibling. + let amount = ONE_DOT * 50; + + assert_ok!( + moonbeam_runtime::PolkadotXcm::transfer_assets_using_type_and_then( + moonbeam_runtime::RuntimeOrigin::signed(moonbeam_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(dest)), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(dot_location.clone()), + fun: Fungible(amount), + }]))), + Box::new(xcm_executor::traits::TransferType::RemoteReserve( + xcm::VersionedLocation::from(Location::parent()) + )), + Box::new(xcm::VersionedAssetId::from(AssetId(dot_location.clone()))), + Box::new(xcm_executor::traits::TransferType::RemoteReserve( + xcm::VersionedLocation::from(Location::parent()) + )), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![ + BuyExecution { + // Use a small fee amount that will definitely be in holding + // after the relay takes its share. + fees: Asset { + id: AssetId(dot_location), + fun: Fungible(ONE_DOT / 10), + }, + weight_limit: WeightLimit::Unlimited, + }, + DepositAsset { + assets: Wild(All), + beneficiary, + }, + ]))), + WeightLimit::Unlimited, + ) + ); + }); + + // Trigger message routing on the relay so the DMP is delivered to sibling. + WestendRelay::::execute_with(|| {}); + + // Verify BALTATHAR received DOT on the sibling. + sibling_execute_with(|| { + let balance = moonbeam_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonbeam_runtime::AccountId::from(BALTATHAR), + ) + .unwrap(); + assert!( + balance > U256::zero(), + "BALTATHAR should have DOT on sibling, got {balance}" + ); + }); +} + +// =========================================================================== +// EVM account with native balance receives foreign assets +// =========================================================================== + +#[test] +fn evm_account_receives_foreign_asset() { + setup_relay_to_moonbeam(); + + // ALITH has GLMR from genesis. Send DOT and verify both balances coexist. + WestendRelay::::execute_with(|| { + assert_ok!( + westend_runtime::XcmPallet::transfer_assets_using_type_and_then( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(MOONBEAM_PARA_ID)], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(ONE_DOT * 10), + }]))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedAssetId::from(AssetId(Location::here()))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: ALITH + }], + ), + }]))), + WeightLimit::Unlimited, + ) + ); + }); + + moonbeam_execute_with(|| { + // ALITH should have both native GLMR and foreign DOT. + let glmr = >::balance( + &moonbeam_runtime::AccountId::from(ALITH), + ); + assert!(glmr > 0, "ALITH should still have GLMR"); + + let dot = moonbeam_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonbeam_runtime::AccountId::from(ALITH), + ) + .unwrap(); + assert!(dot > U256::zero(), "ALITH should also have DOT"); + }); +} + +// =========================================================================== +// Foreign assets survive native balance drainage +// =========================================================================== + +#[test] +fn foreign_assets_survive_native_balance_drain() { + setup_relay_to_moonbeam(); + + let test_account: [u8; 20] = [77u8; 20]; + + // Give the test account some GLMR. + moonbeam_execute_with(|| { + >::mint_into( + &moonbeam_runtime::AccountId::from(test_account), + moonbeam_runtime::currency::GLMR, + ) + .expect("Should mint GLMR"); + }); + + // Send DOT to the test account. + WestendRelay::::execute_with(|| { + assert_ok!( + westend_runtime::XcmPallet::transfer_assets_using_type_and_then( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(MOONBEAM_PARA_ID)], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(ONE_DOT * 10), + }]))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedAssetId::from(AssetId(Location::here()))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: test_account + }], + ), + }]))), + WeightLimit::Unlimited, + ) + ); + }); + + // Drain all GLMR, then verify foreign asset is still accessible. + moonbeam_execute_with(|| { + let balance = >::balance( + &moonbeam_runtime::AccountId::from(test_account), + ); + let _ = >::burn_from( + &moonbeam_runtime::AccountId::from(test_account), + balance, + frame_support::traits::tokens::Preservation::Expendable, + frame_support::traits::tokens::Precision::BestEffort, + frame_support::traits::tokens::Fortitude::Force, + ); + + let remaining = >::balance( + &moonbeam_runtime::AccountId::from(test_account), + ); + assert_eq!(remaining, 0, "Native balance should be zero after drain"); + + // Foreign asset balance should still be accessible. + let dot = moonbeam_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonbeam_runtime::AccountId::from(test_account), + ) + .unwrap(); + assert!( + dot > U256::zero(), + "Foreign asset should survive native balance drain" + ); + }); +} + +// =========================================================================== +// Native asset (GLMR) para → para transfers +// =========================================================================== + +/// Register Moonbeam's native GLMR as a foreign asset on the sibling and +/// configure the XCM weight trader price. +fn register_glmr_on_sibling() { + sibling_execute_with(|| { + // From the sibling's perspective, Moonbeam's native token lives at: + // ../Parachain(2004)/PalletInstance(10) (pallet_balances = index 10) + let glmr_location = + xcm::latest::Location::new(1, [Parachain(MOONBEAM_PARA_ID), PalletInstance(10u8)]); + + frame_support::assert_ok!(moonbeam_runtime::EvmForeignAssets::create_foreign_asset( + moonbeam_runtime::RuntimeOrigin::root(), + GLMR_ASSET_ID, + glmr_location.clone(), + 18, // GLMR has 18 decimals + b"GLMR".to_vec().try_into().unwrap(), + b"Glimmer".to_vec().try_into().unwrap(), + )); + + frame_support::assert_ok!(moonbeam_runtime::XcmWeightTrader::add_asset( + moonbeam_runtime::RuntimeOrigin::root(), + glmr_location, + 10_000_000_000_000_000_000_000_000_000u128, // 10^28 (generous relative price) + )); + }); +} + +/// Setup for GLMR para→para transfers: open HRMP, register DOT on Moonbeam, +/// register GLMR on sibling. +fn setup_glmr_para_to_para() { + setup_with_sibling(); + register_glmr_on_sibling(); +} + +/// Transfer GLMR from Moonbeam to Sibling (reserve-backed). +#[test] +fn transfer_glmr_from_moonbeam_to_sibling() { + setup_glmr_para_to_para(); + + let alith_before = moonbeam_execute_with(|| { + >::balance(&moonbeam_runtime::AccountId::from( + ALITH, + )) + }); + + let amount = moonbeam_runtime::currency::GLMR; // 1 GLMR + + moonbeam_execute_with(|| { + assert_ok!(moonbeam_runtime::PolkadotXcm::transfer_assets( + moonbeam_runtime::RuntimeOrigin::signed(moonbeam_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(SIBLING_PARA_ID)], + ))), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [AccountKey20 { + network: None, + key: BALTATHAR + }], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::new(0, [PalletInstance(10)])), + fun: Fungible(amount), + }]))), + 0, + WeightLimit::Unlimited, + )); + }); + + // ALITH should have less GLMR after the transfer. + let alith_after = moonbeam_execute_with(|| { + >::balance(&moonbeam_runtime::AccountId::from( + ALITH, + )) + }); + assert!( + alith_after < alith_before, + "ALITH should have less GLMR after transfer" + ); + assert!( + alith_before - alith_after >= amount, + "ALITH should have spent at least {amount}" + ); + + // BALTATHAR should have GLMR on sibling (as foreign asset). + sibling_execute_with(|| { + let balance = moonbeam_runtime::EvmForeignAssets::balance( + GLMR_ASSET_ID, + moonbeam_runtime::AccountId::from(BALTATHAR), + ) + .unwrap(); + assert!( + balance > U256::zero(), + "BALTATHAR should have GLMR on sibling" + ); + }); +} + +/// Roundtrip: GLMR from Moonbeam → Sibling → back to Moonbeam. +#[test] +fn transfer_glmr_roundtrip_moonbeam_sibling() { + setup_glmr_para_to_para(); + + let alith_initial = moonbeam_execute_with(|| { + >::balance(&moonbeam_runtime::AccountId::from( + ALITH, + )) + }); + + let amount = moonbeam_runtime::currency::GLMR; // 1 GLMR + + // Step 1: Send GLMR from Moonbeam to Sibling (BALTATHAR). + moonbeam_execute_with(|| { + assert_ok!(moonbeam_runtime::PolkadotXcm::transfer_assets( + moonbeam_runtime::RuntimeOrigin::signed(moonbeam_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(SIBLING_PARA_ID)], + ))), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [AccountKey20 { + network: None, + key: BALTATHAR + }], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::new(0, [PalletInstance(10)])), + fun: Fungible(amount), + }]))), + 0, + WeightLimit::Unlimited, + )); + }); + + // Verify BALTATHAR got GLMR on sibling. + let glmr_on_sibling = sibling_execute_with(|| { + moonbeam_runtime::EvmForeignAssets::balance( + GLMR_ASSET_ID, + moonbeam_runtime::AccountId::from(BALTATHAR), + ) + .unwrap() + }); + assert!( + glmr_on_sibling > U256::zero(), + "BALTATHAR should have GLMR on sibling: {glmr_on_sibling}" + ); + + // Step 2: Send GLMR back from Sibling to Moonbeam (ALITH). + // From the sibling's perspective, GLMR is at ../Parachain(2004)/PalletInstance(10). + sibling_execute_with(|| { + let glmr_location = Location::new(1, [Parachain(MOONBEAM_PARA_ID), PalletInstance(10)]); + + assert_ok!(moonbeam_runtime::PolkadotXcm::transfer_assets( + moonbeam_runtime::RuntimeOrigin::signed(moonbeam_runtime::AccountId::from(BALTATHAR),), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(MOONBEAM_PARA_ID)], + ))), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [AccountKey20 { + network: None, + key: ALITH + }], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(glmr_location), + fun: Fungible(glmr_on_sibling.try_into().unwrap()), + }]))), + 0, + WeightLimit::Unlimited, + )); + }); + + // ALITH should have recovered most of the GLMR (minus fees on both hops). + let alith_final = moonbeam_execute_with(|| { + >::balance(&moonbeam_runtime::AccountId::from( + ALITH, + )) + }); + // After roundtrip, ALITH loses some to fees but should still have most. + let total_lost = alith_initial.saturating_sub(alith_final); + assert!( + total_lost < amount, + "Roundtrip should only lose fees, not the full amount: lost={total_lost}, sent={amount}" + ); +} + +/// GLMR transfer with trader: fees are deducted from GLMR on the sibling. +#[test] +fn transfer_glmr_to_sibling_with_trader_fees() { + setup_glmr_para_to_para(); + + let amount = moonbeam_runtime::currency::GLMR * 100; // 100 GLMR + + moonbeam_execute_with(|| { + assert_ok!(moonbeam_runtime::PolkadotXcm::transfer_assets( + moonbeam_runtime::RuntimeOrigin::signed(moonbeam_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(SIBLING_PARA_ID)], + ))), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [AccountKey20 { + network: None, + key: BALTATHAR + }], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::new(0, [PalletInstance(10)])), + fun: Fungible(amount), + }]))), + 0, + WeightLimit::Unlimited, + )); + }); + + sibling_execute_with(|| { + let received = moonbeam_runtime::EvmForeignAssets::balance( + GLMR_ASSET_ID, + moonbeam_runtime::AccountId::from(BALTATHAR), + ) + .unwrap(); + + // BALTATHAR should receive less than the full amount (fees deducted). + assert!( + received > U256::zero() && received < U256::from(amount), + "Should receive less than full amount due to fees: received={received}, sent={amount}" + ); + + // Treasury should have received some GLMR as fees. + let treasury = moonbeam_runtime::Treasury::account_id(); + let treasury_fee = + moonbeam_runtime::EvmForeignAssets::balance(GLMR_ASSET_ID, treasury).unwrap(); + assert!( + treasury_fee > U256::zero(), + "Treasury should have collected GLMR fees" + ); + }); +} + +// =========================================================================== +// DOT transfers via RemoteReserve (relay as reserve) +// =========================================================================== + +/// Fund ALITH with DOT via relay DMP. +fn fund_moonbeam_alith_with_dot(amount: u128) { + WestendRelay::::execute_with(|| { + let beneficiary = Location::new( + 0, + [AccountKey20 { + network: None, + key: ALITH, + }], + ); + let assets: xcm::VersionedAssets = (Location::here(), amount).into(); + let fees_id: xcm::VersionedAssetId = AssetId(Location::here()).into(); + let xcm_on_dest = Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary, + }]); + + assert_ok!( + westend_runtime::XcmPallet::transfer_assets_using_type_and_then( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE.clone()), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(MOONBEAM_PARA_ID)] + ))), + Box::new(assets), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(fees_id), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedXcm::V5(xcm_on_dest)), + WeightLimit::Unlimited, + ) + ); + }); +} + +/// Send DOT from Moonbeam to a sibling using `RemoteReserve` through the +/// relay. DOT's reserve is the relay (parent), so a direct +/// `DestinationReserve` is invalid — the relay must mediate. +#[test] +fn transfer_dot_to_sibling_via_remote_reserve() { + setup_with_sibling(); + + let send_amount = ONE_DOT * 100; + fund_moonbeam_alith_with_dot(send_amount); + + let alith_dot_before = moonbeam_execute_with(|| { + moonbeam_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonbeam_runtime::AccountId::from(ALITH), + ) + .unwrap_or_default() + }); + assert!( + alith_dot_before > U256::zero(), + "ALITH should have DOT before transfer" + ); + + let transfer = ONE_DOT * 50; + + moonbeam_execute_with(|| { + let dot_location = Location::parent(); + + assert_ok!( + moonbeam_runtime::PolkadotXcm::transfer_assets_using_type_and_then( + moonbeam_runtime::RuntimeOrigin::signed(moonbeam_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(SIBLING_PARA_ID)], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(dot_location.clone()), + fun: Fungible(transfer), + }]))), + Box::new(xcm_executor::traits::TransferType::RemoteReserve( + xcm::VersionedLocation::from(Location::parent()), + )), + Box::new(xcm::VersionedAssetId::from(AssetId(dot_location.clone()))), + Box::new(xcm_executor::traits::TransferType::RemoteReserve( + xcm::VersionedLocation::from(Location::parent()), + )), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![ + BuyExecution { + fees: Asset { + id: AssetId(dot_location), + fun: Fungible(ONE_DOT / 10), + }, + weight_limit: WeightLimit::Unlimited, + }, + DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: BALTATHAR, + }], + ), + }, + ]))), + WeightLimit::Unlimited, + ) + ); + }); + + WestendRelay::::execute_with(|| {}); + + let alith_dot_after = moonbeam_execute_with(|| { + moonbeam_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonbeam_runtime::AccountId::from(ALITH), + ) + .unwrap_or_default() + }); + assert!( + alith_dot_after < alith_dot_before, + "ALITH DOT should decrease after transfer" + ); + + let baltathar_dot = sibling_execute_with(|| { + moonbeam_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonbeam_runtime::AccountId::from(BALTATHAR), + ) + .unwrap_or_default() + }); + assert!( + baltathar_dot > U256::zero(), + "BALTATHAR should have DOT on sibling (got {baltathar_dot})" + ); +} + +/// Roundtrip: DOT from Moonbeam → Sibling → back to Moonbeam, both legs +/// using RemoteReserve through the relay. +#[test] +fn transfer_dot_roundtrip_via_remote_reserve() { + setup_with_sibling(); + + let send_amount = ONE_DOT * 100; + fund_moonbeam_alith_with_dot(send_amount); + + let outbound = ONE_DOT * 50; + let dot_location = Location::parent(); + + // ── Moonbeam → Sibling ──────────────────────────────────────────────── + moonbeam_execute_with(|| { + assert_ok!( + moonbeam_runtime::PolkadotXcm::transfer_assets_using_type_and_then( + moonbeam_runtime::RuntimeOrigin::signed(moonbeam_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(SIBLING_PARA_ID)], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(dot_location.clone()), + fun: Fungible(outbound), + }]))), + Box::new(xcm_executor::traits::TransferType::RemoteReserve( + xcm::VersionedLocation::from(Location::parent()), + )), + Box::new(xcm::VersionedAssetId::from(AssetId(dot_location.clone()))), + Box::new(xcm_executor::traits::TransferType::RemoteReserve( + xcm::VersionedLocation::from(Location::parent()), + )), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![ + BuyExecution { + fees: Asset { + id: AssetId(dot_location.clone()), + fun: Fungible(ONE_DOT / 10), + }, + weight_limit: WeightLimit::Unlimited, + }, + DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: BALTATHAR, + }], + ), + }, + ]))), + WeightLimit::Unlimited, + ) + ); + }); + + WestendRelay::::execute_with(|| {}); + + let baltathar_dot = sibling_execute_with(|| { + moonbeam_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonbeam_runtime::AccountId::from(BALTATHAR), + ) + .unwrap_or_default() + }); + assert!(baltathar_dot > U256::zero(), "Sibling should have DOT"); + + // ── Sibling → Moonbeam ──────────────────────────────────────────────── + let return_amount_raw: u128 = baltathar_dot.try_into().unwrap(); + let return_half = return_amount_raw / 2; + + sibling_execute_with(|| { + assert_ok!( + moonbeam_runtime::PolkadotXcm::transfer_assets_using_type_and_then( + moonbeam_runtime::RuntimeOrigin::signed(moonbeam_runtime::AccountId::from( + BALTATHAR, + )), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(MOONBEAM_PARA_ID)], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(dot_location.clone()), + fun: Fungible(return_half), + }]))), + Box::new(xcm_executor::traits::TransferType::RemoteReserve( + xcm::VersionedLocation::from(Location::parent()), + )), + Box::new(xcm::VersionedAssetId::from(AssetId(dot_location.clone()))), + Box::new(xcm_executor::traits::TransferType::RemoteReserve( + xcm::VersionedLocation::from(Location::parent()), + )), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![ + BuyExecution { + fees: Asset { + id: AssetId(dot_location), + fun: Fungible(ONE_DOT / 10), + }, + weight_limit: WeightLimit::Unlimited, + }, + DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: ALITH, + }], + ), + }, + ]))), + WeightLimit::Unlimited, + ) + ); + }); + + WestendRelay::::execute_with(|| {}); + + let alith_dot_final = moonbeam_execute_with(|| { + moonbeam_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonbeam_runtime::AccountId::from(ALITH), + ) + .unwrap_or_default() + }); + assert!( + alith_dot_final > U256::from(send_amount - outbound), + "ALITH should have more DOT than after the outbound leg (got {alith_dot_final})" + ); +} + +/// Transfer GLMR to a sibling as a self-reserve asset (GLMR pays its own +/// fees). Exercises `transfer_assets` with a single asset where the fee +/// asset and the transfer asset are the same. +#[test] +fn transfer_glmr_self_reserve_to_sibling() { + setup_with_sibling(); + register_glmr_on_sibling(); + + let glmr_amount = moonbeam_runtime::currency::GLMR; + + moonbeam_execute_with(|| { + assert_ok!(moonbeam_runtime::PolkadotXcm::transfer_assets( + moonbeam_runtime::RuntimeOrigin::signed(moonbeam_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(SIBLING_PARA_ID)], + ))), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [AccountKey20 { + network: None, + key: BALTATHAR, + }], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::new(0, [PalletInstance(10)])), + fun: Fungible(glmr_amount), + }]))), + 0, + WeightLimit::Unlimited, + )); + }); + + let bal_glmr = sibling_execute_with(|| { + moonbeam_runtime::EvmForeignAssets::balance( + GLMR_ASSET_ID, + moonbeam_runtime::AccountId::from(BALTATHAR), + ) + .unwrap_or_default() + }); + assert!( + bal_glmr > U256::zero(), + "BALTATHAR should have received GLMR on sibling (got {bal_glmr})" + ); +} + +/// Receive a sibling-native foreign asset on Moonbeam. +/// A sibling sends its own native token (another Moonbeam instance's GLMR) +/// to Moonbeam, which receives it as an EVM foreign asset. +#[test] +fn receive_sibling_native_asset() { + setup_with_sibling(); + + // On Moonbeam, register the sibling's GLMR (PalletInstance(10) on para 2005) + // as a foreign asset with id=3. + const SIBLING_GLMR_ASSET_ID: u128 = 3; + moonbeam_execute_with(|| { + let sibling_glmr_location = + xcm::latest::Location::new(1, [Parachain(SIBLING_PARA_ID), PalletInstance(10u8)]); + + frame_support::assert_ok!(moonbeam_runtime::EvmForeignAssets::create_foreign_asset( + moonbeam_runtime::RuntimeOrigin::root(), + SIBLING_GLMR_ASSET_ID, + sibling_glmr_location.clone(), + 18, + b"sGLMR".to_vec().try_into().unwrap(), + b"Sibling Glimmer".to_vec().try_into().unwrap(), + )); + + frame_support::assert_ok!(moonbeam_runtime::XcmWeightTrader::add_asset( + moonbeam_runtime::RuntimeOrigin::root(), + sibling_glmr_location, + 10_000_000_000_000_000_000_000_000_000u128, + )); + }); + + let amount = moonbeam_runtime::currency::GLMR; + + sibling_execute_with(|| { + assert_ok!(moonbeam_runtime::PolkadotXcm::transfer_assets( + moonbeam_runtime::RuntimeOrigin::signed(moonbeam_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(MOONBEAM_PARA_ID)], + ))), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [AccountKey20 { + network: None, + key: BALTATHAR, + }], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::new(0, [PalletInstance(10)])), + fun: Fungible(amount), + }]))), + 0, + WeightLimit::Unlimited, + )); + }); + + let bal = moonbeam_execute_with(|| { + moonbeam_runtime::EvmForeignAssets::balance( + SIBLING_GLMR_ASSET_ID, + moonbeam_runtime::AccountId::from(BALTATHAR), + ) + .unwrap_or_default() + }); + assert!( + bal > U256::zero(), + "BALTATHAR should have sibling GLMR on Moonbeam (got {bal})" + ); +} diff --git a/runtime/moonbeam/tests/xcm_emulator/versioning.rs b/runtime/moonbeam/tests/xcm_emulator/versioning.rs new file mode 100644 index 00000000000..0e31e3e19c0 --- /dev/null +++ b/runtime/moonbeam/tests/xcm_emulator/versioning.rs @@ -0,0 +1,207 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! XCM version discovery / negotiation tests. +//! +//! Verifies that `SafeXcmVersion` is configured from genesis and that +//! Moonbeam discovers the XCM version of remote chains (relay and siblings) +//! after the first cross-chain interaction. +//! +//! Full runtime-upgrade version negotiation (as in the legacy mock tests) +//! is not feasible with xcm-emulator because there is no mock version +//! switcher. These tests cover the subset that works with the real runtime. + +use crate::network::*; +use frame_support::assert_ok; +use xcm::latest::prelude::*; +use xcm_emulator::TestExt; + +// =========================================================================== +// Helpers +// =========================================================================== + +/// Register Moonbeam GLMR as foreign asset on the current chain context. +fn register_glmr_foreign_asset(source_para_id: u32) { + let glmr_location = + xcm::latest::Location::new(1, [Parachain(source_para_id), PalletInstance(10u8)]); + + frame_support::assert_ok!(moonbeam_runtime::EvmForeignAssets::create_foreign_asset( + moonbeam_runtime::RuntimeOrigin::root(), + GLMR_ASSET_ID, + glmr_location.clone(), + 18, + b"GLMR".to_vec().try_into().unwrap(), + b"Glimmer".to_vec().try_into().unwrap(), + )); + + frame_support::assert_ok!(moonbeam_runtime::XcmWeightTrader::add_asset( + moonbeam_runtime::RuntimeOrigin::root(), + glmr_location, + 10_000_000_000_000_000_000_000_000_000u128, + )); +} + +// =========================================================================== +// Tests +// =========================================================================== + +/// Verify that Moonbeam subscribes to the relay's XCM version on first +/// interaction. After a DMP transfer the relay should know Moonbeam's +/// supported XCM version. +#[test] +fn xcm_version_discovery_with_relay() { + init_network(); + + moonbeam_execute_with(|| { + register_dot_asset(DOT_ASSET_ID); + }); + + // Send DOT from relay to Moonbeam to trigger version discovery. + WestendRelay::::execute_with(|| { + let beneficiary = Location::new( + 0, + [AccountKey20 { + network: None, + key: ALITH, + }], + ); + let assets: xcm::VersionedAssets = (Location::here(), ONE_DOT * 5).into(); + let fees_id: xcm::VersionedAssetId = AssetId(Location::here()).into(); + let xcm_on_dest = Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary, + }]); + + assert_ok!( + westend_runtime::XcmPallet::transfer_assets_using_type_and_then( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE.clone()), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(MOONBEAM_PARA_ID)] + ))), + Box::new(assets), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(fees_id), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedXcm::V5(xcm_on_dest)), + WeightLimit::Unlimited, + ) + ); + }); + + // Verify the relay can version-wrap an XCM destined for Moonbeam. + // query_delivery_fees calls validate_send → ChildParachainRouter::validate → + // wrap_version, which requires SupportedVersion or SafeXcmVersion to be set. + WestendRelay::::execute_with(|| { + use xcm_runtime_apis::fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV2; + let fees = westend_runtime::Runtime::query_delivery_fees( + xcm::VersionedLocation::from(Location::new(0, [Parachain(MOONBEAM_PARA_ID)])), + xcm::VersionedXcm::from(Xcm::<()>(vec![ClearOrigin])), + xcm::VersionedAssetId::from(AssetId(Location::here())), + ); + assert!( + fees.is_ok(), + "Relay should resolve XCM version for Moonbeam destination" + ); + }); + + // Verify Moonbeam can version-wrap an XCM destined for the relay. + // SafeXcmVersion is set from genesis, which wrap_version uses as fallback. + moonbeam_execute_with(|| { + use xcm_runtime_apis::fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV2; + let fees = moonbeam_runtime::Runtime::query_delivery_fees( + xcm::VersionedLocation::from(Location::parent()), + xcm::VersionedXcm::from(Xcm::<()>(vec![ClearOrigin])), + xcm::VersionedAssetId::from(AssetId(Location::here())), + ); + assert!( + fees.is_ok(), + "Moonbeam should resolve XCM version for relay destination" + ); + }); +} + +/// Verify that Moonbeam and a sibling negotiate XCM versions via HRMP. +#[test] +fn xcm_version_discovery_with_sibling() { + init_network(); + + moonbeam_execute_with(|| register_dot_asset(DOT_ASSET_ID)); + sibling_execute_with(|| register_dot_asset(DOT_ASSET_ID)); + + WestendRelay::::execute_with(|| { + open_hrmp_channels(MOONBEAM_PARA_ID, SIBLING_PARA_ID); + }); + + // Register GLMR on sibling so we can do a transfer. + sibling_execute_with(|| register_glmr_foreign_asset(MOONBEAM_PARA_ID)); + + let amount = moonbeam_runtime::currency::GLMR; + + // Transfer triggers version negotiation between the two parachains. + moonbeam_execute_with(|| { + assert_ok!(moonbeam_runtime::PolkadotXcm::transfer_assets( + moonbeam_runtime::RuntimeOrigin::signed(moonbeam_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(SIBLING_PARA_ID)], + ))), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [AccountKey20 { + network: None, + key: BALTATHAR, + }], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::new(0, [PalletInstance(10)])), + fun: Fungible(amount), + }]))), + 0, + WeightLimit::Unlimited, + )); + }); + + // Verify sibling can version-wrap an XCM destined for Moonbeam. + // query_delivery_fees calls validate_send → XcmpQueue::validate → + // wrap_version, which requires SupportedVersion or SafeXcmVersion. + sibling_execute_with(|| { + use xcm_runtime_apis::fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV2; + let fees = moonbeam_runtime::Runtime::query_delivery_fees( + xcm::VersionedLocation::from(Location::new(1, [Parachain(MOONBEAM_PARA_ID)])), + xcm::VersionedXcm::from(Xcm::<()>(vec![ClearOrigin])), + xcm::VersionedAssetId::from(AssetId(Location::here())), + ); + assert!( + fees.is_ok(), + "Sibling should resolve XCM version for Moonbeam destination" + ); + }); + + // Verify Moonbeam can version-wrap an XCM destined for the sibling. + moonbeam_execute_with(|| { + use xcm_runtime_apis::fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV2; + let fees = moonbeam_runtime::Runtime::query_delivery_fees( + xcm::VersionedLocation::from(Location::new(1, [Parachain(SIBLING_PARA_ID)])), + xcm::VersionedXcm::from(Xcm::<()>(vec![ClearOrigin])), + xcm::VersionedAssetId::from(AssetId(Location::here())), + ); + assert!( + fees.is_ok(), + "Moonbeam should resolve XCM version for sibling destination" + ); + }); +} diff --git a/runtime/moonbeam/tests/xcm_mock/mod.rs b/runtime/moonbeam/tests/xcm_mock/mod.rs deleted file mode 100644 index 3c911c63ca7..00000000000 --- a/runtime/moonbeam/tests/xcm_mock/mod.rs +++ /dev/null @@ -1,286 +0,0 @@ -// Copyright 2021 Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -pub mod parachain; -pub mod relay_chain; -pub mod statemint_like; - -use cumulus_primitives_core::ParaId; -use pallet_xcm_transactor::relay_indices::*; -use sp_runtime::traits::AccountIdConversion; -use sp_runtime::{AccountId32, BuildStorage}; -use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain, TestExt}; - -use polkadot_runtime_parachains::configuration::{ - GenesisConfig as ConfigurationGenesisConfig, HostConfiguration, -}; -use polkadot_runtime_parachains::paras::{ - GenesisConfig as ParasGenesisConfig, ParaGenesisArgs, ParaKind, -}; -use sp_core::{H160, U256}; -use std::{collections::BTreeMap, str::FromStr}; - -pub const PARAALICE: [u8; 20] = [1u8; 20]; -pub const PARABOB: [u8; 20] = [2u8; 20]; -pub const RELAYALICE: AccountId32 = AccountId32::new([0u8; 32]); -pub const RELAYBOB: AccountId32 = AccountId32::new([2u8; 32]); - -pub fn para_a_account() -> AccountId32 { - ParaId::from(1).into_account_truncating() -} - -pub fn para_b_account() -> AccountId32 { - ParaId::from(2).into_account_truncating() -} - -pub fn para_a_account_20() -> parachain::AccountId { - ParaId::from(1).into_account_truncating() -} - -pub fn evm_account() -> H160 { - H160::from_str("1000000000000000000000000000000000000001").unwrap() -} - -pub fn mock_para_genesis_info() -> ParaGenesisArgs { - ParaGenesisArgs { - genesis_head: vec![1u8].into(), - validation_code: vec![1u8].into(), - para_kind: ParaKind::Parachain, - } -} - -pub fn mock_relay_config() -> HostConfiguration { - HostConfiguration:: { - hrmp_channel_max_capacity: u32::MAX, - hrmp_channel_max_total_size: u32::MAX, - hrmp_max_parachain_inbound_channels: 10, - hrmp_max_parachain_outbound_channels: 10, - hrmp_channel_max_message_size: u32::MAX, - // Changed to avoid arithmetic errors within hrmp_close - max_downward_message_size: 100_000u32, - ..Default::default() - } -} - -pub fn mock_xcm_transactor_storage() -> RelayChainIndices { - RelayChainIndices { - staking: 0u8, - utility: 5u8, - hrmp: 6u8, - bond: 0u8, - bond_extra: 1u8, - unbond: 2u8, - withdraw_unbonded: 3u8, - validate: 4u8, - nominate: 5u8, - chill: 6u8, - set_payee: 7u8, - set_controller: 8u8, - rebond: 19u8, - as_derivative: 1u8, - init_open_channel: 0u8, - accept_open_channel: 1u8, - close_channel: 2u8, - cancel_open_request: 6u8, - } -} - -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_parachain! { - pub struct Statemint { - Runtime = statemint_like::Runtime, - XcmpMessageHandler = statemint_like::MsgQueue, - DmpMessageHandler = statemint_like::MsgQueue, - new_ext = statemint_ext(1000), - } -} - -decl_test_relay_chain! { - pub struct Relay { - Runtime = relay_chain::Runtime, - RuntimeCall = relay_chain::RuntimeCall, - RuntimeEvent = relay_chain::RuntimeEvent, - XcmConfig = relay_chain::XcmConfig, - MessageQueue = relay_chain::MessageQueue, - System = relay_chain::System, - new_ext = relay_ext(vec![1, 2, 3, 1000]), - } -} - -decl_test_network! { - pub struct MockNet { - relay_chain = Relay, - parachains = vec![ - (1, ParaA), - (2, ParaB), - (3, ParaC), - (1000, Statemint), - ], - } -} - -pub const INITIAL_BALANCE: u128 = 10_000_000_000_000_000; - -pub const INITIAL_EVM_BALANCE: u128 = 0; -pub const INITIAL_EVM_NONCE: u32 = 1; - -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![(PARAALICE.into(), INITIAL_BALANCE)], - dev_accounts: None, - } - .assimilate_storage(&mut t) - .unwrap(); - - pallet_xcm_transactor::GenesisConfig:: { - relay_indices: mock_xcm_transactor_storage(), - ..Default::default() - } - .assimilate_storage(&mut t) - .unwrap(); - - // EVM accounts are self-sufficient. - let mut evm_accounts = BTreeMap::new(); - evm_accounts.insert( - evm_account(), - fp_evm::GenesisAccount { - nonce: U256::from(INITIAL_EVM_NONCE), - balance: U256::from(INITIAL_EVM_BALANCE), - storage: Default::default(), - code: vec![ - 0x00, // STOP - ], - }, - ); - - let genesis_config = pallet_evm::GenesisConfig:: { - accounts: evm_accounts, - ..Default::default() - }; - genesis_config.assimilate_storage(&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 statemint_ext(para_id: u32) -> sp_io::TestExternalities { - use statemint_like::{MsgQueue, Runtime, System}; - - let mut t = frame_system::GenesisConfig::::default() - .build_storage() - .unwrap(); - - pallet_balances::GenesisConfig:: { - balances: vec![ - (RELAYALICE.into(), INITIAL_BALANCE), - (RELAYBOB.into(), INITIAL_BALANCE), - ], - dev_accounts: None, - } - .assimilate_storage(&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(paras: Vec) -> sp_io::TestExternalities { - use relay_chain::{Runtime, System}; - - let mut t = frame_system::GenesisConfig::::default() - .build_storage() - .unwrap(); - - pallet_balances::GenesisConfig:: { - balances: vec![(RELAYALICE, INITIAL_BALANCE)], - dev_accounts: None, - } - .assimilate_storage(&mut t) - .unwrap(); - - let para_genesis: Vec<(ParaId, ParaGenesisArgs)> = paras - .iter() - .map(|¶_id| (para_id.into(), mock_para_genesis_info())) - .collect(); - - let genesis_config = ConfigurationGenesisConfig:: { - config: mock_relay_config(), - }; - genesis_config.assimilate_storage(&mut t).unwrap(); - - let genesis_config = ParasGenesisConfig:: { - paras: para_genesis, - ..Default::default() - }; - genesis_config.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 Hrmp = polkadot_runtime_parachains::hrmp::Pallet; - -pub type StatemintBalances = pallet_balances::Pallet; -pub type StatemintChainPalletXcm = pallet_xcm::Pallet; -pub type StatemintAssets = pallet_assets::Pallet; - -pub type RelayBalances = pallet_balances::Pallet; -pub type ParaBalances = pallet_balances::Pallet; -pub type XcmTransactor = pallet_xcm_transactor::Pallet; diff --git a/runtime/moonbeam/tests/xcm_mock/parachain.rs b/runtime/moonbeam/tests/xcm_mock/parachain.rs deleted file mode 100644 index 9d60c681c9e..00000000000 --- a/runtime/moonbeam/tests/xcm_mock/parachain.rs +++ /dev/null @@ -1,1033 +0,0 @@ -// Copyright 2021 Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -//! Parachain runtime mock. - -use frame_support::{ - construct_runtime, ensure, parameter_types, - traits::{ - fungible::NativeOrWithId, ConstU32, EitherOf, Everything, Get, InstanceFilter, Nothing, - PalletInfoAccess, - }, - weights::Weight, - PalletId, -}; -pub use moonbeam_runtime::xcm_config::AssetType; - -use frame_system::{pallet_prelude::BlockNumberFor, EnsureRoot}; -use moonbeam_runtime_common::{ - impl_asset_conversion::AssetRateConverter, impl_multiasset_paymaster::MultiAssetPaymaster, - xcm_origins::AllowSiblingParachains, -}; -use pallet_moonbeam_foreign_assets::{MapSuccessToGovernance, MapSuccessToXcm}; -use pallet_xcm::{migration::v1::VersionUncheckedMigrateToV1, EnsureXcm}; -use parity_scale_codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen}; -use sp_core::{H160, H256}; -use sp_runtime::{ - traits::{BlakeTwo256, Hash, IdentityLookup, MaybeEquivalence, Zero}, - Permill, -}; -use sp_std::{ - convert::{From, Into, TryFrom}, - prelude::*, -}; -use xcm::{latest::prelude::*, Version as XcmVersion, VersionedXcm}; - -use cumulus_primitives_core::relay_chain::HrmpChannelId; -use pallet_ethereum::PostLogContent; -use polkadot_core_primitives::BlockNumber as RelayBlockNumber; -use polkadot_parachain::primitives::{Id as ParaId, Sibling}; -use xcm::latest::{ - Error as XcmError, ExecuteXcm, - Junction::{PalletInstance, Parachain}, - Location, NetworkId, Outcome, Xcm, -}; -use xcm_builder::{ - AccountKey20Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, - AllowTopLevelPaidExecutionFrom, Case, EnsureXcmOrigin, FixedWeightBounds, FungibleAdapter, - IsConcrete, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, - SiblingParachainConvertsVia, SignedAccountKey20AsNative, SovereignSignedViaLocation, - TakeWeightCredit, WithComputedOrigin, -}; -use xcm_executor::{Config, XcmExecutor}; - -#[cfg(feature = "runtime-benchmarks")] -use moonbeam_runtime_common::benchmarking::BenchmarkHelper as ArgumentsBenchmarkHelper; -use pallet_xcm_transactor::RelayIndices; -use scale_info::TypeInfo; -use xcm_simulator::{ - DmpMessageHandlerT as DmpMessageHandler, XcmpMessageFormat, - XcmpMessageHandlerT as XcmpMessageHandler, -}; - -pub type AccountId = moonbeam_core_primitives::AccountId; -pub type Balance = u128; -pub type AssetId = u128; -pub type BlockNumber = BlockNumberFor; - -parameter_types! { - pub const BlockHashCount: u32 = 250; -} - -impl frame_system::Config for Runtime { - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type RuntimeTask = RuntimeTask; - type Nonce = u64; - type Block = Block; - type Hash = H256; - type Hashing = ::sp_runtime::traits::BlakeTwo256; - type AccountId = AccountId; - type Lookup = IdentityLookup; - type RuntimeEvent = RuntimeEvent; - 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 = frame_support::traits::ConstU32<16>; - type SingleBlockMigrations = (); - type MultiBlockMigrator = (); - type PreInherents = (); - type PostInherents = (); - type PostTransactions = (); - type ExtensionsWeightInfo = (); -} - -parameter_types! { - pub ExistentialDeposit: Balance = 0; - pub const MaxLocks: u32 = 50; - pub const MaxReserves: u32 = 50; -} - -impl pallet_balances::Config for Runtime { - type MaxLocks = MaxLocks; - type Balance = Balance; - type RuntimeEvent = RuntimeEvent; - type DustRemoval = (); - type ExistentialDeposit = ExistentialDeposit; - type AccountStore = System; - type WeightInfo = (); - type MaxReserves = MaxReserves; - type ReserveIdentifier = [u8; 8]; - type RuntimeHoldReason = (); - type FreezeIdentifier = (); - type MaxFreezes = (); - type RuntimeFreezeReason = (); - type DoneSlashHandler = (); -} - -parameter_types! { - pub const AssetDeposit: Balance = 10; // Does not really matter as this will be only called by root - pub const ApprovalDeposit: Balance = 0; - pub const AssetsStringLimit: u32 = 50; - pub const MetadataDepositBase: Balance = 0; - pub const MetadataDepositPerByte: Balance = 0; - pub const AssetAccountDeposit: Balance = 0; -} - -/// Type for specifying how a `Location` can be converted into an `AccountId`. This is used -/// when determining ownership of accounts for asset transacting and when attempting to use XCM -/// `Transact` in order to determine the dispatch Origin. -pub type LocationToAccountId = ( - // The parent (Relay-chain) origin converts to the default `AccountId`. - ParentIsPreset, - // Sibling parachain origins convert to AccountId via the `ParaId::into`. - SiblingParachainConvertsVia, - AccountKey20Aliases, - // Generate remote accounts according to polkadot standards - xcm_builder::HashedDescription< - AccountId, - xcm_builder::DescribeFamily, - >, -); - -/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, -/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can -/// biases the kind of local `Origin` it will become. -pub type XcmOriginToTransactDispatchOrigin = ( - // Sovereign account converter; this attempts to derive an `AccountId` from the origin location - // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for - // foreign chains who want to have a local sovereign account on this chain which they control. - SovereignSignedViaLocation, - // Native converter for Relay-chain (Parent) location; will converts to a `Relay` origin when - // recognised. - RelayChainAsNative, - // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when - // recognised. - SiblingParachainAsNative, - // Superuser converter for the Relay-chain (Parent) location. This will allow it to issue a - // transaction from the Root origin. - ParentAsSuperuser, - // Xcm origins can be represented natively under the Xcm pallet's Xcm origin. - pallet_xcm::XcmPassthrough, - SignedAccountKey20AsNative, -); - -parameter_types! { - pub const UnitWeightCost: Weight = Weight::from_parts(1u64, 1u64); - pub MaxInstructions: u32 = 100; -} - -/// The transactor for our own chain currency. -pub type LocalAssetTransactor = FungibleAdapter< - // Use this currency: - Balances, - // Use this currency when it is a fungible asset matching any of the locations in - // SelfReserveRepresentations - IsConcrete, - // We can convert the Locations with our converter above: - LocationToAccountId, - // Our chain's account ID type (we can't get away without mentioning it explicitly): - AccountId, - // We dont allow teleport - (), ->; - -// These will be our transactors -// We use both transactors -pub type AssetTransactors = (LocalAssetTransactor, EvmForeignAssets); -pub type XcmRouter = super::ParachainXcmRouter; - -pub type XcmBarrier = ( - // Weight that is paid for may be consumed. - TakeWeightCredit, - // Expected responses are OK. - AllowKnownQueryResponses, - WithComputedOrigin< - ( - // If the message is one that immediately attemps to pay for execution, then allow it. - AllowTopLevelPaidExecutionFrom, - // Subscriptions for version tracking are OK. - AllowSubscriptionsFrom, - ), - UniversalLocation, - ConstU32<8>, - >, -); - -parameter_types! { - /// Xcm fees will go to the treasury account - pub XcmFeesAccount: AccountId = Treasury::account_id(); - /// Parachain token units per second of execution - pub ParaTokensPerSecond: u128 = WEIGHT_REF_TIME_PER_SECOND as u128; -} - -pub struct WeightToFee; -impl sp_weights::WeightToFee for WeightToFee { - type Balance = Balance; - - fn weight_to_fee(weight: &Weight) -> Self::Balance { - use sp_runtime::SaturatedConversion as _; - Self::Balance::saturated_from(weight.ref_time()) - .saturating_mul(ParaTokensPerSecond::get()) - .saturating_div(frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND as u128) - } -} - -parameter_types! { - pub RelayNetwork: NetworkId = moonbeam_runtime::xcm_config::RelayNetwork::get(); - pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); - pub UniversalLocation: InteriorLocation = - [GlobalConsensus(RelayNetwork::get()), Parachain(MsgQueue::parachain_id().into())].into(); - pub SelfReserve: Location = Location { - parents:0, - interior: [ - PalletInstance(::index() as u8) - ].into() - }; - pub const MaxAssetsIntoHolding: u32 = 64; - - pub AssetHubLocation: Location = Location::new(1, [Parachain(1000)]); - pub RelayLocationFilter: AssetFilter = Wild(AllOf { - fun: WildFungible, - id: xcm::prelude::AssetId(Location::parent()), - }); - - pub RelayChainNativeAssetFromAssetHub: (AssetFilter, Location) = ( - RelayLocationFilter::get(), - AssetHubLocation::get() - ); -} - -use frame_system::RawOrigin; -use sp_runtime::traits::PostDispatchInfoOf; -use sp_runtime::DispatchErrorWithPostInfo; -use xcm_executor::traits::CallDispatcher; -moonbeam_runtime_common::impl_moonbeam_xcm_call!(); - -type Reserves = ( - // Relaychain (DOT) from Asset Hub - Case, - // Assets which the reserve is the same as the origin. - xcm_primitives::MultiNativeAsset< - xcm_primitives::AbsoluteAndRelativeReserve, - >, -); - -pub struct XcmConfig; -impl Config for XcmConfig { - type RuntimeCall = RuntimeCall; - type XcmSender = XcmRouter; - type AssetTransactor = AssetTransactors; - type OriginConverter = XcmOriginToTransactDispatchOrigin; - type IsReserve = Reserves; - type IsTeleporter = (); - type UniversalLocation = UniversalLocation; - type Barrier = XcmBarrier; - type Weigher = FixedWeightBounds; - type Trader = pallet_xcm_weight_trader::Trader; - type ResponseHandler = PolkadotXcm; - type SubscriptionService = PolkadotXcm; - type AssetTrap = PolkadotXcm; - type AssetClaims = PolkadotXcm; - type CallDispatcher = MoonbeamCall; - type AssetLocker = (); - type AssetExchanger = (); - type PalletInstancesInfo = (); - type MaxAssetsIntoHolding = MaxAssetsIntoHolding; - type FeeManager = (); - type MessageExporter = (); - type UniversalAliases = Nothing; - type SafeCallFilter = Everything; - type Aliasers = Nothing; - type TransactionalProcessor = (); - type HrmpNewChannelOpenRequestHandler = (); - type HrmpChannelAcceptedHandler = (); - type HrmpChannelClosingHandler = (); - type XcmRecorder = PolkadotXcm; - type XcmEventEmitter = (); -} - -impl cumulus_pallet_xcm::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type XcmExecutor = XcmExecutor; -} - -// Our currencyId. We distinguish for now between SelfReserve, and Others, defined by their Id. -#[derive( - Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo, DecodeWithMemTracking, -)] -pub enum CurrencyId { - SelfReserve, - ForeignAsset(AssetId), -} - -// How to convert from CurrencyId to Location -pub struct CurrencyIdToLocation(sp_std::marker::PhantomData); -impl sp_runtime::traits::Convert> - for CurrencyIdToLocation -where - AssetXConverter: MaybeEquivalence, -{ - fn convert(currency: CurrencyId) -> Option { - match currency { - CurrencyId::SelfReserve => { - let multi: Location = SelfReserve::get(); - Some(multi) - } - CurrencyId::ForeignAsset(asset) => AssetXConverter::convert_back(&asset), - } - } -} - -parameter_types! { - pub const BaseXcmWeight: Weight = Weight::from_parts(100u64, 100u64); - pub const MaxAssetsForTransfer: usize = 2; - pub SelfLocation: Location = Location::here(); - pub SelfLocationAbsolute: Location = Location { - parents:1, - interior: [ - Parachain(MsgQueue::parachain_id().into()) - ].into() - }; -} - -parameter_types! { - pub const ProposalBond: Permill = Permill::from_percent(5); - pub const ProposalBondMinimum: Balance = 0; - pub const SpendPeriod: u32 = 0; - pub const TreasuryId: PalletId = PalletId(*b"pc/trsry"); - pub const MaxApprovals: u32 = 100; - pub TreasuryAccount: AccountId = Treasury::account_id(); -} - -impl pallet_treasury::Config for Runtime { - type PalletId = TreasuryId; - type Currency = Balances; - type RejectOrigin = EnsureRoot; - type RuntimeEvent = RuntimeEvent; - type SpendPeriod = SpendPeriod; - type Burn = (); - type BurnDestination = (); - type MaxApprovals = MaxApprovals; - type WeightInfo = (); - type SpendFunds = (); - type SpendOrigin = frame_support::traits::NeverEnsureOrigin; // Same as Polkadot - type AssetKind = NativeOrWithId; - type Beneficiary = AccountId; - type BeneficiaryLookup = IdentityLookup; - type Paymaster = MultiAssetPaymaster; - type BalanceConverter = AssetRateConverter; - type PayoutPeriod = ConstU32<0>; - #[cfg(feature = "runtime-benchmarks")] - type BenchmarkHelper = ArgumentsBenchmarkHelper; - type BlockNumberProvider = System; -} - -#[frame_support::pallet] -pub mod mock_msg_queue { - use super::*; - use frame_support::pallet_prelude::*; - - #[pallet::config] - pub trait Config: frame_system::Config { - type XcmExecutor: ExecuteXcm; - } - - #[pallet::call] - impl Pallet {} - - #[pallet::pallet] - pub struct Pallet(_); - - #[pallet::storage] - #[pallet::getter(fn parachain_id)] - pub(super) type ParachainId = StorageValue<_, ParaId, 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, InstructionError), - /// 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 = Location::new(1, [Parachain(sender.into())]); - let mut id = [0u8; 32]; - id.copy_from_slice(hash.as_ref()); - match T::XcmExecutor::prepare_and_execute( - location, - xcm, - &mut id, - max_weight, - Weight::zero(), - ) { - Outcome::Error(error) => { - (Err(error.clone()), Event::Fail(Some(hash), error)) - } - Outcome::Complete { used } => (Ok(used), 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 { used, error } => { - (Ok(used), Event::Fail(Some(hash), error)) - } - } - } - Err(()) => ( - Err(InstructionError { - error: XcmError::UnhandledXcmVersion, - index: 0, - }), - 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 mut 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::prepare_and_execute( - Parent, - x, - &mut id, - limit, - Weight::zero(), - ); - - Self::deposit_event(Event::ExecutedDownward(id, outcome)); - } - } - } - limit - } - } -} - -// 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 {} - - #[pallet::call] - impl Pallet {} - - #[pallet::pallet] - 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)); - } - } -} - -impl mock_msg_queue::Config for Runtime { - type XcmExecutor = XcmExecutor; -} - -impl mock_version_changer::Config for Runtime {} - -pub type LocalOriginToLocation = - xcm_primitives::SignedToAccountId20; - -parameter_types! { - pub MatcherLocation: Location = Location::here(); -} - -impl pallet_xcm::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type SendXcmOrigin = EnsureXcmOrigin; - type XcmRouter = XcmRouter; - type ExecuteXcmOrigin = EnsureXcmOrigin; - type XcmExecuteFilter = frame_support::traits::Nothing; - type XcmExecutor = XcmExecutor; - // Do not allow teleports - type XcmTeleportFilter = Nothing; - type XcmReserveTransferFilter = Everything; - type Weigher = FixedWeightBounds; - type UniversalLocation = UniversalLocation; - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; - // We use a custom one to test runtime ugprades - type AdvertisedXcmVersion = XcmVersioner; - type Currency = Balances; - type CurrencyMatcher = IsConcrete; - type TrustedLockers = (); - type SovereignAccountOf = (); - type MaxLockers = ConstU32<8>; - type WeightInfo = pallet_xcm::TestWeightInfo; - type MaxRemoteLockConsumers = ConstU32<0>; - type RemoteLockConsumerIdentifier = (); - type AdminOrigin = frame_system::EnsureRoot; - type AuthorizedAliasConsideration = Disabled; -} - -#[derive( - Clone, - Default, - Eq, - Debug, - PartialEq, - Ord, - PartialOrd, - Encode, - Decode, - TypeInfo, - DecodeWithMemTracking, -)] -pub struct AssetMetadata { - pub name: Vec, - pub symbol: Vec, - pub decimals: u8, -} - -pub struct AccountIdToH160; -impl sp_runtime::traits::Convert for AccountIdToH160 { - fn convert(account_id: AccountId) -> H160 { - account_id.into() - } -} - -pub type ForeignAssetManagerOrigin = EitherOf< - MapSuccessToXcm>, - MapSuccessToGovernance>, ->; - -moonbeam_runtime_common::impl_evm_runner_precompile_or_eth_xcm!(); - -parameter_types! { - pub ForeignAssetCreationDeposit: u128 = 100 * currency::GLMR; -} - -impl pallet_moonbeam_foreign_assets::Config for Runtime { - type AccountIdToH160 = AccountIdToH160; - type AssetIdFilter = Everything; - type EvmRunner = EvmRunnerPrecompileOrEthXcm; - type ConvertLocation = - SiblingParachainConvertsVia; - type ForeignAssetCreatorOrigin = ForeignAssetManagerOrigin; - type ForeignAssetFreezerOrigin = ForeignAssetManagerOrigin; - type ForeignAssetModifierOrigin = ForeignAssetManagerOrigin; - type ForeignAssetUnfreezerOrigin = ForeignAssetManagerOrigin; - type OnForeignAssetCreated = (); - type MaxForeignAssets = ConstU32<256>; - type WeightInfo = (); - type XcmLocationToH160 = LocationToH160; - type ForeignAssetCreationDeposit = ForeignAssetCreationDeposit; - type Balance = Balance; - type Currency = Balances; -} - -// 1 DOT should be enough -parameter_types! { - pub MaxHrmpRelayFee: Asset = (Location::parent(), 1_000_000_000_000u128).into(); -} - -impl pallet_xcm_transactor::Config for Runtime { - type Balance = Balance; - type Transactor = MockTransactors; - type DerivativeAddressRegistrationOrigin = EnsureRoot; - type SovereignAccountDispatcherOrigin = frame_system::EnsureRoot; - type CurrencyId = CurrencyId; - type AccountIdToLocation = xcm_primitives::AccountIdToLocation; - type CurrencyIdToLocation = CurrencyIdToLocation; - type SelfLocation = SelfLocation; - type Weigher = xcm_builder::FixedWeightBounds; - type UniversalLocation = UniversalLocation; - type XcmSender = XcmRouter; - type BaseXcmWeight = BaseXcmWeight; - type AssetTransactor = AssetTransactors; - type ReserveProvider = xcm_primitives::AbsoluteAndRelativeReserve; - type WeightInfo = (); - type HrmpManipulatorOrigin = EnsureRoot; - type HrmpOpenOrigin = EnsureRoot; - type MaxHrmpFee = xcm_builder::Case; - type FeeTrader = moonbeam_tests_primitives::MemoryFeeTrader; -} - -parameter_types! { - pub RelayLocation: Location = Location::parent(); -} - -impl pallet_xcm_weight_trader::Config for Runtime { - type AccountIdToLocation = xcm_primitives::AccountIdToLocation; - type AddSupportedAssetOrigin = EnsureRoot; - type AssetLocationFilter = Everything; - type AssetTransactor = AssetTransactors; - type Balance = Balance; - type EditSupportedAssetOrigin = EnsureRoot; - type NativeLocation = SelfReserve; - type PauseSupportedAssetOrigin = EnsureRoot; - type RemoveSupportedAssetOrigin = EnsureRoot; - type ResumeSupportedAssetOrigin = EnsureRoot; - type WeightInfo = (); - type WeightToFee = WeightToFee; - type XcmFeesAccount = XcmFeesAccount; - #[cfg(feature = "runtime-benchmarks")] - type NotFilteredLocation = RelayLocation; -} - -parameter_types! { - pub const MinimumPeriod: u64 = 1000; -} -impl pallet_timestamp::Config for Runtime { - type Moment = u64; - type OnTimestampSet = (); - type MinimumPeriod = MinimumPeriod; - type WeightInfo = (); -} - -parameter_types! { - pub BlockGasLimit: U256 = moonbeam_runtime::BlockGasLimit::get(); - pub WeightPerGas: Weight = moonbeam_runtime::WeightPerGas::get(); - pub const GasLimitPovSizeRatio: u64 = moonbeam_runtime::GasLimitPovSizeRatio::get(); - pub GasLimitStorageGrowthRatio: u64 = moonbeam_runtime::GasLimitStorageGrowthRatio::get(); -} - -impl pallet_evm::Config for Runtime { - type FeeCalculator = (); - type GasWeightMapping = pallet_evm::FixedGasWeightMapping; - type WeightPerGas = WeightPerGas; - - type CallOrigin = pallet_evm::EnsureAddressRoot; - type WithdrawOrigin = pallet_evm::EnsureAddressNever; - - type AddressMapping = pallet_evm::IdentityAddressMapping; - type Currency = Balances; - type Runner = pallet_evm::runner::stack::Runner; - - type PrecompilesType = (); - type PrecompilesValue = (); - type ChainId = (); - type BlockGasLimit = BlockGasLimit; - type TransactionGasLimit = moonbeam_runtime::TransactionGasLimit; - type OnChargeTransaction = (); - type BlockHashMapping = pallet_evm::SubstrateBlockHashMapping; - type FindAuthor = (); - type OnCreate = (); - type GasLimitPovSizeRatio = GasLimitPovSizeRatio; - type GasLimitStorageGrowthRatio = GasLimitStorageGrowthRatio; - type Timestamp = Timestamp; - type WeightInfo = pallet_evm::weights::SubstrateWeight; - type AccountProvider = FrameSystemAccountProvider; - type CreateOriginFilter = (); - type CreateInnerOriginFilter = (); -} - -#[allow(dead_code)] -pub struct NormalFilter; - -impl frame_support::traits::Contains for NormalFilter { - fn contains(c: &RuntimeCall) -> bool { - match c { - _ => true, - } - } -} - -// We need to use the encoding from the relay mock runtime -#[derive(Encode, Decode)] -pub enum RelayCall { - #[codec(index = 5u8)] - // the index should match the position of the module in `construct_runtime!` - Utility(UtilityCall), - #[codec(index = 6u8)] - // the index should match the position of the module in `construct_runtime!` - Hrmp(HrmpCall), -} - -#[derive(Encode, Decode)] -pub enum UtilityCall { - #[codec(index = 1u8)] - AsDerivative(u16), -} - -// HRMP call encoding, needed for xcm transactor pallet -#[derive(Encode, Decode)] -pub enum HrmpCall { - #[codec(index = 0u8)] - InitOpenChannel(ParaId, u32, u32), - #[codec(index = 1u8)] - AcceptOpenChannel(ParaId), - #[codec(index = 2u8)] - CloseChannel(HrmpChannelId), - #[codec(index = 6u8)] - CancelOpenRequest(HrmpChannelId, u32), -} - -#[derive( - Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo, DecodeWithMemTracking, -)] -pub enum MockTransactors { - Relay, -} - -impl xcm_primitives::XcmTransact for MockTransactors { - fn destination(self) -> Location { - match self { - MockTransactors::Relay => Location::parent(), - } - } - - fn utility_pallet_index(&self) -> u8 { - RelayIndices::::get().utility - } - - fn staking_pallet_index(&self) -> u8 { - RelayIndices::::get().staking - } -} - -#[allow(dead_code)] -pub struct MockHrmpEncoder; - -impl xcm_primitives::HrmpEncodeCall for MockHrmpEncoder { - fn hrmp_encode_call( - call: xcm_primitives::HrmpAvailableCalls, - ) -> Result, xcm::latest::Error> { - match call { - xcm_primitives::HrmpAvailableCalls::InitOpenChannel(a, b, c) => Ok(RelayCall::Hrmp( - HrmpCall::InitOpenChannel(a.clone(), b.clone(), c.clone()), - ) - .encode()), - xcm_primitives::HrmpAvailableCalls::AcceptOpenChannel(a) => { - Ok(RelayCall::Hrmp(HrmpCall::AcceptOpenChannel(a.clone())).encode()) - } - xcm_primitives::HrmpAvailableCalls::CloseChannel(a) => { - Ok(RelayCall::Hrmp(HrmpCall::CloseChannel(a.clone())).encode()) - } - xcm_primitives::HrmpAvailableCalls::CancelOpenRequest(a, b) => { - Ok(RelayCall::Hrmp(HrmpCall::CancelOpenRequest(a.clone(), b.clone())).encode()) - } - } - } -} - -parameter_types! { - pub const PostBlockAndTxnHashes: PostLogContent = PostLogContent::BlockAndTxnHashes; - pub const AllowUnprotectedTxs: bool = false; -} - -impl pallet_ethereum::Config for Runtime { - type StateRoot = - pallet_ethereum::IntermediateStateRoot<::Version>; - type PostLogContent = PostBlockAndTxnHashes; - type ExtraDataLength = ConstU32<30>; - type AllowUnprotectedTxs = AllowUnprotectedTxs; -} -parameter_types! { - pub ReservedXcmpWeight: Weight = Weight::from_parts(u64::max_value(), 0); -} - -#[derive( - Copy, - Clone, - Eq, - PartialEq, - Ord, - PartialOrd, - Encode, - Decode, - Debug, - MaxEncodedLen, - TypeInfo, - DecodeWithMemTracking, -)] -pub enum ProxyType { - NotAllowed = 0, - Any = 1, -} - -impl pallet_evm_precompile_proxy::EvmProxyCallFilter for ProxyType {} - -impl InstanceFilter for ProxyType { - fn filter(&self, _c: &RuntimeCall) -> bool { - match self { - ProxyType::NotAllowed => false, - ProxyType::Any => true, - } - } - fn is_superset(&self, _o: &Self) -> bool { - false - } -} - -impl Default for ProxyType { - fn default() -> Self { - Self::NotAllowed - } -} - -parameter_types! { - pub const ProxyCost: u64 = 1; -} - -impl pallet_proxy::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type RuntimeCall = RuntimeCall; - type Currency = Balances; - type ProxyType = ProxyType; - type ProxyDepositBase = ProxyCost; - type ProxyDepositFactor = ProxyCost; - type MaxProxies = ConstU32<32>; - type WeightInfo = pallet_proxy::weights::SubstrateWeight; - type MaxPending = ConstU32<32>; - type CallHasher = BlakeTwo256; - type AnnouncementDepositBase = ProxyCost; - type AnnouncementDepositFactor = ProxyCost; - type BlockNumberProvider = System; -} - -pub struct EthereumXcmEnsureProxy; -impl xcm_primitives::EnsureProxy for EthereumXcmEnsureProxy { - fn ensure_ok(delegator: AccountId, delegatee: AccountId) -> Result<(), &'static str> { - // The EVM implicitely contains an Any proxy, so we only allow for "Any" proxies - let def: pallet_proxy::ProxyDefinition = - pallet_proxy::Pallet::::find_proxy( - &delegator, - &delegatee, - Some(ProxyType::Any), - ) - .map_err(|_| "proxy error: expected `ProxyType::Any`")?; - // We only allow to use it for delay zero proxies, as the call will iMmediatly be executed - ensure!(def.delay.is_zero(), "proxy delay is Non-zero`"); - Ok(()) - } -} - -impl pallet_ethereum_xcm::Config for Runtime { - type InvalidEvmTransactionError = pallet_ethereum::InvalidTransactionWrapper; - type ValidatedTransaction = pallet_ethereum::ValidatedTransaction; - type XcmEthereumOrigin = pallet_ethereum_xcm::EnsureXcmEthereumTransaction; - type ReservedXcmpWeight = ReservedXcmpWeight; - type EnsureProxy = EthereumXcmEnsureProxy; - type ControllerOrigin = EnsureRoot; - type ForceOrigin = EnsureRoot; -} - -type Block = frame_system::mocking::MockBlockU32; - -construct_runtime!( - pub enum Runtime { - System: frame_system, - Balances: pallet_balances, - MsgQueue: mock_msg_queue, - XcmVersioner: mock_version_changer, - - PolkadotXcm: pallet_xcm, - CumulusXcm: cumulus_pallet_xcm, - XcmTransactor: pallet_xcm_transactor, - XcmWeightTrader: pallet_xcm_weight_trader, - Treasury: pallet_treasury, - Proxy: pallet_proxy, - - Timestamp: pallet_timestamp, - EVM: pallet_evm, - Ethereum: pallet_ethereum, - EthereumXcm: pallet_ethereum_xcm, - EvmForeignAssets: pallet_moonbeam_foreign_assets, - } -); - -pub(crate) fn para_events() -> Vec { - System::events() - .into_iter() - .map(|r| r.event) - .filter_map(|e| Some(e)) - .collect::>() -} - -use frame_support::traits::{Disabled, OnFinalize, OnInitialize, UncheckedOnRuntimeUpgrade}; -use moonbeam_runtime::{currency, xcm_config::LocationToH160}; -use pallet_evm::FrameSystemAccountProvider; -use sp_weights::constants::WEIGHT_REF_TIME_PER_SECOND; - -pub(crate) fn on_runtime_upgrade() { - VersionUncheckedMigrateToV1::::on_runtime_upgrade(); -} - -pub(crate) fn para_roll_to(n: BlockNumber) { - 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/moonbeam/tests/xcm_mock/relay_chain.rs b/runtime/moonbeam/tests/xcm_mock/relay_chain.rs deleted file mode 100644 index dfc0eabea08..00000000000 --- a/runtime/moonbeam/tests/xcm_mock/relay_chain.rs +++ /dev/null @@ -1,452 +0,0 @@ -// Copyright 2021 Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -//! Relay chain runtime mock. - -use frame_support::{ - construct_runtime, parameter_types, - traits::{Everything, Nothing, ProcessMessage, ProcessMessageError}, -}; -use frame_system::pallet_prelude::BlockNumberFor; -use sp_core::H256; -use sp_runtime::{ - traits::{ConstU32, IdentityLookup}, - AccountId32, -}; - -use frame_support::weights::{Weight, WeightMeter}; -use polkadot_parachain::primitives::Id as ParaId; -use polkadot_runtime_parachains::{ - configuration, dmp, hrmp, - inclusion::{AggregateMessageOrigin, UmpQueueId}, - origin, paras, shared, -}; -use sp_runtime::transaction_validity::TransactionPriority; -use sp_runtime::Permill; -use xcm::latest::prelude::*; -use xcm_builder::{ - Account32Hash, AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, - AllowTopLevelPaidExecutionFrom, ChildParachainAsNative, ChildParachainConvertsVia, - ChildSystemParachainAsSuperuser, FixedRateOfFungible, FixedWeightBounds, - FungibleAdapter as XcmCurrencyAdapter, IsConcrete, ProcessXcmMessage, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, - WithComputedOrigin, -}; -use xcm_executor::{Config, XcmExecutor}; -pub type AccountId = AccountId32; -pub type Balance = u128; -pub type BlockNumber = BlockNumberFor; - -parameter_types! { - pub const BlockHashCount: u32 = 250; -} - -impl frame_system::Config for Runtime { - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type RuntimeTask = RuntimeTask; - type Nonce = u64; - type Block = Block; - type Hash = H256; - type Hashing = ::sp_runtime::traits::BlakeTwo256; - type AccountId = AccountId; - type Lookup = IdentityLookup; - type RuntimeEvent = RuntimeEvent; - 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>; - type SingleBlockMigrations = (); - type MultiBlockMigrator = (); - type PreInherents = (); - type PostInherents = (); - type PostTransactions = (); - type ExtensionsWeightInfo = (); -} - -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 RuntimeEvent = RuntimeEvent; - type DustRemoval = (); - type ExistentialDeposit = ExistentialDeposit; - type AccountStore = System; - type WeightInfo = (); - type MaxReserves = MaxReserves; - type ReserveIdentifier = [u8; 8]; - type RuntimeHoldReason = (); - type FreezeIdentifier = (); - type MaxFreezes = (); - type RuntimeFreezeReason = (); - type DoneSlashHandler = (); -} - -impl pallet_utility::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type RuntimeCall = RuntimeCall; - type WeightInfo = (); - type PalletsOrigin = OriginCaller; -} - -impl shared::Config for Runtime { - type DisabledValidators = (); -} - -impl configuration::Config for Runtime { - type WeightInfo = configuration::TestWeightInfo; -} - -parameter_types! { - pub KsmLocation: Location = Here.into(); - pub const KusamaNetwork: NetworkId = NetworkId::Kusama; - pub const AnyNetwork: Option = None; - pub UniversalLocation: InteriorLocation = Here; -} - -pub type SovereignAccountOf = ( - ChildParachainConvertsVia, - AccountId32Aliases, - // Not enabled in the relay per se, but we enable it to test - // the transact_through_signed extrinsic - Account32Hash, -); - -pub type LocalAssetTransactor = - XcmCurrencyAdapter, SovereignAccountOf, AccountId, ()>; - -type LocalOriginConverter = ( - SovereignSignedViaLocation, - ChildParachainAsNative, - SignedAccountId32AsNative, - ChildSystemParachainAsSuperuser, -); - -parameter_types! { - pub const BaseXcmWeight: Weight = Weight::from_parts(1000u64, 1000u64); - pub KsmPerSecond: (AssetId, u128, u128) = (AssetId(KsmLocation::get()), 1, 1); - pub const MaxInstructions: u32 = 100; - pub const MaxAssetsIntoHolding: u32 = 64; - pub MatcherLocation: Location = Location::here(); -} - -pub type XcmRouter = super::RelayChainXcmRouter; - -pub type XcmBarrier = ( - // Weight that is paid for may be consumed. - TakeWeightCredit, - // Expected responses are OK. - AllowKnownQueryResponses, - WithComputedOrigin< - ( - // If the message is one that immediately attemps to pay for execution, then allow it. - AllowTopLevelPaidExecutionFrom, - // Subscriptions for version tracking are OK. - AllowSubscriptionsFrom, - ), - UniversalLocation, - ConstU32<8>, - >, -); - -parameter_types! { - pub Kusama: AssetFilter = Wild(AllOf { fun: WildFungible, id: AssetId(KsmLocation::get()) }); - pub Statemine: Location = Parachain(1000).into(); - pub KusamaForStatemine: (AssetFilter, Location) = (Kusama::get(), Statemine::get()); -} - -pub type TrustedTeleporters = xcm_builder::Case; - -pub struct XcmConfig; -impl Config for XcmConfig { - type RuntimeCall = RuntimeCall; - type XcmSender = XcmRouter; - type AssetTransactor = LocalAssetTransactor; - type OriginConverter = LocalOriginConverter; - type IsReserve = (); - type IsTeleporter = TrustedTeleporters; - type UniversalLocation = UniversalLocation; - type Barrier = XcmBarrier; - type Weigher = FixedWeightBounds; - type Trader = FixedRateOfFungible; - type ResponseHandler = XcmPallet; - type AssetTrap = XcmPallet; - type AssetClaims = XcmPallet; - type SubscriptionService = XcmPallet; - type CallDispatcher = RuntimeCall; - type AssetLocker = (); - type AssetExchanger = (); - type PalletInstancesInfo = (); - type MaxAssetsIntoHolding = MaxAssetsIntoHolding; - type FeeManager = (); - type MessageExporter = (); - type UniversalAliases = Nothing; - type SafeCallFilter = Everything; - type Aliasers = Nothing; - type TransactionalProcessor = (); - type HrmpNewChannelOpenRequestHandler = (); - type HrmpChannelAcceptedHandler = (); - type HrmpChannelClosingHandler = (); - type XcmRecorder = XcmPallet; - type XcmEventEmitter = XcmPallet; -} - -pub type LocalOriginToLocation = SignedToAccountId32; - -impl pallet_xcm::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - 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 UniversalLocation = UniversalLocation; - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; - type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; - type Currency = Balances; - type CurrencyMatcher = (); - type TrustedLockers = (); - type SovereignAccountOf = (); - type MaxLockers = ConstU32<8>; - type WeightInfo = pallet_xcm::TestWeightInfo; - type MaxRemoteLockConsumers = ConstU32<0>; - type RemoteLockConsumerIdentifier = (); - type AdminOrigin = frame_system::EnsureRoot; - type AuthorizedAliasConsideration = Disabled; -} - -parameter_types! { - pub const FirstMessageFactorPercent: u64 = 100; -} - -parameter_types! { - pub const ParasUnsignedPriority: TransactionPriority = TransactionPriority::max_value(); -} - -/// A very dumb implementation of `EstimateNextSessionRotation`. At the moment of writing, this -/// is more to satisfy type requirements rather than to test anything. -pub struct TestNextSessionRotation; - -impl frame_support::traits::EstimateNextSessionRotation for TestNextSessionRotation { - fn average_session_length() -> u32 { - 10 - } - - fn estimate_current_session_progress(_now: u32) -> (Option, Weight) { - (None, Weight::zero()) - } - - fn estimate_next_session_rotation(_now: u32) -> (Option, Weight) { - (None, Weight::zero()) - } -} - -impl paras::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = paras::TestWeightInfo; - type UnsignedPriority = ParasUnsignedPriority; - type NextSessionRotation = TestNextSessionRotation; - type QueueFootprinter = (); - type OnNewHead = (); - type AssignCoretime = (); - type Fungible = (); - type CooldownRemovalMultiplier = (); - type AuthorizeCurrentCodeOrigin = frame_system::EnsureRoot; -} - -impl dmp::Config for Runtime {} - -parameter_types! { - pub const DefaultChannelSizeAndCapacityWithSystem: (u32, u32) = (4, 1); -} - -impl hrmp::Config for Runtime { - type RuntimeOrigin = RuntimeOrigin; - type RuntimeEvent = RuntimeEvent; - type Currency = Balances; - type WeightInfo = TestHrmpWeightInfo; - type ChannelManager = frame_system::EnsureRoot; - type DefaultChannelSizeAndCapacityWithSystem = DefaultChannelSizeAndCapacityWithSystem; - type VersionWrapper = XcmPallet; -} - -impl frame_system::offchain::CreateTransactionBase for Runtime -where - RuntimeCall: From, -{ - type Extrinsic = UncheckedExtrinsic; - type RuntimeCall = RuntimeCall; -} - -impl origin::Config for Runtime {} - -type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; -type Block = frame_system::mocking::MockBlockU32; - -impl frame_system::offchain::CreateInherent for Runtime -where - RuntimeCall: From, -{ - fn create_inherent(call: RuntimeCall) -> UncheckedExtrinsic { - UncheckedExtrinsic::new_bare(call) - } - - fn create_bare(call: RuntimeCall) -> UncheckedExtrinsic { - UncheckedExtrinsic::new_bare(call) - } -} - -parameter_types! { - pub MessageQueueServiceWeight: Weight = Weight::from_parts(1_000_000_000, 1_000_000); - pub const MessageQueueHeapSize: u32 = 65_536; - pub const MessageQueueMaxStale: u32 = 16; -} - -pub struct MessageProcessor; -impl ProcessMessage for MessageProcessor { - type Origin = AggregateMessageOrigin; - - fn process_message( - message: &[u8], - origin: Self::Origin, - meter: &mut WeightMeter, - id: &mut [u8; 32], - ) -> Result { - let para = match origin { - AggregateMessageOrigin::Ump(UmpQueueId::Para(para)) => para, - }; - ProcessXcmMessage::, RuntimeCall>::process_message( - message, - Junction::Parachain(para.into()), - meter, - id, - ) - } -} - -impl pallet_message_queue::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Size = u32; - type HeapSize = MessageQueueHeapSize; - type MaxStale = MessageQueueMaxStale; - type ServiceWeight = MessageQueueServiceWeight; - type MessageProcessor = MessageProcessor; - type QueueChangeHandler = (); - type WeightInfo = (); - type QueuePausedQuery = (); - type IdleMaxServiceWeight = MessageQueueServiceWeight; -} - -construct_runtime!( - pub enum Runtime { - System: frame_system, - Balances: pallet_balances, - ParasOrigin: origin, - MessageQueue: pallet_message_queue, - XcmPallet: pallet_xcm, - Utility: pallet_utility, - Hrmp: hrmp, - Dmp: dmp, - Paras: paras, - Configuration: configuration, - } -); - -pub(crate) fn relay_events() -> Vec { - System::events() - .into_iter() - .map(|r| r.event) - .filter_map(|e| Some(e)) - .collect::>() -} - -use frame_support::traits::{Disabled, OnFinalize, OnInitialize}; -pub(crate) fn relay_roll_to(n: BlockNumber) { - 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()); - } -} - -/// A weight info that is only suitable for testing. -pub struct TestHrmpWeightInfo; - -impl hrmp::WeightInfo for TestHrmpWeightInfo { - fn hrmp_accept_open_channel() -> Weight { - Weight::from_parts(1, 0) - } - fn force_clean_hrmp(_: u32, _: u32) -> Weight { - Weight::from_parts(1, 0) - } - fn force_process_hrmp_close(_: u32) -> Weight { - Weight::from_parts(1, 0) - } - fn force_process_hrmp_open(_: u32) -> Weight { - Weight::from_parts(1, 0) - } - fn hrmp_cancel_open_request(_: u32) -> Weight { - Weight::from_parts(1, 0) - } - fn hrmp_close_channel() -> Weight { - Weight::from_parts(1, 0) - } - fn hrmp_init_open_channel() -> Weight { - Weight::from_parts(1, 0) - } - fn clean_open_channel_requests(_: u32) -> Weight { - Weight::from_parts(1, 0) - } - fn force_open_hrmp_channel(_: u32) -> Weight { - Weight::from_parts(1, 0) - } - fn establish_system_channel() -> Weight { - Weight::from_parts(1, 0) - } - - fn poke_channel_deposits() -> Weight { - Weight::from_parts(1, 0) - } - - fn establish_channel_with_system() -> Weight { - Weight::from_parts(1, 0) - } -} diff --git a/runtime/moonbeam/tests/xcm_mock/statemint_like.rs b/runtime/moonbeam/tests/xcm_mock/statemint_like.rs deleted file mode 100644 index 07d30d8bb18..00000000000 --- a/runtime/moonbeam/tests/xcm_mock/statemint_like.rs +++ /dev/null @@ -1,616 +0,0 @@ -// Copyright 2021 Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -//! Relay chain runtime mock. - -use frame_support::traits::Disabled; -use frame_support::{ - construct_runtime, parameter_types, - traits::{AsEnsureOriginWithArg, Contains, ContainsPair, Everything, Get, Nothing}, - weights::Weight, -}; -use frame_system::{EnsureRoot, EnsureSigned}; - -use sp_core::H256; -use sp_runtime::{ - traits::{ConstU32, Hash, IdentityLookup}, - AccountId32, -}; - -use polkadot_core_primitives::BlockNumber as RelayBlockNumber; - -use polkadot_parachain::primitives::Id as ParaId; -use polkadot_parachain::primitives::Sibling; -use sp_std::convert::TryFrom; -use xcm::latest::prelude::*; -use xcm::VersionedXcm; -use xcm_builder::{ - AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, - AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, - ConvertedConcreteId, EnsureXcmOrigin, FixedRateOfFungible, FixedWeightBounds, FungibleAdapter, - FungiblesAdapter, IsConcrete, NoChecking, ParentAsSuperuser, ParentIsPreset, - RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, -}; -use xcm_executor::{traits::JustTry, Config, XcmExecutor}; -use xcm_simulator::{ - DmpMessageHandlerT as DmpMessageHandler, XcmpMessageFormat, - XcmpMessageHandlerT as XcmpMessageHandler, -}; -pub type AccountId = AccountId32; -pub type Balance = u128; -pub type AssetId = u128; -pub type ReserveId = u128; - -parameter_types! { - pub const BlockHashCount: u32 = 250; -} - -impl frame_system::Config for Runtime { - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type RuntimeTask = RuntimeTask; - type Nonce = u64; - type Block = Block; - type Hash = H256; - type Hashing = ::sp_runtime::traits::BlakeTwo256; - type AccountId = AccountId; - type Lookup = IdentityLookup; - type RuntimeEvent = RuntimeEvent; - 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 = frame_support::traits::ConstU32<16>; - type SingleBlockMigrations = (); - type MultiBlockMigrator = (); - type PreInherents = (); - type PostInherents = (); - type PostTransactions = (); - type ExtensionsWeightInfo = (); -} - -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 RuntimeEvent = RuntimeEvent; - type DustRemoval = (); - type ExistentialDeposit = ExistentialDeposit; - type AccountStore = System; - type WeightInfo = (); - type MaxReserves = MaxReserves; - type ReserveIdentifier = [u8; 8]; - type RuntimeHoldReason = (); - type FreezeIdentifier = (); - type MaxFreezes = (); - type RuntimeFreezeReason = (); - type DoneSlashHandler = (); -} - -// Required for runtime benchmarks -pallet_assets::runtime_benchmarks_enabled! { - pub struct BenchmarkHelper; - impl pallet_assets::BenchmarkHelper for BenchmarkHelper - where - AssetIdParameter: From, - ReserveIdParameter: From, - { - fn create_asset_id_parameter(id: u32) -> AssetIdParameter { - (id as u128).into() - } - fn create_reserve_id_parameter(id: u32) -> ReserveIdParameter { - (id as u128).into() - } - } -} - -parameter_types! { - pub const AssetDeposit: Balance = 0; // 1 UNIT deposit to create asset - pub const ApprovalDeposit: Balance = 0; - pub const AssetsStringLimit: u32 = 50; - /// Key = 32 bytes, Value = 36 bytes (32+1+1+1+1) - // https://github.com/paritytech/substrate/blob/069917b/frame/assets/src/lib.rs#L257L271 - pub const MetadataDepositBase: Balance = 0; - pub const MetadataDepositPerByte: Balance = 0; - pub const ExecutiveBody: BodyId = BodyId::Executive; - pub const AssetAccountDeposit: Balance = 0; -} - -impl pallet_assets::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Balance = Balance; - type AssetId = AssetId; - type Currency = Balances; - type ForceOrigin = EnsureRoot; - type AssetDeposit = AssetDeposit; - type MetadataDepositBase = MetadataDepositBase; - type MetadataDepositPerByte = MetadataDepositPerByte; - type ApprovalDeposit = ApprovalDeposit; - type StringLimit = AssetsStringLimit; - type Freezer = (); - type Extra = (); - type AssetAccountDeposit = AssetAccountDeposit; - type WeightInfo = (); - type RemoveItemsLimit = ConstU32<656>; - type AssetIdParameter = AssetId; - type ReserveData = ReserveId; - type CreateOrigin = AsEnsureOriginWithArg>; - type CallbackHandle = (); - type Holder = (); - pallet_assets::runtime_benchmarks_enabled! { - type BenchmarkHelper = BenchmarkHelper; - } -} - -parameter_types! { - pub const KsmLocation: Location = Location::parent(); - pub const RelayNetwork: NetworkId = NetworkId::Kusama; - pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); - pub UniversalLocation: InteriorLocation = - [GlobalConsensus(RelayNetwork::get()), Parachain(MsgQueue::parachain_id().into())].into(); - pub Local: Location = Here.into(); - pub CheckingAccount: AccountId = PolkadotXcm::check_account(); - pub KsmPerSecond: (xcm::latest::prelude::AssetId, u128, u128) = - (AssetId(KsmLocation::get()), 1, 1); -} - -/// Type for specifying how a `Location` can be converted into an `AccountId`. This is used -/// when determining ownership of accounts for asset transacting and when attempting to use XCM -/// `Transact` in order to determine the dispatch Origin. -pub type LocationToAccountId = ( - // The parent (Relay-chain) origin converts to the default `AccountId`. - ParentIsPreset, - // Sibling parachain origins convert to AccountId via the `ParaId::into`. - SiblingParachainConvertsVia, - // Straight up local `AccountId32` origins just alias directly to `AccountId`. - AccountId32Aliases, -); - -/// Means for transacting the native currency on this chain. -pub type CurrencyTransactor = FungibleAdapter< - // Use this currency: - Balances, - // Use this currency when it is a fungible asset matching the given location or name: - IsConcrete, - // Convert an XCM Location into a local account id: - LocationToAccountId, - // Our chain's account ID type (we can't get away without mentioning it explicitly): - AccountId, - // We don't track any teleports of `Balances`. - (), ->; - -/// Means for transacting assets besides the native currency on this chain. -pub type FungiblesTransactor = FungiblesAdapter< - // Use this fungibles implementation: - Assets, - // Use this currency when it is a fungible asset matching the given location or name: - ConvertedConcreteId< - AssetId, - Balance, - AsPrefixedGeneralIndex, - JustTry, - >, - // Convert an XCM Location into a local account id: - LocationToAccountId, - // Our chain's account ID type (we can't get away without mentioning it explicitly): - AccountId, - // We only want to allow teleports of known assets. We use non-zero issuance as an indication - // that this asset is known. - NoChecking, - // The account to use for tracking teleports. - CheckingAccount, ->; -/// Means for transacting assets on this chain. -pub type AssetTransactors = (CurrencyTransactor, FungiblesTransactor); - -/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, -/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can -/// biases the kind of local `Origin` it will become. -pub type XcmOriginToTransactDispatchOrigin = ( - // Sovereign account converter; this attempts to derive an `AccountId` from the origin location - // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for - // foreign chains who want to have a local sovereign account on this chain which they control. - SovereignSignedViaLocation, - // Native converter for Relay-chain (Parent) location; will convert to a `Relay` origin when - // recognised. - RelayChainAsNative, - // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when - // recognised. - SiblingParachainAsNative, - // Superuser converter for the Relay-chain (Parent) location. This will allow it to issue a - // transaction from the Root origin. - ParentAsSuperuser, - // Native signed account converter; this just converts an `AccountId32` origin into a normal - // `RuntimeOrigin::signed` origin of the same 32-byte value. - SignedAccountId32AsNative, - // Xcm origins can be represented natively under the Xcm pallet's Xcm origin. - pallet_xcm::XcmPassthrough, -); - -parameter_types! { - // One XCM operation is 1_000_000_000 weight - almost certainly a conservative estimate. - pub UnitWeightCost: Weight = Weight::from_parts(100u64, 100u64); - pub const MaxInstructions: u32 = 100; -} - -pub struct ParentOrParentsExecutivePlurality; -impl Contains for ParentOrParentsExecutivePlurality { - fn contains(location: &Location) -> bool { - matches!( - location.unpack(), - (1, []) - | ( - 1, - [Plurality { - id: BodyId::Executive, - .. - }] - ) - ) - } -} - -pub struct ParentOrSiblings; -impl Contains for ParentOrSiblings { - fn contains(location: &Location) -> bool { - matches!(location.unpack(), (1, []) | (1, [_])) - } -} - -pub type Barrier = ( - TakeWeightCredit, - AllowTopLevelPaidExecutionFrom, - // Parent and its exec plurality get free execution - AllowUnpaidExecutionFrom, - // Expected responses are OK. - AllowKnownQueryResponses, - // Subscriptions for version tracking are OK. - AllowSubscriptionsFrom, -); - -parameter_types! { - pub MatcherLocation: Location = Location::here(); - pub const MaxAssetsIntoHolding: u32 = 64; - pub const RelayTokenLocation: Location = Location::parent(); -} - -// Copied from: -// -// https://github.com/paritytech/polkadot-sdk/blob/f4eb41773611008040c9d4d8a8e6b7323eccfca1/cumulus -// /parachains/common/src/xcm_config.rs#L118 -// -// The difference with the original "ConcreteAssetFromSystem" (which is used by AssetHub), -// is that in our tests we only need to check if the asset matches the relay one. -pub struct ConcreteAssetFromRelay(sp_std::marker::PhantomData); -impl> ContainsPair - for ConcreteAssetFromRelay -{ - fn contains(asset: &Asset, origin: &Location) -> bool { - let is_relay = match origin.unpack() { - // The Relay Chain - (1, []) => true, - // Others - _ => false, - }; - asset.id.0 == AssetLocation::get() && is_relay - } -} - -pub type TrustedTeleporters = (ConcreteAssetFromRelay,); - -pub struct XcmConfig; -impl Config for XcmConfig { - type RuntimeCall = RuntimeCall; - type XcmSender = XcmRouter; - type AssetTransactor = AssetTransactors; - type OriginConverter = XcmOriginToTransactDispatchOrigin; - type IsReserve = xcm_primitives::MultiNativeAsset; - type IsTeleporter = TrustedTeleporters; - type UniversalLocation = UniversalLocation; - type Barrier = Barrier; - type Weigher = FixedWeightBounds; - type Trader = FixedRateOfFungible; - type ResponseHandler = PolkadotXcm; - type AssetTrap = PolkadotXcm; - type AssetClaims = PolkadotXcm; - type SubscriptionService = PolkadotXcm; - type CallDispatcher = RuntimeCall; - type AssetLocker = (); - type AssetExchanger = (); - type PalletInstancesInfo = (); - type MaxAssetsIntoHolding = MaxAssetsIntoHolding; - type FeeManager = (); - type MessageExporter = (); - type UniversalAliases = Nothing; - type SafeCallFilter = Everything; - type Aliasers = Nothing; - type TransactionalProcessor = (); - type HrmpNewChannelOpenRequestHandler = (); - type HrmpChannelAcceptedHandler = (); - type HrmpChannelClosingHandler = (); - type XcmRecorder = PolkadotXcm; - type XcmEventEmitter = (); -} - -/// No local origins on this chain are allowed to dispatch XCM sends/executions. -pub type LocalOriginToLocation = SignedToAccountId32; - -pub type XcmRouter = super::ParachainXcmRouter; - -impl pallet_xcm::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type SendXcmOrigin = EnsureXcmOrigin; - type XcmRouter = XcmRouter; - type ExecuteXcmOrigin = EnsureXcmOrigin; - type XcmExecuteFilter = Nothing; - type XcmExecutor = XcmExecutor; - type XcmTeleportFilter = Everything; - type XcmReserveTransferFilter = Everything; - type Weigher = FixedWeightBounds; - type UniversalLocation = UniversalLocation; - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; - type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; - type Currency = Balances; - type CurrencyMatcher = IsConcrete; - type TrustedLockers = (); - type SovereignAccountOf = (); - type MaxLockers = ConstU32<8>; - type WeightInfo = pallet_xcm::TestWeightInfo; - type MaxRemoteLockConsumers = ConstU32<0>; - type RemoteLockConsumerIdentifier = (); - type AdminOrigin = frame_system::EnsureRoot; - type AuthorizedAliasConsideration = Disabled; -} - -impl cumulus_pallet_xcm::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type XcmExecutor = XcmExecutor; -} - -#[frame_support::pallet] -pub mod mock_msg_queue { - use super::*; - use frame_support::pallet_prelude::*; - - #[pallet::config] - pub trait Config: frame_system::Config { - type XcmExecutor: ExecuteXcm; - } - - #[pallet::call] - impl Pallet {} - - #[pallet::pallet] - pub struct Pallet(_); - - #[pallet::storage] - #[pallet::getter(fn parachain_id)] - pub(super) type ParachainId = StorageValue<_, ParaId, 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, InstructionError), - /// 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 = Location::new(1, [Parachain(sender.into())]); - let mut id = [0u8; 32]; - id.copy_from_slice(hash.as_ref()); - match T::XcmExecutor::prepare_and_execute( - location, - xcm, - &mut id, - max_weight, - Weight::zero(), - ) { - Outcome::Error(error) => { - (Err(error.clone()), Event::Fail(Some(hash), error)) - } - Outcome::Complete { used } => (Ok(used), 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 { used, error } => { - (Ok(used), Event::Fail(Some(hash), error)) - } - } - } - Err(()) => ( - Err(InstructionError { - error: XcmError::UnhandledXcmVersion, - index: 0, - }), - 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 mut 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::prepare_and_execute( - Parent, - x, - &mut id, - limit, - Weight::zero(), - ); - - Self::deposit_event(Event::ExecutedDownward(id, outcome)); - } - } - } - limit - } - } -} -impl mock_msg_queue::Config for Runtime { - type XcmExecutor = XcmExecutor; -} - -// Pallet to cover test cases for change https://github.com/paritytech/cumulus/pull/831 -#[frame_support::pallet] -pub mod mock_statemint_prefix { - use super::*; - use frame_support::pallet_prelude::*; - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::call] - impl Pallet {} - - #[pallet::pallet] - #[pallet::without_storage_info] - pub struct Pallet(_); - - #[pallet::storage] - #[pallet::getter(fn current_prefix)] - pub(super) type CurrentPrefix = StorageValue<_, Location, ValueQuery>; - - impl Get for Pallet { - fn get() -> Location { - Self::current_prefix() - } - } - - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event { - // Changed Prefix - PrefixChanged(Location), - } - - impl Pallet { - pub fn set_prefix(prefix: Location) { - CurrentPrefix::::put(&prefix); - Self::deposit_event(Event::PrefixChanged(prefix)); - } - } -} - -impl mock_statemint_prefix::Config for Runtime {} - -type Block = frame_system::mocking::MockBlockU32; -construct_runtime!( - pub enum Runtime { - System: frame_system, - Balances: pallet_balances, - PolkadotXcm: pallet_xcm, - CumulusXcm: cumulus_pallet_xcm, - MsgQueue: mock_msg_queue, - Assets: pallet_assets, - PrefixChanger: mock_statemint_prefix, - - } -); diff --git a/runtime/moonbeam/tests/xcm_tests.rs b/runtime/moonbeam/tests/xcm_tests.rs deleted file mode 100644 index bca604c2738..00000000000 --- a/runtime/moonbeam/tests/xcm_tests.rs +++ /dev/null @@ -1,5279 +0,0 @@ -// Copyright 2019-2025 PureStake Inc. -// This file is part of Moonbeam. - -// Moonbeam 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. - -// Moonbeam 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 Moonbeam. If not, see . - -//! Moonbeam Runtime Xcm Tests - -mod xcm_mock; - -use cumulus_primitives_core::relay_chain::HrmpChannelId; -use frame_support::{ - assert_ok, - traits::{PalletInfo, PalletInfoAccess}, - weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight}, - BoundedVec, -}; -use moonbeam_runtime::xcm_config::AssetType; -use pallet_xcm_transactor::{ - Currency, CurrencyPayment, HrmpInitParams, HrmpOperation, TransactWeights, -}; -use sp_core::ConstU32; -use sp_core::U256; -use sp_runtime::traits::Convert; -use xcm::{ - latest::prelude::{ - AccountId32, AccountKey20, All, Asset, AssetId, Assets as XcmAssets, DepositAsset, - Fungibility, Fungible, GeneralIndex, Junction, Junctions, Limited, Location, OriginKind, - PalletInstance, Parachain, QueryResponse, Reanchorable, Response, WeightLimit, Wild, Xcm, - }, - IntoVersion, VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm, WrapVersion, -}; -use xcm_executor::traits::{ConvertLocation, TransferType}; -use xcm_mock::parachain::{self, EvmForeignAssets, PolkadotXcm, Treasury}; -use xcm_mock::relay_chain; -use xcm_mock::*; -use xcm_primitives::{ - split_location_into_chain_part_and_beneficiary, UtilityEncodeCall, DEFAULT_PROOF_SIZE, -}; -use xcm_simulator::TestExt; - -fn add_supported_asset(asset_type: parachain::AssetType, units_per_second: u128) -> Result<(), ()> { - let parachain::AssetType::Xcm(location_v3) = asset_type; - let VersionedLocation::V5(location_v5) = VersionedLocation::V3(location_v3) - .into_version(xcm::latest::VERSION) - .map_err(|_| ())? - else { - return Err(()); - }; - use frame_support::weights::WeightToFee as _; - let native_amount_per_second: u128 = - ::WeightToFee::weight_to_fee( - &Weight::from_parts( - frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND, - 0, - ), - ) - .try_into() - .map_err(|_| ())?; - let precision_factor = 10u128.pow(pallet_xcm_weight_trader::RELATIVE_PRICE_DECIMALS); - let relative_price: u128 = if units_per_second > 0u128 { - native_amount_per_second - .saturating_mul(precision_factor) - .saturating_div(units_per_second) - } else { - 0u128 - }; - pallet_xcm_weight_trader::SupportedAssets::::insert( - location_v5, - (true, relative_price), - ); - Ok(()) -} - -/// Helper function to set fee per second for an asset location (for compatibility with old tests). -/// Converts fee_per_second to relative_price and adds/edits the asset in the weight-trader. -fn set_fee_per_second_for_location(location: Location, fee_per_second: u128) -> Result<(), ()> { - use moonbeam_tests_primitives::MemoryFeeTrader; - use xcm_primitives::XcmFeeTrader; - - // Configure fees for XcmTransactor via the in-memory fee trader only, so that - // the initial funding XCM transfers stay free and only transactor calls pay fees. - let precision_factor = 10u128.pow(moonbeam_tests_primitives::RELATIVE_PRICE_DECIMALS); - let native_amount_per_second = - frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND as u128; - let relative_price = native_amount_per_second - .saturating_mul(precision_factor) - .checked_div(fee_per_second) - .ok_or(())?; - - ::set_asset_price(location, relative_price).map_err(|_| ()) -} - -fn currency_to_asset(currency_id: parachain::CurrencyId, amount: u128) -> Asset { - Asset { - id: AssetId( - ::CurrencyIdToLocation::convert( - currency_id, - ) - .unwrap(), - ), - fun: Fungibility::Fungible(amount), - } -} - -// Send a relay asset (like DOT) to a parachain A -#[test] -fn receive_relay_asset_from_relay() { - MockNet::reset(); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - // Register relay asset in paraA - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 0u128)); - }); - - // Actually send relay asset to parachain - let dest: Location = AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - - // First send relay chain asset to Parachain - Relay::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([] /* Here */, 123).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - // Verify that parachain received the asset - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(123)) - ); - }); -} - -// Send relay asset (like DOT) back from Parachain A to relaychain -#[test] -fn send_relay_asset_to_relay() { - MockNet::reset(); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - // Register relay asset in paraA - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - // Free execution - assert_ok!(add_supported_asset(source_location, 0u128)); - }); - - let dest: Location = Junction::AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - - // First send relay chain asset to Parachain like in previous test - Relay::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([] /* Here */, 123).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - ParaA::execute_with(|| { - // Free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(123)) - ); - }); - - // Lets gather the balance before sending back money - let mut balance_before_sending = 0; - Relay::execute_with(|| { - balance_before_sending = RelayBalances::free_balance(&RELAYALICE); - }); - - // We now send back some money to the relay - let dest = Location { - parents: 1, - interior: [AccountId32 { - network: None, - id: RELAYALICE.into(), - }] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_id), 123); - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: beneficiary.clone(), - }]); - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from(vec![asset])), - Box::new(TransferType::DestinationReserve), - Box::new(fees_id), - Box::new(TransferType::DestinationReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) - )); - }); - - // The balances in paraAlice should have been substracted - ParaA::execute_with(|| { - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(0)) - ); - }); - - // Balances in the relay should have been received - Relay::execute_with(|| { - // free execution,x full amount received - assert!(RelayBalances::free_balance(&RELAYALICE) > balance_before_sending); - }); -} - -#[test] -fn send_relay_asset_to_para_b() { - MockNet::reset(); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - // Register asset in paraA. Free execution - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.clone().try_into().expect("too long"), - asset_metadata.name.clone().try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 0u128)); - }); - - // Register asset in paraB. Free execution - ParaB::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 0u128)); - }); - - let dest: Location = Junction::AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - Relay::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([] /* Here */, 123).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(123)) - ); - }); - - // Now send relay asset from para A to para B - let dest = Location { - parents: 1, - interior: [ - Parachain(2), - AccountKey20 { - network: None, - key: PARAALICE.into(), - }, - ] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_id), 100); - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: beneficiary.clone(), - }]); - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from(vec![asset])), - Box::new(TransferType::RemoteReserve(Location::parent().into())), - Box::new(fees_id), - Box::new(TransferType::RemoteReserve(Location::parent().into())), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) - )); - }); - - // Para A balances should have been substracted - ParaA::execute_with(|| { - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(23)) - ); - }); - - // Para B balances should have been credited - ParaB::execute_with(|| { - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(100)) - ); - }); -} - -#[test] -fn send_para_a_asset_to_para_b() { - MockNet::reset(); - - // This represents the asset in paraA - let para_a_balances = Location::new(1, [Parachain(1), PalletInstance(1u8)]); - let source_location: AssetType = para_a_balances - .try_into() - .expect("Location convertion to AssetType should succeed"); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"ParaAToken".to_vec(), - symbol: b"ParaA".to_vec(), - decimals: 18, - }; - - // Register asset in paraB. Free execution - ParaB::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 0u128)); - }); - - // Send para A asset from para A to para B - let dest = Location { - parents: 1, - interior: [ - Parachain(2), - AccountKey20 { - network: None, - key: PARAALICE.into(), - }, - ] - .into(), - }; - - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - // Native token is substracted in paraA - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::SelfReserve, 100); - // Free execution, full amount received - assert_ok!(PolkadotXcm::transfer_assets( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedLocation::from(beneficiary)), - Box::new(VersionedAssets::from(asset)), - 0, - WeightLimit::Limited(Weight::from_parts(800000u64, DEFAULT_PROOF_SIZE)) - )); - }); - - ParaA::execute_with(|| { - assert_eq!( - ParaBalances::free_balance(&PARAALICE.into()), - INITIAL_BALANCE - 100 - ); - }); - - // Asset is minted in paraB - ParaB::execute_with(|| { - // Free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(100)) - ); - }); -} - -#[test] -fn send_para_a_asset_from_para_b_to_para_c() { - MockNet::reset(); - - // Represents para A asset - let para_a_balances = Location::new(1, [Parachain(1), PalletInstance(1u8)]); - let source_location: AssetType = para_a_balances - .try_into() - .expect("Location convertion to AssetType should succeed"); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"ParaAToken".to_vec(), - symbol: b"ParaA".to_vec(), - decimals: 18, - }; - - // Register para A asset in parachain B. Free execution - ParaB::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.clone().try_into().expect("too long"), - asset_metadata.name.clone().try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 0u128)); - }); - - // Register para A asset in parachain C. Free execution - ParaC::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 0u128)); - }); - - let dest = Location { - parents: 1, - interior: [ - Parachain(2), - AccountKey20 { - network: None, - key: PARAALICE.into(), - }, - ] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::SelfReserve, 100); - assert_ok!(PolkadotXcm::transfer_assets( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedLocation::from(beneficiary)), - Box::new(VersionedAssets::from(asset)), - 0, - WeightLimit::Limited(Weight::from_parts(80u64, DEFAULT_PROOF_SIZE)) - )); - }); - - // Para A balances have been substracted - ParaA::execute_with(|| { - assert_eq!( - ParaBalances::free_balance(&PARAALICE.into()), - INITIAL_BALANCE - 100 - ); - }); - - // Para B balances have been credited - ParaB::execute_with(|| { - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(100)) - ); - }); - - // Send para A asset from para B to para C - let dest = Location { - parents: 1, - interior: [ - Parachain(3), - AccountKey20 { - network: None, - key: PARAALICE.into(), - }, - ] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - ParaB::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_id), 100); - assert_ok!(PolkadotXcm::transfer_assets( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedLocation::from(beneficiary)), - Box::new(VersionedAssets::from(asset)), - 0, - WeightLimit::Limited(Weight::from_parts(80u64, DEFAULT_PROOF_SIZE)) - )); - }); - - // The message passed through parachainA so we needed to pay since its the native token - ParaC::execute_with(|| { - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(95)) - ); - }); -} - -#[test] -fn send_para_a_asset_to_para_b_and_back_to_para_a() { - MockNet::reset(); - - // para A asset - let para_a_balances = Location::new(1, [Parachain(1), PalletInstance(1u8)]); - let source_location: AssetType = para_a_balances - .try_into() - .expect("Location convertion to AssetType should succeed"); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"ParaAToken".to_vec(), - symbol: b"ParaA".to_vec(), - decimals: 18, - }; - - // Register para A asset in para B - ParaB::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 0u128)); - }); - - // Send para A asset to para B - let dest = Location { - parents: 1, - interior: [ - Parachain(2), - AccountKey20 { - network: None, - key: PARAALICE.into(), - }, - ] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::SelfReserve, 100); - assert_ok!(PolkadotXcm::transfer_assets( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedLocation::from(beneficiary)), - Box::new(VersionedAssets::from(asset)), - 0, - WeightLimit::Limited(Weight::from_parts(80u64, DEFAULT_PROOF_SIZE)) - )); - }); - - // Balances have been substracted - ParaA::execute_with(|| { - assert_eq!( - ParaBalances::free_balance(&PARAALICE.into()), - INITIAL_BALANCE - 100 - ); - }); - - // Para B balances have been credited - ParaB::execute_with(|| { - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(100)) - ); - }); - - // Send back para A asset to para A - let dest = Location { - parents: 1, - interior: [ - Parachain(1), - AccountKey20 { - network: None, - key: PARAALICE.into(), - }, - ] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - ParaB::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_id), 100); - assert_ok!(PolkadotXcm::transfer_assets( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedLocation::from(beneficiary)), - Box::new(VersionedAssets::from(asset)), - 0, - WeightLimit::Limited(Weight::from_parts(80u64, DEFAULT_PROOF_SIZE)) - )); - }); - - ParaA::execute_with(|| { - // Weight used is 4 - assert_eq!( - ParaBalances::free_balance(&PARAALICE.into()), - INITIAL_BALANCE - 4 - ); - }); -} - -#[test] -fn receive_relay_asset_with_trader() { - MockNet::reset(); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - // This time we are gonna put a rather high number of units per second - // we know later we will divide by 1e12 - // Lets put 1e6 as units per second - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset( - source_location.clone(), - 2500000000000u128 - )); - }); - - let dest: Location = Junction::AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - // We are sending 100 tokens from relay. - // Amount spent in fees is Units per second * weight / 1_000_000_000_000 (weight per second) - // weight is 4 since we are executing 4 instructions with a unitweightcost of 1. - // Units per second should be 2_500_000_000_000_000 - // Therefore with no refund, we should receive 10 tokens less - // Native trader fails for this, and we use the asset trader - Relay::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([] /* Here */, 100).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - ParaA::execute_with(|| { - // non-free execution, not full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(90)) - ); - // Fee should have been received by treasury - assert_eq!( - EvmForeignAssets::balance(source_id, Treasury::account_id()), - Ok(U256::from(10)) - ); - }); -} - -#[test] -fn send_para_a_asset_to_para_b_with_trader() { - MockNet::reset(); - - let para_a_balances = Location::new(1, [Parachain(1), PalletInstance(1u8)]); - let source_location: AssetType = para_a_balances - .try_into() - .expect("Location convertion to AssetType should succeed"); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"ParaAToken".to_vec(), - symbol: b"ParaA".to_vec(), - decimals: 18, - }; - - ParaB::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset( - source_location.clone(), - 2500000000000u128 - )); - }); - - let dest = Location { - parents: 1, - interior: [ - Parachain(2), - AccountKey20 { - network: None, - key: PARAALICE.into(), - }, - ] - .into(), - }; - - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - // In destination chain, we only need 4 weight - // We put 10 weight, 6 of which should be refunded and 4 of which should go to treasury - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::SelfReserve, 100); - assert_ok!(PolkadotXcm::transfer_assets( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedLocation::from(beneficiary)), - Box::new(VersionedAssets::from(asset)), - 0, - WeightLimit::Limited(Weight::from_parts(10u64, DEFAULT_PROOF_SIZE)) - )); - }); - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - ParaBalances::free_balance(&PARAALICE.into()), - INITIAL_BALANCE - 100 - ); - }); - - // We are sending 100 tokens from para A. - // Amount spent in fees is Units per second * weight / 1_000_000_000_000 (weight per second) - // weight is 4 since we are executing 4 instructions with a unitweightcost of 1. - // Units per second should be 2_500_000_000_000_000 - // Since we set 10 weight in destination chain, 25 will be charged upfront - // 15 of those will be refunded, while 10 will go to treasury as the true weight used - // will be 4 - ParaB::execute_with(|| { - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(90)) - ); - // Fee should have been received by treasury - assert_eq!( - EvmForeignAssets::balance(source_id, Treasury::account_id()), - Ok(U256::from(10)) - ); - }); -} - -#[test] -fn send_para_a_asset_to_para_b_with_trader_and_fee() { - MockNet::reset(); - - let para_a_balances = Location::new(1, [Parachain(1), PalletInstance(1u8)]); - let source_location: AssetType = para_a_balances - .try_into() - .expect("Location convertion to AssetType should succeed"); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"ParaAToken".to_vec(), - symbol: b"ParaA".to_vec(), - decimals: 18, - }; - - ParaB::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - // With these units per second, 80K weight convrets to 1 asset unit - assert_ok!(add_supported_asset(source_location.clone(), 12500000u128)); - }); - - let dest = Location { - parents: 1, - interior: [ - Parachain(2), - AccountKey20 { - network: None, - key: PARAALICE.into(), - }, - ] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - // we use transfer_with_fee - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::SelfReserve, 100); - let asset_fee = currency_to_asset(parachain::CurrencyId::SelfReserve, 1); - assert_ok!(PolkadotXcm::transfer_assets( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedLocation::from(beneficiary)), - Box::new(VersionedAssets::from(vec![asset_fee, asset])), - 0, - WeightLimit::Limited(Weight::from_parts(800000u64, DEFAULT_PROOF_SIZE)) - )); - }); - ParaA::execute_with(|| { - // 100 tokens transferred plus 1 taken from fees - assert_eq!( - ParaBalances::free_balance(&PARAALICE.into()), - INITIAL_BALANCE - 100 - 1 - ); - }); - - ParaB::execute_with(|| { - // free execution, full amount received because trully the xcm instruction does not cost - // what it is specified - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(101)) - ); - }); -} - -#[test] -fn error_when_not_paying_enough() { - MockNet::reset(); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - let dest: Location = Junction::AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - // This time we are gonna put a rather high number of units per second - // we know later we will divide by 1e12 - // Lets put 1e6 as units per second - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset( - source_location.clone(), - 2500000000000u128 - )); - }); - - // We are sending 100 tokens from relay. - // If we set the dest weight to be 1e7, we know the buy_execution will spend 1e7*1e6/1e12 = 10 - // Therefore with no refund, we should receive 10 tokens less - Relay::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([] /* Here */, 5).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - ParaA::execute_with(|| { - // amount not received as it is not paying enough - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(0)) - ); - }); -} - -#[test] -fn transact_through_derivative_multilocation() { - MockNet::reset(); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 1u128)); - - // Root can set transact info - assert_ok!(XcmTransactor::set_transact_info( - parachain::RuntimeOrigin::root(), - Box::new(xcm::VersionedLocation::from(Location::parent())), - // Relay charges 1000 for every instruction, and we have 3, so 3000 - 3000.into(), - 20000000000.into(), - None - )); - // Root can set transact info - // Set fee per second using weight-trader (replaces old set_fee_per_second) - set_fee_per_second_for_location(Location::parent(), WEIGHT_REF_TIME_PER_SECOND as u128) - .expect("must succeed"); - }); - - // Let's construct the call to know how much weight it is going to require - - let dest: Location = AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - Relay::execute_with(|| { - // 4000000000 transact + 3000 correspond to 4000003000 tokens. 100 more for the transfer call - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([] /* Here */, 4000003100u128).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(4000003100u64)) - ); - }); - - // Register address - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::register( - parachain::RuntimeOrigin::root(), - PARAALICE.into(), - 0, - )); - }); - - // Send to registered address - - let registered_address = derivative_account_id(para_a_account(), 0); - let dest = Location { - parents: 1, - interior: [AccountId32 { - network: None, - id: registered_address.clone().into(), - }] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_id), 100); - // free execution, full amount received - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: beneficiary.clone(), - }]); - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from(vec![asset])), - Box::new(TransferType::DestinationReserve), - Box::new(fees_id), - Box::new(TransferType::DestinationReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) - )); - }); - - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(4000003000u64)) - ); - }); - - // What we will do now is transfer this relay tokens from the derived account to the sovereign - // again - Relay::execute_with(|| { - // free execution,x full amount received - assert!(RelayBalances::free_balance(¶_a_account()) == 4000003000); - }); - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = ::PalletInfo::index::< - relay_chain::Balances, - >() - .unwrap() as u8; - - encoded.push(index); - - // Then call bytes - let mut call_bytes = pallet_balances::Call::::transfer_allow_death { - dest: para_a_account(), - value: 100u32.into(), - } - .encode(); - encoded.append(&mut call_bytes); - - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::transact_through_derivative( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - parachain::MockTransactors::Relay, - 0, - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - Location::parent() - ))), - fee_amount: None - }, - encoded, - // 400000000 + 3000 we should have taken out 4000003000 tokens from the caller - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: None - }, - false - )); - }); - - Relay::execute_with(|| { - // free execution,x full amount received - assert!(RelayBalances::free_balance(¶_a_account()) == 100); - - assert!(RelayBalances::free_balance(®istered_address) == 0); - }); -} - -#[test] -fn transact_through_derivative_with_custom_fee_weight() { - MockNet::reset(); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 1u128)); - }); - - // Let's construct the call to know how much weight it is going to require - - let dest: Location = AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - Relay::execute_with(|| { - // 4000000000 transact + 3000 correspond to 4000003000 tokens. 100 more for the transfer call - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([] /* Here */, 4000003100u128).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(4000003100u64)) - ); - }); - - // Register address - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::register( - parachain::RuntimeOrigin::root(), - PARAALICE.into(), - 0, - )); - }); - - // Send to registered address - - let registered_address = derivative_account_id(para_a_account(), 0); - let dest = Location { - parents: 1, - interior: [AccountId32 { - network: None, - id: registered_address.clone().into(), - }] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_id), 100); - // free execution, full amount received - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: beneficiary.clone(), - }]); - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from(vec![asset])), - Box::new(TransferType::DestinationReserve), - Box::new(fees_id), - Box::new(TransferType::DestinationReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) - )); - }); - - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(4000003000u64)) - ); - }); - - // What we will do now is transfer this relay tokens from the derived account to the sovereign - // again - Relay::execute_with(|| { - // free execution,x full amount received - assert!(RelayBalances::free_balance(¶_a_account()) == 4000003000); - }); - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = ::PalletInfo::index::< - relay_chain::Balances, - >() - .unwrap() as u8; - - encoded.push(index); - - // Then call bytes - let mut call_bytes = pallet_balances::Call::::transfer_allow_death { - dest: para_a_account(), - value: 100u32.into(), - } - .encode(); - encoded.append(&mut call_bytes); - - let overall_weight = 4000003000u64; - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::transact_through_derivative( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - parachain::MockTransactors::Relay, - 0, - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - Location::parent() - ))), - // 1-1 fee weight mapping - fee_amount: Some(overall_weight as u128) - }, - // 4000000000 + 3000 we should have taken out 4000003000 tokens from the caller - encoded, - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: Some(Limited(overall_weight.into())) - }, - false - )); - let event_found: Option = parachain::para_events() - .iter() - .find_map(|event| match event.clone() { - parachain::RuntimeEvent::PolkadotXcm(pallet_xcm::Event::AssetsTrapped { - .. - }) => Some(event.clone()), - _ => None, - }); - // Assert that the events do not contain the assets being trapped - assert!(event_found.is_none()); - }); - - Relay::execute_with(|| { - // free execution,x full amount received - assert!(RelayBalances::free_balance(¶_a_account()) == 100); - - assert!(RelayBalances::free_balance(®istered_address) == 0); - }); -} - -#[test] -fn transact_through_derivative_with_custom_fee_weight_refund() { - MockNet::reset(); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 1u128)); - }); - - // Let's construct the call to know how much weight it is going to require - - let dest: Location = AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - Relay::execute_with(|| { - // 4000000000 transact + 9000 correspond to 4000009000 tokens. 100 more for the transfer call - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([] /* Here */, 4000009100u128).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(4000009100u64)) - ); - }); - - // Register address - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::register( - parachain::RuntimeOrigin::root(), - PARAALICE.into(), - 0, - )); - }); - - // Send to registered address - - let registered_address = derivative_account_id(para_a_account(), 0); - let dest = Location { - parents: 1, - interior: [AccountId32 { - network: None, - id: registered_address.clone().into(), - }] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_id), 100); - // free execution, full amount received - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: beneficiary.clone(), - }]); - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from(vec![asset])), - Box::new(TransferType::DestinationReserve), - Box::new(fees_id), - Box::new(TransferType::DestinationReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) - )); - }); - - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(4000009000u64)) - ); - }); - - // What we will do now is transfer this relay tokens from the derived account to the sovereign - // again - Relay::execute_with(|| { - // free execution,x full amount received - assert!(RelayBalances::free_balance(¶_a_account()) == 4000009000); - }); - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = ::PalletInfo::index::< - relay_chain::Balances, - >() - .unwrap() as u8; - - encoded.push(index); - - // Then call bytes - let mut call_bytes = pallet_balances::Call::::transfer_allow_death { - dest: para_a_account(), - value: 100u32.into(), - } - .encode(); - encoded.append(&mut call_bytes); - - let overall_weight = 4000009000u64; - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::transact_through_derivative( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - parachain::MockTransactors::Relay, - 0, - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - Location::parent() - ))), - // 1-1 fee weight mapping - fee_amount: Some(overall_weight as u128) - }, - encoded, - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: Some(Limited(overall_weight.into())) - }, - true - )); - let event_found: Option = parachain::para_events() - .iter() - .find_map(|event| match event.clone() { - parachain::RuntimeEvent::PolkadotXcm(pallet_xcm::Event::AssetsTrapped { - .. - }) => Some(event.clone()), - _ => None, - }); - // Assert that the events do not contain the assets being trapped - assert!(event_found.is_none()); - }); - - Relay::execute_with(|| { - // free execution,x full amount received - // 4000009000 refunded + 100 transferred = 4000009100 - assert_eq!(RelayBalances::free_balance(¶_a_account()), 4000009100); - assert_eq!(RelayBalances::free_balance(®istered_address), 0); - }); -} - -#[test] -fn transact_through_sovereign() { - MockNet::reset(); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 1u128)); - - // Root can set transact info - assert_ok!(XcmTransactor::set_transact_info( - parachain::RuntimeOrigin::root(), - Box::new(xcm::VersionedLocation::from(Location::parent())), - // Relay charges 1000 for every instruction, and we have 3, so 3000 - 3000.into(), - 20000000000.into(), - None - )); - // Root can set transact info - // Set fee per second using weight-trader (replaces old set_fee_per_second) - set_fee_per_second_for_location(Location::parent(), WEIGHT_REF_TIME_PER_SECOND as u128) - .expect("must succeed"); - }); - - let dest: Location = AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - Relay::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([] /* Here */, 4000003100u128).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(4000003100u64)) - ); - }); - - // Register address - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::register( - parachain::RuntimeOrigin::root(), - PARAALICE.into(), - 0, - )); - }); - - // Send to registered address - let registered_address = derivative_account_id(para_a_account(), 0); - let dest = Location { - parents: 1, - interior: [AccountId32 { - network: None, - id: registered_address.clone().into(), - }] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_id), 100); - // free execution, full amount received - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: beneficiary.clone(), - }]); - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from(vec![asset])), - Box::new(TransferType::DestinationReserve), - Box::new(fees_id), - Box::new(TransferType::DestinationReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) - )); - }); - - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(4000003000u64)) - ); - }); - - // What we will do now is transfer this relay tokens from the derived account to the sovereign - // again - Relay::execute_with(|| { - // free execution,x full amount received - assert!(RelayBalances::free_balance(¶_a_account()) == 4000003000); - 0 - }); - - // We send the xcm transact operation to parent - let dest = Location { - parents: 1, - interior: /* Here */ [].into(), - }; - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = ::PalletInfo::index::< - relay_chain::Balances, - >() - .unwrap() as u8; - - encoded.push(index); - - // Then call bytes - let mut call_bytes = pallet_balances::Call::::transfer_allow_death { - dest: para_a_account(), - value: 100u32.into(), - } - .encode(); - encoded.append(&mut call_bytes); - - // Root can directly pass the execution byes to the sovereign - ParaA::execute_with(|| { - let utility_bytes = ::encode_call( - parachain::MockTransactors::Relay, - xcm_primitives::UtilityAvailableCalls::AsDerivative(0, encoded), - ); - - assert_ok!(XcmTransactor::transact_through_sovereign( - parachain::RuntimeOrigin::root(), - Box::new(xcm::VersionedLocation::from(dest)), - Some(PARAALICE.into()), - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - Location::parent() - ))), - fee_amount: None - }, - utility_bytes, - OriginKind::SovereignAccount, - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: None - }, - false - )); - }); - - Relay::execute_with(|| { - // free execution,x full amount received - assert!(RelayBalances::free_balance(¶_a_account()) == 100); - - assert!(RelayBalances::free_balance(®istered_address) == 0); - }); -} - -#[test] -fn transact_through_sovereign_fee_payer_none() { - MockNet::reset(); - - ParaA::execute_with(|| { - // Root can set transact info - assert_ok!(XcmTransactor::set_transact_info( - parachain::RuntimeOrigin::root(), - Box::new(xcm::VersionedLocation::from(Location::parent())), - // Relay charges 1000 for every instruction, and we have 3, so 3000 - 3000.into(), - 20000000000.into(), - None - )); - // Root can set transact info - // Set fee per second using weight-trader (replaces old set_fee_per_second) - set_fee_per_second_for_location(Location::parent(), WEIGHT_REF_TIME_PER_SECOND as u128) - .expect("must succeed"); - }); - - let derivative_address = derivative_account_id(para_a_account(), 0); - - Relay::execute_with(|| { - // Transfer 100 tokens to derivative_address on the relay - assert_ok!(RelayBalances::transfer_keep_alive( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - derivative_address.clone(), - 100u128 - )); - - // Transfer the XCM execution fee amount to ParaA's sovereign account - assert_ok!(RelayBalances::transfer_keep_alive( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - para_a_account(), - 4000003000u128 - )); - }); - - // Check balances before the transact call - Relay::execute_with(|| { - assert_eq!(RelayBalances::free_balance(¶_a_account()), 4000003000); - assert_eq!(RelayBalances::free_balance(&derivative_address), 100); - assert_eq!(RelayBalances::free_balance(&RELAYBOB), 0); - }); - - // Encode the call. Balances transfer of 100 relay tokens to RELAYBOB - let mut encoded: Vec = Vec::new(); - let index = ::PalletInfo::index::< - relay_chain::Balances, - >() - .unwrap() as u8; - - encoded.push(index); - - let mut call_bytes = pallet_balances::Call::::transfer_allow_death { - dest: RELAYBOB, - value: 100u32.into(), - } - .encode(); - encoded.append(&mut call_bytes); - - // We send the xcm transact operation to parent - let dest = Location { - parents: 1, - interior: /* Here */ [].into(), - }; - - // Root can directly pass the execution byes to the sovereign - ParaA::execute_with(|| { - // The final call will be an AsDerivative using index 0 - let utility_bytes = ::encode_call( - parachain::MockTransactors::Relay, - xcm_primitives::UtilityAvailableCalls::AsDerivative(0, encoded), - ); - - assert_ok!(XcmTransactor::transact_through_sovereign( - parachain::RuntimeOrigin::root(), - Box::new(xcm::VersionedLocation::from(dest)), - // No fee_payer here. The sovereign account will pay the fees on destination. - None, - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - Location::parent() - ))), - fee_amount: None - }, - utility_bytes, - OriginKind::SovereignAccount, - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: None - }, - false - )); - }); - - // Check balances after the transact call are correct - Relay::execute_with(|| { - assert_eq!(RelayBalances::free_balance(¶_a_account()), 0); - assert_eq!(RelayBalances::free_balance(&derivative_address), 0); - assert_eq!(RelayBalances::free_balance(&RELAYBOB), 100); - }); -} - -#[test] -fn transact_through_sovereign_with_custom_fee_weight() { - MockNet::reset(); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 1u128)); - }); - - let dest: Location = AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - Relay::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([] /* Here */, 4000003100u128).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(4000003100u64)) - ); - }); - - // Register address - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::register( - parachain::RuntimeOrigin::root(), - PARAALICE.into(), - 0, - )); - }); - - // Send to registered address - let registered_address = derivative_account_id(para_a_account(), 0); - let dest = Location { - parents: 1, - interior: [AccountId32 { - network: None, - id: registered_address.clone().into(), - }] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_id), 100); - // free execution, full amount received - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: beneficiary.clone(), - }]); - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from(vec![asset])), - Box::new(TransferType::DestinationReserve), - Box::new(fees_id), - Box::new(TransferType::DestinationReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) - )); - }); - - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(4000003000u64)) - ); - }); - - // What we will do now is transfer this relay tokens from the derived account to the sovereign - // again - Relay::execute_with(|| { - // free execution,x full amount received - assert!(RelayBalances::free_balance(¶_a_account()) == 4000003000); - 0 - }); - - // We send the xcm transact operation to parent - let dest = Location { - parents: 1, - interior: /* Here */ [].into(), - }; - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = ::PalletInfo::index::< - relay_chain::Balances, - >() - .unwrap() as u8; - - encoded.push(index); - - // Then call bytes - let mut call_bytes = pallet_balances::Call::::transfer_allow_death { - dest: para_a_account(), - value: 100u32.into(), - } - .encode(); - encoded.append(&mut call_bytes); - - let total_weight = 4000003000u64; - // Root can directly pass the execution byes to the sovereign - ParaA::execute_with(|| { - let utility_bytes = ::encode_call( - parachain::MockTransactors::Relay, - xcm_primitives::UtilityAvailableCalls::AsDerivative(0, encoded), - ); - - assert_ok!(XcmTransactor::transact_through_sovereign( - parachain::RuntimeOrigin::root(), - Box::new(xcm::VersionedLocation::from(dest)), - Some(PARAALICE.into()), - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - Location::parent() - ))), - // 1-1 fee-weight mapping - fee_amount: Some(total_weight as u128) - }, - utility_bytes, - OriginKind::SovereignAccount, - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: Some(Limited(total_weight.into())) - }, - false - )); - }); - - Relay::execute_with(|| { - // free execution,x full amount received - assert!(RelayBalances::free_balance(¶_a_account()) == 100); - - assert!(RelayBalances::free_balance(®istered_address) == 0); - }); -} - -#[test] -fn transact_through_sovereign_with_custom_fee_weight_refund() { - MockNet::reset(); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 1u128)); - }); - - let dest: Location = AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - Relay::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([] /* Here */, 4000009100u128).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(4000009100u64)) - ); - }); - - // Register address - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::register( - parachain::RuntimeOrigin::root(), - PARAALICE.into(), - 0, - )); - }); - - // Send to registered address - let registered_address = derivative_account_id(para_a_account(), 0); - let dest = Location { - parents: 1, - interior: [AccountId32 { - network: None, - id: registered_address.clone().into(), - }] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_id), 100); - // free execution, full amount received - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: beneficiary.clone(), - }]); - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from(vec![asset])), - Box::new(TransferType::DestinationReserve), - Box::new(fees_id), - Box::new(TransferType::DestinationReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) - )); - }); - - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(4000009000u64)) - ); - }); - - // What we will do now is transfer this relay tokens from the derived account to the sovereign - // again - Relay::execute_with(|| { - // free execution,x full amount received - assert!(RelayBalances::free_balance(¶_a_account()) == 4000009000); - 0 - }); - - // We send the xcm transact operation to parent - let dest = Location { - parents: 1, - interior: /* Here */ [].into(), - }; - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = ::PalletInfo::index::< - relay_chain::Balances, - >() - .unwrap() as u8; - - encoded.push(index); - - // Then call bytes - let mut call_bytes = pallet_balances::Call::::transfer_allow_death { - dest: para_a_account(), - value: 100u32.into(), - } - .encode(); - encoded.append(&mut call_bytes); - - let total_weight = 4000009000u64; - // Root can directly pass the execution byes to the sovereign - ParaA::execute_with(|| { - let utility_bytes = ::encode_call( - parachain::MockTransactors::Relay, - xcm_primitives::UtilityAvailableCalls::AsDerivative(0, encoded), - ); - - assert_ok!(XcmTransactor::transact_through_sovereign( - parachain::RuntimeOrigin::root(), - Box::new(xcm::VersionedLocation::from(dest)), - Some(PARAALICE.into()), - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - Location::parent() - ))), - // 1-1 fee-weight mapping - fee_amount: Some(total_weight as u128) - }, - utility_bytes, - OriginKind::SovereignAccount, - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: Some(Limited(total_weight.into())) - }, - true - )); - }); - - Relay::execute_with(|| { - // free execution, full amount received - // 4000009000 refunded + 100 transferred = 4000009100 - assert_eq!(RelayBalances::free_balance(¶_a_account()), 4000009100); - - assert_eq!(RelayBalances::free_balance(®istered_address), 0); - }); -} - -#[test] -fn test_automatic_versioning_on_runtime_upgrade_with_relay() { - MockNet::reset(); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - // register relay asset in parachain A and set XCM version to 1 - ParaA::execute_with(|| { - parachain::XcmVersioner::set_version(1); - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 0u128)); - }); - - let response = Response::Version(2); - let querier: Location = ([]/* Here */).into(); - - // 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: Weight::zero(), - querier: Some(querier), - }]); - // The router is mocked, and we cannot use WrapVersion in ChildParachainRouter. So we will force - // it directly here - // Actually send relay asset to parachain - let dest: Location = AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - - Relay::execute_with(|| { - // This sets the default version, for not known destinations - assert_ok!(RelayChainPalletXcm::force_default_xcm_version( - relay_chain::RuntimeOrigin::root(), - Some(3) - )); - - // Wrap version, which sets VersionedStorage - // This is necessary because the mock router does not use wrap_version, but - // this is not necessary in prod - assert_ok!(::wrap_version( - &Parachain(1).into(), - mock_message - )); - - // Transfer assets. Since it is an unknown destination, it will query for version - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([] /* Here */, 123).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - - // 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::RuntimeEvent = - pallet_xcm::Event::SupportedVersionChanged { - location: Location { - parents: 0, - interior: [Parachain(1)].into(), - }, - version: 1, - } - .into(); - - Relay::execute_with(|| { - // Assert that the events vector contains the version change - assert!(relay_chain::relay_events().contains(&expected_supported_version)); - }); - - // 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().iter().any(|e| matches!( - e, - parachain::RuntimeEvent::PolkadotXcm(pallet_xcm::Event::VersionChangeNotified { - result: 2, - .. - }) - ))); - }); - - // This event should have been seen in the relay - let expected_supported_version_2: relay_chain::RuntimeEvent = - pallet_xcm::Event::SupportedVersionChanged { - location: Location { - parents: 0, - interior: [Parachain(1)].into(), - }, - version: 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 receive_asset_with_no_sufficients_is_possible_for_non_existent_account() { - MockNet::reset(); - - let fresh_account = PARABOB; - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - // register relay asset in parachain A - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 0u128)); - }); - - // Actually send relay asset to parachain - let dest: Location = AccountKey20 { - network: None, - key: fresh_account, - } - .into(); - Relay::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([] /* Here */, 123).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - // parachain should have received assets - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, fresh_account.into()), - Ok(U256::from(123)) - ); - }); -} - -#[test] -fn receive_assets_with_sufficients_true_allows_non_funded_account_to_receive_assets() { - MockNet::reset(); - - let fresh_account = [2u8; 20]; - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - // register relay asset in parachain A - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 0u128)); - }); - - // Actually send relay asset to parachain - let dest: Location = AccountKey20 { - network: None, - key: fresh_account, - } - .into(); - Relay::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([] /* Here */, 123).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - // parachain should have received assets - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, fresh_account.into()), - Ok(U256::from(123)) - ); - }); -} - -#[test] -fn evm_account_receiving_assets_should_handle_sufficients_ref_count() { - MockNet::reset(); - - let mut sufficient_account = [0u8; 20]; - sufficient_account[0..20].copy_from_slice(&evm_account()[..]); - - let evm_account_id = parachain::AccountId::from(sufficient_account); - - // Evm account is self sufficient - ParaA::execute_with(|| { - assert_eq!(parachain::System::account(evm_account_id).sufficients, 1); - }); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - // register relay asset in parachain A - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 0u128)); - }); - - // Actually send relay asset to parachain - let dest: Location = AccountKey20 { - network: None, - key: sufficient_account, - } - .into(); - Relay::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([] /* Here */, 123).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - // Evm account sufficient ref count increased by 1. - ParaA::execute_with(|| { - // TODO: since the suicided logic was introduced an smart contract account - // is not deleted completely until it's data is deleted. Data deletion - // will be implemented in a future release - // assert_eq!(parachain::System::account(evm_account_id).sufficients, 2); - }); - - ParaA::execute_with(|| { - // Remove the account from the evm context. - parachain::EVM::remove_account(&evm_account()); - // Evm account sufficient ref count decreased by 1. - // TODO: since the suicided logic was introduced an smart contract account - // is not deleted completely until it's data is deleted. Data deletion - // will be implemented in a future release - // assert_eq!(parachain::System::account(evm_account_id).sufficients, 1); - }); -} - -#[test] -fn empty_account_should_not_be_reset() { - MockNet::reset(); - - // Test account has nonce 1 on genesis. - let sufficient_account = PARABOB; - - let evm_account_id = parachain::AccountId::from(sufficient_account); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - // register relay asset in parachain A - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 0u128)); - }); - - // Send native token to evm_account - ParaA::execute_with(|| { - assert_ok!(ParaBalances::transfer_allow_death( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - evm_account_id, - 100 - )); - }); - - // Actually send relay asset to parachain - let dest: Location = AccountKey20 { - network: None, - key: sufficient_account, - } - .into(); - Relay::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([] /* Here */, 123).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - ParaA::execute_with(|| { - // Empty the assets from the account. - // As this makes the account go below the `min_balance`, the account is considered dead - // at eyes of pallet-assets, and the consumer reference is decreased by 1 and is now Zero. - // Transfer using EvmForeignAssets - assert_ok!(EvmForeignAssets::transfer( - source_id, - evm_account_id, - PARAALICE.into(), - U256::from(123) - )); - // Verify account asset balance is Zero. - assert_eq!( - EvmForeignAssets::balance(source_id, evm_account_id.into()), - Ok(U256::from(0)) - ); - // Because we no longer have consumer references, we can set the balance to Zero. - // This would reset the account if our ED were to be > than Zero. - assert_ok!(ParaBalances::force_set_balance( - parachain::RuntimeOrigin::root(), - evm_account_id, - 0, - )); - // Verify account native balance is Zero. - assert_eq!(ParaBalances::free_balance(&evm_account_id), 0); - // Remove the account from the evm context. - // This decreases the sufficients reference by 1 and now is Zero. - parachain::EVM::remove_account(&evm_account()); - // Verify reference count. - let account = parachain::System::account(evm_account_id); - assert_eq!(account.sufficients, 0); - assert_eq!(account.consumers, 0); - assert_eq!(account.providers, 1); - // We expect the account to be alive in a Zero ED context. - assert_eq!(parachain::System::account_nonce(evm_account_id), 1); - }); -} - -#[test] -fn test_statemint_like() { - MockNet::reset(); - - let dest_para = Location::new(1, [Parachain(1)]); - - let sov = xcm_builder::SiblingParachainConvertsVia::< - polkadot_parachain::primitives::Sibling, - statemint_like::AccountId, - >::convert_location(&dest_para) - .unwrap(); - - let statemint_asset_a_balances = Location::new( - 1, - [ - Parachain(1000), - PalletInstance(5), - xcm::latest::prelude::GeneralIndex(0u128), - ], - ); - let source_location: AssetType = statemint_asset_a_balances - .try_into() - .expect("Location convertion to AssetType should succeed"); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"StatemintToken".to_vec(), - symbol: b"StatemintToken".to_vec(), - decimals: 12, - }; - - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 0u128)); - }); - - Statemint::execute_with(|| { - // Set new prefix - statemint_like::PrefixChanger::set_prefix( - PalletInstance(::index() as u8).into(), - ); - - assert_ok!(StatemintAssets::create( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - 0, - RELAYALICE, - 1 - )); - - assert_ok!(StatemintAssets::mint( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - 0, - RELAYALICE, - 300000000000000 - )); - - // This is needed, since the asset is created as non-sufficient - assert_ok!(StatemintBalances::transfer_allow_death( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - sov, - 100000000000000 - )); - - // Actually send relay asset to parachain - let dest: Location = AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - - // Send with new prefix - let assets: VersionedAssets = ( - [ - xcm::latest::prelude::PalletInstance( - ::index() as u8, - ), - xcm::latest::prelude::GeneralIndex(0), - ], - 123, - ) - .into(); - let fees_id: VersionedAssetId = AssetId(Location::new( - 0, - [ - xcm::latest::prelude::PalletInstance( - ::index() as u8, - ), - xcm::latest::prelude::GeneralIndex(0), - ], - )) - .into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!( - StatemintChainPalletXcm::transfer_assets_using_type_and_then( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new(assets), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - }); - - ParaA::execute_with(|| { - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(123)) - ); - }); -} - -#[test] - -fn send_statemint_asset_from_para_a_to_statemint_with_relay_fee() { - MockNet::reset(); - - // Relay asset - let relay_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_relay_id: parachain::AssetId = relay_location.clone().into(); - - let relay_asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - // Statemint asset - let statemint_asset = Location::new( - 1, - [ - Parachain(1000u32), - PalletInstance(5u8), - GeneralIndex(10u128), - ], - ); - let statemint_location_asset: AssetType = statemint_asset - .clone() - .try_into() - .expect("Location convertion to AssetType should succeed"); - let source_statemint_asset_id: parachain::AssetId = statemint_location_asset.clone().into(); - - let asset_metadata_statemint_asset = parachain::AssetMetadata { - name: b"USDC".to_vec(), - symbol: b"USDC".to_vec(), - decimals: 12, - }; - - let dest_para = Location::new(1, [Parachain(1)]); - - let sov = xcm_builder::SiblingParachainConvertsVia::< - polkadot_parachain::primitives::Sibling, - statemint_like::AccountId, - >::convert_location(&dest_para) - .unwrap(); - - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = relay_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_relay_id, - source_location_latest, - relay_asset_metadata.decimals, - relay_asset_metadata.symbol.try_into().expect("too long"), - relay_asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(relay_location, 0u128)); - - let parachain::AssetType::Xcm(source_location_v3) = statemint_location_asset.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_statemint_asset_id, - source_location_latest, - asset_metadata_statemint_asset.decimals, - asset_metadata_statemint_asset - .symbol - .try_into() - .expect("too long"), - asset_metadata_statemint_asset - .name - .try_into() - .expect("too long"), - )); - assert_ok!(add_supported_asset(statemint_location_asset, 0u128)); - }); - - let parachain_beneficiary_from_relay: Location = Junction::AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - - // Send relay chain asset to Alice in Parachain A - Relay::execute_with(|| { - let assets: VersionedAssets = ([] /* Here */, 200).into(); - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_from_relay.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(assets), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - Statemint::execute_with(|| { - // Set new prefix - statemint_like::PrefixChanger::set_prefix( - PalletInstance(::index() as u8).into(), - ); - - assert_ok!(StatemintAssets::create( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - 10, - RELAYALICE, - 1 - )); - - assert_ok!(StatemintAssets::mint( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - 10, - RELAYALICE, - 300000000000000 - )); - - // Send some native statemint tokens to sovereign for fees. - // We can't pay fees with USDC as the asset is minted as non-sufficient. - assert_ok!(StatemintBalances::transfer_allow_death( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - sov, - 100000000000000 - )); - - // Send statemint USDC asset to Alice in Parachain A - let parachain_beneficiary_from_statemint: Location = AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - - // Send with new prefix - let assets: VersionedAssets = ( - [ - xcm::latest::prelude::PalletInstance( - ::index() as u8, - ), - GeneralIndex(10), - ], - 125, - ) - .into(); - let fees_id: VersionedAssetId = AssetId(Location::new( - 0, - [ - xcm::latest::prelude::PalletInstance( - ::index() as u8, - ), - GeneralIndex(10), - ], - )) - .into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_from_statemint.clone(), - }]); - assert_ok!( - StatemintChainPalletXcm::transfer_assets_using_type_and_then( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new(assets), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - }); - - let statemint_beneficiary = Location { - parents: 1, - interior: [ - Parachain(1000), - AccountId32 { - network: None, - id: RELAYBOB.into(), - }, - ] - .into(), - }; - - ParaA::execute_with(|| { - // Alice has received 125 USDC - assert_eq!( - EvmForeignAssets::balance(source_statemint_asset_id, PARAALICE.into()), - Ok(U256::from(125)) - ); - - // Alice has received 200 Relay assets - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(200)) - ); - }); - - Statemint::execute_with(|| { - // Check that BOB's balance is empty before the transfer - assert_eq!(StatemintAssets::account_balances(RELAYBOB), vec![]); - }); - let (chain_part, beneficiary) = - split_location_into_chain_part_and_beneficiary(statemint_beneficiary).unwrap(); - // Transfer USDC from Parachain A to Statemint using Relay asset as fee - ParaA::execute_with(|| { - let asset = currency_to_asset( - parachain::CurrencyId::ForeignAsset(source_statemint_asset_id), - 100, - ); - let asset_fee = - currency_to_asset(parachain::CurrencyId::ForeignAsset(source_relay_id), 100); - let assets_to_send: XcmAssets = XcmAssets::from(vec![asset, asset_fee.clone()]); - assert_eq!(assets_to_send.get(0).unwrap(), &asset_fee); - - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: beneficiary.clone(), - }]); - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from(assets_to_send)), - Box::new(TransferType::DestinationReserve), - Box::new(fees_id), - Box::new(TransferType::DestinationReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(80_000_000u64, 100_000u64)) - )); - }); - - ParaA::execute_with(|| { - // Alice has 100 USDC less - assert_eq!( - EvmForeignAssets::balance(source_statemint_asset_id, PARAALICE.into()), - Ok(U256::from(25)) - ); - - // Alice has 100 relay asset less - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(100)) - ); - }); - - Statemint::execute_with(|| { - println!("STATEMINT EVENTS: {:?}", parachain::para_events()); - // Check that BOB received 100 USDC on statemint - assert_eq!(StatemintAssets::account_balances(RELAYBOB), vec![(10, 100)]); - }); -} - -#[test] -fn send_dot_from_moonbeam_to_statemint_via_xtokens_transfer() { - MockNet::reset(); - - // Relay asset - let relay_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_relay_id: parachain::AssetId = relay_location.clone().into(); - - let relay_asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - let dest_para = Location::new(1, [Parachain(1)]); - - let sov = xcm_builder::SiblingParachainConvertsVia::< - polkadot_parachain::primitives::Sibling, - statemint_like::AccountId, - >::convert_location(&dest_para) - .unwrap(); - - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = relay_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_relay_id, - source_location_latest, - relay_asset_metadata.decimals, - relay_asset_metadata.symbol.try_into().expect("too long"), - relay_asset_metadata.name.try_into().expect("too long"), - )); - XcmWeightTrader::set_asset_price(Location::parent(), 0u128); - }); - - let parachain_beneficiary_absolute: Location = Junction::AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - - let statemint_beneficiary_absolute: Location = Junction::AccountId32 { - network: None, - id: RELAYALICE.into(), - } - .into(); - - // First we send relay chain asset to Alice in AssetHub (via teleport) - Relay::execute_with(|| { - assert_ok!(RelayChainPalletXcm::limited_teleport_assets( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1000).into()), - Box::new( - VersionedLocation::from(statemint_beneficiary_absolute) - .clone() - .into() - ), - Box::new(([], 200).into()), - 0, - WeightLimit::Unlimited - )); - }); - - // Send DOTs from AssetHub to ParaA (Moonbeam) - Statemint::execute_with(|| { - // Check Alice received 200 tokens on AssetHub - assert_eq!( - StatemintBalances::free_balance(RELAYALICE), - INITIAL_BALANCE + 200 - ); - - assert_ok!(StatemintBalances::transfer_allow_death( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - sov, - 110000000000000 - )); - - // Now send those tokens to ParaA - let assets: VersionedAssets = (Location::parent(), 200).into(); - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_absolute.clone(), - }]); - assert_ok!( - StatemintChainPalletXcm::transfer_assets_using_type_and_then( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new(assets), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - }); - - ParaA::execute_with(|| { - // Alice should have received the DOTs - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(200)) - ); - }); - - let dest = Location::new( - 1, - [ - Parachain(1000), - AccountId32 { - network: None, - id: RELAYBOB.into(), - }, - ], - ); - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - // Finally we test that we are able to send back the DOTs to AssetHub from the ParaA - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_relay_id), 100); - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: beneficiary.clone(), - }]); - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from(asset)), - Box::new(TransferType::DestinationReserve), - Box::new(fees_id), - Box::new(TransferType::DestinationReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) - )); - - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(100)) - ); - }); - - Statemint::execute_with(|| { - // Check that Bob received the tokens back in AssetHub - assert_eq!( - StatemintBalances::free_balance(RELAYBOB), - INITIAL_BALANCE + 100 - ); - }); - - // Send back tokens from AH to ParaA from Bob's account - Statemint::execute_with(|| { - // Now send those tokens to ParaA - let assets: VersionedAssets = (Location::parent(), 100).into(); - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_absolute.clone(), - }]); - assert_ok!( - StatemintChainPalletXcm::transfer_assets_using_type_and_then( - statemint_like::RuntimeOrigin::signed(RELAYBOB), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new(assets), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - - // 100 DOTs were deducted from Bob's account - assert_eq!(StatemintBalances::free_balance(RELAYBOB), INITIAL_BALANCE); - }); - - ParaA::execute_with(|| { - // Alice should have received 100 DOTs - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(200)) - ); - }); -} - -#[test] -fn send_dot_from_moonbeam_to_statemint_via_xtokens_transfer_with_fee() { - MockNet::reset(); - - // Relay asset - let relay_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_relay_id: parachain::AssetId = relay_location.clone().into(); - - let relay_asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - let dest_para = Location::new(1, [Parachain(1)]); - - let sov = xcm_builder::SiblingParachainConvertsVia::< - polkadot_parachain::primitives::Sibling, - statemint_like::AccountId, - >::convert_location(&dest_para) - .unwrap(); - - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = relay_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_relay_id, - source_location_latest, - relay_asset_metadata.decimals, - relay_asset_metadata.symbol.try_into().expect("too long"), - relay_asset_metadata.name.try_into().expect("too long"), - )); - XcmWeightTrader::set_asset_price(Location::parent(), 0u128); - }); - - let parachain_beneficiary_absolute: Location = Junction::AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - - let statemint_beneficiary_absolute: Location = Junction::AccountId32 { - network: None, - id: RELAYALICE.into(), - } - .into(); - - // First we send relay chain asset to Alice in AssetHub (via teleport) - Relay::execute_with(|| { - assert_ok!(RelayChainPalletXcm::limited_teleport_assets( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1000).into()), - Box::new( - VersionedLocation::from(statemint_beneficiary_absolute) - .clone() - .into() - ), - Box::new(([], 200).into()), - 0, - WeightLimit::Unlimited - )); - }); - - // Send DOTs from AssetHub to ParaA (Moonbeam) - Statemint::execute_with(|| { - // Check Alice received 200 tokens on AssetHub - assert_eq!( - StatemintBalances::free_balance(RELAYALICE), - INITIAL_BALANCE + 200 - ); - - assert_ok!(StatemintBalances::transfer_allow_death( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - sov, - 110000000000000 - )); - - // Now send those tokens to ParaA - let assets: VersionedAssets = (Location::parent(), 200).into(); - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_absolute.clone(), - }]); - assert_ok!( - StatemintChainPalletXcm::transfer_assets_using_type_and_then( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new(assets), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - }); - - ParaA::execute_with(|| { - // Alice should have received the DOTs - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(200)) - ); - }); - - let dest = Location::new( - 1, - [ - Parachain(1000), - AccountId32 { - network: None, - id: RELAYBOB.into(), - }, - ], - ); - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - // Finally we test that we are able to send back the DOTs to AssetHub from the ParaA - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_relay_id), 100); - let asset_fee = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_relay_id), 10); - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: beneficiary.clone(), - }]); - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from(vec![asset_fee, asset])), - Box::new(TransferType::DestinationReserve), - Box::new(fees_id), - Box::new(TransferType::DestinationReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) - )); - - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(90)) - ); - }); - - Statemint::execute_with(|| { - // Free execution: check that Bob received the tokens back in AssetHub - assert_eq!( - StatemintBalances::free_balance(RELAYBOB), - INITIAL_BALANCE + 110 - ); - }); - - // Send back tokens from AH to ParaA from Bob's account - Statemint::execute_with(|| { - // Now send those tokens to ParaA - let assets: VersionedAssets = (Location::parent(), 100).into(); - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_absolute.clone(), - }]); - assert_ok!( - StatemintChainPalletXcm::transfer_assets_using_type_and_then( - statemint_like::RuntimeOrigin::signed(RELAYBOB), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new(assets), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - - // 100 DOTs were deducted from Bob's account - assert_eq!( - StatemintBalances::free_balance(RELAYBOB), - INITIAL_BALANCE + 10 - ); - }); - - ParaA::execute_with(|| { - // Alice should have received 100 DOTs - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(190)) - ); - }); -} - -#[test] -fn send_dot_from_moonbeam_to_statemint_via_xtokens_transfer_multiasset() { - MockNet::reset(); - - // Relay asset - let relay_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_relay_id: parachain::AssetId = relay_location.clone().into(); - - let relay_asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - let dest_para = Location::new(1, [Parachain(1)]); - - let sov = xcm_builder::SiblingParachainConvertsVia::< - polkadot_parachain::primitives::Sibling, - statemint_like::AccountId, - >::convert_location(&dest_para) - .unwrap(); - - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = relay_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_relay_id, - source_location_latest, - relay_asset_metadata.decimals, - relay_asset_metadata.symbol.try_into().expect("too long"), - relay_asset_metadata.name.try_into().expect("too long"), - )); - XcmWeightTrader::set_asset_price(Location::parent(), 0u128); - }); - - let parachain_beneficiary_absolute: Location = Junction::AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - - let statemint_beneficiary_absolute: Location = Junction::AccountId32 { - network: None, - id: RELAYALICE.into(), - } - .into(); - - // First we send relay chain asset to Alice in AssetHub (via teleport) - Relay::execute_with(|| { - assert_ok!(RelayChainPalletXcm::limited_teleport_assets( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1000).into()), - Box::new( - VersionedLocation::from(statemint_beneficiary_absolute) - .clone() - .into() - ), - Box::new(([], 200).into()), - 0, - WeightLimit::Unlimited - )); - }); - - // Send DOTs from AssetHub to ParaA (Moonbeam) - Statemint::execute_with(|| { - // Check Alice received 200 tokens on AssetHub - assert_eq!( - StatemintBalances::free_balance(RELAYALICE), - INITIAL_BALANCE + 200 - ); - - assert_ok!(StatemintBalances::transfer_allow_death( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - sov, - 110000000000000 - )); - - // Now send those tokens to ParaA - let assets: VersionedAssets = (Location::parent(), 200).into(); - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_absolute.clone(), - }]); - assert_ok!( - StatemintChainPalletXcm::transfer_assets_using_type_and_then( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new(assets), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - }); - - ParaA::execute_with(|| { - // Alice should have received the DOTs - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(200)) - ); - }); - - let dest = Location::new( - 1, - [ - Parachain(1000), - AccountId32 { - network: None, - id: RELAYBOB.into(), - }, - ], - ); - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - // Finally we test that we are able to send back the DOTs to AssetHub from the ParaA - ParaA::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: beneficiary.clone(), - }]); - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from((Location::parent(), 100))), - Box::new(TransferType::DestinationReserve), - Box::new(fees_id), - Box::new(TransferType::DestinationReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) - )); - - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(100)) - ); - }); - - Statemint::execute_with(|| { - // Check that Bob received the tokens back in AssetHub - assert_eq!( - StatemintBalances::free_balance(RELAYBOB), - INITIAL_BALANCE + 100 - ); - }); - - // Send back tokens from AH to ParaA from Bob's account - Statemint::execute_with(|| { - // Now send those tokens to ParaA - let assets: VersionedAssets = (Location::parent(), 100).into(); - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_absolute.clone(), - }]); - assert_ok!( - StatemintChainPalletXcm::transfer_assets_using_type_and_then( - statemint_like::RuntimeOrigin::signed(RELAYBOB), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new(assets), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - - // 100 DOTs were deducted from Bob's account - assert_eq!(StatemintBalances::free_balance(RELAYBOB), INITIAL_BALANCE); - }); - - ParaA::execute_with(|| { - // Alice should have received 100 DOTs - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(200)) - ); - }); -} - -#[test] -fn send_dot_from_moonbeam_to_statemint_via_xtokens_transfer_multicurrencies() { - MockNet::reset(); - - // Relay asset - let relay_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_relay_id: parachain::AssetId = relay_location.clone().into(); - - let relay_asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - // Statemint asset - let statemint_asset = Location::new( - 1, - [ - Parachain(1000u32), - PalletInstance(5u8), - GeneralIndex(10u128), - ], - ); - let statemint_location_asset: AssetType = statemint_asset - .clone() - .try_into() - .expect("Location convertion to AssetType should succeed"); - let source_statemint_asset_id: parachain::AssetId = statemint_location_asset.clone().into(); - - let asset_metadata_statemint_asset = parachain::AssetMetadata { - name: b"USDC".to_vec(), - symbol: b"USDC".to_vec(), - decimals: 12, - }; - - let dest_para = Location::new(1, [Parachain(1)]); - - let sov = xcm_builder::SiblingParachainConvertsVia::< - polkadot_parachain::primitives::Sibling, - statemint_like::AccountId, - >::convert_location(&dest_para) - .unwrap(); - - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = relay_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_relay_id, - source_location_latest, - relay_asset_metadata.decimals, - relay_asset_metadata.symbol.try_into().expect("too long"), - relay_asset_metadata.name.try_into().expect("too long"), - )); - XcmWeightTrader::set_asset_price(Location::parent(), 0u128); - - let parachain::AssetType::Xcm(source_location_v3) = statemint_location_asset.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_statemint_asset_id, - source_location_latest, - asset_metadata_statemint_asset.decimals, - asset_metadata_statemint_asset - .symbol - .try_into() - .expect("too long"), - asset_metadata_statemint_asset - .name - .try_into() - .expect("too long"), - )); - XcmWeightTrader::set_asset_price(statemint_asset.clone(), 0u128); - }); - - let parachain_beneficiary_absolute: Location = Junction::AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - - let statemint_beneficiary_absolute: Location = Junction::AccountId32 { - network: None, - id: RELAYALICE.into(), - } - .into(); - - // First we send relay chain asset to Alice in AssetHub (via teleport) - Relay::execute_with(|| { - assert_ok!(RelayChainPalletXcm::limited_teleport_assets( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1000).into()), - Box::new( - VersionedLocation::from(statemint_beneficiary_absolute) - .clone() - .into() - ), - Box::new(([], 200).into()), - 0, - WeightLimit::Unlimited - )); - }); - - // Send DOTs and USDC from AssetHub to ParaA (Moonbeam) - Statemint::execute_with(|| { - // Check Alice received 200 tokens on AssetHub - assert_eq!( - StatemintBalances::free_balance(RELAYALICE), - INITIAL_BALANCE + 200 - ); - - assert_ok!(StatemintBalances::transfer_allow_death( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - sov, - 110000000000000 - )); - - statemint_like::PrefixChanger::set_prefix( - PalletInstance(::index() as u8).into(), - ); - - assert_ok!(StatemintAssets::create( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - 10, - RELAYALICE, - 1 - )); - - assert_ok!(StatemintAssets::mint( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - 10, - RELAYALICE, - 300000000000000 - )); - - // Now send relay tokens to ParaA - let assets: VersionedAssets = (Location::parent(), 200).into(); - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_absolute.clone(), - }]); - assert_ok!( - StatemintChainPalletXcm::transfer_assets_using_type_and_then( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new(assets), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - - // Send USDC - let assets: VersionedAssets = ( - [ - xcm::latest::prelude::PalletInstance( - ::index() as u8, - ), - GeneralIndex(10), - ], - 125, - ) - .into(); - let fees_id: VersionedAssetId = AssetId(Location::new( - 0, - [ - xcm::latest::prelude::PalletInstance( - ::index() as u8, - ), - GeneralIndex(10), - ], - )) - .into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_absolute.clone(), - }]); - assert_ok!( - StatemintChainPalletXcm::transfer_assets_using_type_and_then( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new(assets), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - }); - - ParaA::execute_with(|| { - // Alice should have received the DOTs - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(200)) - ); - - // Alice has received 125 USDC - assert_eq!( - EvmForeignAssets::balance(source_statemint_asset_id, PARAALICE.into()), - Ok(U256::from(125)) - ); - }); - - let dest = Location::new( - 1, - [ - Parachain(1000), - AccountId32 { - network: None, - id: RELAYBOB.into(), - }, - ], - ); - - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - // Finally we test that we are able to send back the DOTs to AssetHub from the ParaA - ParaA::execute_with(|| { - let asset_1 = currency_to_asset( - parachain::CurrencyId::ForeignAsset(source_statemint_asset_id), - 100, - ); - let asset_2 = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_relay_id), 100); - let assets_to_send = vec![asset_1, asset_2]; - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: beneficiary.clone(), - }]); - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from(assets_to_send)), - Box::new(TransferType::DestinationReserve), - Box::new(fees_id), - Box::new(TransferType::DestinationReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(80_000_000u64, 100_000u64)) - )); - - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(100)) - ); - }); - - Statemint::execute_with(|| { - // Check that Bob received relay tokens back in AssetHub - assert_eq!( - StatemintBalances::free_balance(RELAYBOB), - INITIAL_BALANCE + 100 - ); - - // Check that BOB received 100 USDC on AssetHub - assert_eq!(StatemintAssets::account_balances(RELAYBOB), vec![(10, 100)]); - }); - - // Send back tokens from AH to ParaA from Bob's account - Statemint::execute_with(|| { - let bob_previous_balance = StatemintBalances::free_balance(RELAYBOB); - - // Now send those tokens to ParaA - let assets: VersionedAssets = (Location::parent(), 100).into(); - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_absolute.clone(), - }]); - assert_ok!( - StatemintChainPalletXcm::transfer_assets_using_type_and_then( - statemint_like::RuntimeOrigin::signed(RELAYBOB), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new(assets), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - - // 100 DOTs were deducted from Bob's account - assert_eq!( - StatemintBalances::free_balance(RELAYBOB), - bob_previous_balance - 100 - ); - }); - - ParaA::execute_with(|| { - // Alice should have received 100 DOTs - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(200)) - ); - }); -} - -#[test] -fn send_dot_from_moonbeam_to_statemint_via_xtokens_transfer_multiassets() { - MockNet::reset(); - - // Relay asset - let relay_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_relay_id: parachain::AssetId = relay_location.clone().into(); - - let relay_asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - // Statemint asset - let statemint_asset = Location::new( - 1, - [ - Parachain(1000u32), - PalletInstance(5u8), - GeneralIndex(10u128), - ], - ); - let statemint_location_asset: AssetType = statemint_asset - .clone() - .try_into() - .expect("Location convertion to AssetType should succeed"); - let source_statemint_asset_id: parachain::AssetId = statemint_location_asset.clone().into(); - - let asset_metadata_statemint_asset = parachain::AssetMetadata { - name: b"USDC".to_vec(), - symbol: b"USDC".to_vec(), - decimals: 12, - }; - - let dest_para = Location::new(1, [Parachain(1)]); - - let sov = xcm_builder::SiblingParachainConvertsVia::< - polkadot_parachain::primitives::Sibling, - statemint_like::AccountId, - >::convert_location(&dest_para) - .unwrap(); - - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = relay_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_relay_id, - source_location_latest, - relay_asset_metadata.decimals, - relay_asset_metadata.symbol.try_into().expect("too long"), - relay_asset_metadata.name.try_into().expect("too long"), - )); - XcmWeightTrader::set_asset_price(Location::parent(), 0u128); - - let parachain::AssetType::Xcm(source_location_v3) = statemint_location_asset.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_statemint_asset_id, - source_location_latest, - asset_metadata_statemint_asset.decimals, - asset_metadata_statemint_asset - .symbol - .try_into() - .expect("too long"), - asset_metadata_statemint_asset - .name - .try_into() - .expect("too long"), - )); - XcmWeightTrader::set_asset_price(statemint_asset.clone(), 0u128); - }); - - let parachain_beneficiary_absolute: Location = Junction::AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - - let statemint_beneficiary_absolute: Location = Junction::AccountId32 { - network: None, - id: RELAYALICE.into(), - } - .into(); - - // First we send relay chain asset to Alice in AssetHub (via teleport) - Relay::execute_with(|| { - assert_ok!(RelayChainPalletXcm::limited_teleport_assets( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1000).into()), - Box::new( - VersionedLocation::from(statemint_beneficiary_absolute) - .clone() - .into() - ), - Box::new(([], 200).into()), - 0, - WeightLimit::Unlimited - )); - }); - - // Send DOTs and USDC from AssetHub to ParaA (Moonbeam) - Statemint::execute_with(|| { - // Check Alice received 200 tokens on AssetHub - assert_eq!( - StatemintBalances::free_balance(RELAYALICE), - INITIAL_BALANCE + 200 - ); - - assert_ok!(StatemintBalances::transfer_allow_death( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - sov, - 110000000000000 - )); - - statemint_like::PrefixChanger::set_prefix( - PalletInstance(::index() as u8).into(), - ); - - assert_ok!(StatemintAssets::create( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - 10, - RELAYALICE, - 1 - )); - - assert_ok!(StatemintAssets::mint( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - 10, - RELAYALICE, - 300000000000000 - )); - - // Now send relay tokens to ParaA - let assets: VersionedAssets = (Location::parent(), 200).into(); - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_absolute.clone(), - }]); - assert_ok!( - StatemintChainPalletXcm::transfer_assets_using_type_and_then( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new(assets), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - - // Send USDC - let assets: VersionedAssets = ( - [ - xcm::latest::prelude::PalletInstance( - ::index() as u8, - ), - GeneralIndex(10), - ], - 125, - ) - .into(); - let fees_id: VersionedAssetId = AssetId(Location::new( - 0, - [ - xcm::latest::prelude::PalletInstance( - ::index() as u8, - ), - GeneralIndex(10), - ], - )) - .into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_absolute.clone(), - }]); - assert_ok!( - StatemintChainPalletXcm::transfer_assets_using_type_and_then( - statemint_like::RuntimeOrigin::signed(RELAYALICE), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new(assets), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - }); - - ParaA::execute_with(|| { - // Alice should have received the DOTs - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(200)) - ); - - // Alice has received 125 USDC - assert_eq!( - EvmForeignAssets::balance(source_statemint_asset_id, PARAALICE.into()), - Ok(U256::from(125)) - ); - }); - - let dest = Location::new( - 1, - [ - Parachain(1000), - AccountId32 { - network: None, - id: RELAYBOB.into(), - }, - ], - ); - - let statemint_asset_to_send = Asset { - id: AssetId(statemint_asset), - fun: Fungible(100), - }; - - let relay_asset_to_send = Asset { - id: AssetId(Location::parent()), - fun: Fungible(100), - }; - - let assets_to_send: XcmAssets = - XcmAssets::from(vec![statemint_asset_to_send, relay_asset_to_send.clone()]); - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - // For some reason the order of the assets is inverted when creating the array above. - // We need to use relay asset for fees, so we pick index 0. - assert_eq!(assets_to_send.get(0).unwrap(), &relay_asset_to_send); - - // Finally we test that we are able to send back the DOTs to AssetHub from the ParaA - ParaA::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: beneficiary.clone(), - }]); - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from(assets_to_send)), - Box::new(TransferType::DestinationReserve), - Box::new(fees_id), - Box::new(TransferType::DestinationReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(80_000_000u64, 100_000u64)) - )); - - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(100)) - ); - }); - - Statemint::execute_with(|| { - // Check that Bob received relay tokens back in AssetHub - assert_eq!( - StatemintBalances::free_balance(RELAYBOB), - INITIAL_BALANCE + 100 - ); - - // Check that BOB received 100 USDC on AssetHub - assert_eq!(StatemintAssets::account_balances(RELAYBOB), vec![(10, 100)]); - }); - - // Send back tokens from AH to ParaA from Bob's account - Statemint::execute_with(|| { - let bob_previous_balance = StatemintBalances::free_balance(RELAYBOB); - - // Now send those tokens to ParaA - let assets: VersionedAssets = (Location::parent(), 100).into(); - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_absolute.clone(), - }]); - assert_ok!( - StatemintChainPalletXcm::transfer_assets_using_type_and_then( - statemint_like::RuntimeOrigin::signed(RELAYBOB), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new(assets), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - - // 100 DOTs were deducted from Bob's account - assert_eq!( - StatemintBalances::free_balance(RELAYBOB), - bob_previous_balance - 100 - ); - }); - - ParaA::execute_with(|| { - // Alice should have received 100 DOTs - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(200)) - ); - }); -} - -#[test] -fn transact_through_signed_multilocation() { - MockNet::reset(); - let mut ancestry = Location::parent(); - - ParaA::execute_with(|| { - // Root can set transact info - assert_ok!(XcmTransactor::set_transact_info( - parachain::RuntimeOrigin::root(), - Box::new(xcm::VersionedLocation::from(Location::parent())), - // Relay charges 1000 for every instruction, and we have 3, so 3000 - 3000.into(), - 20000000000.into(), - // 4 instructions in transact through signed - Some(4000.into()) - )); - // Root can set transact info - // Set fee per second using weight-trader (replaces old set_fee_per_second) - set_fee_per_second_for_location(Location::parent(), WEIGHT_REF_TIME_PER_SECOND as u128) - .expect("must succeed"); - ancestry = parachain::UniversalLocation::get().into(); - }); - - // Let's construct the Junction that we will append with DescendOrigin - let signed_origin: Junctions = [AccountKey20 { - network: None, - key: PARAALICE, - }] - .into(); - - let mut descend_origin_multilocation = parachain::SelfLocation::get(); - descend_origin_multilocation - .append_with(signed_origin) - .unwrap(); - - // To convert it to what the relay will see instead of us - descend_origin_multilocation - .reanchor(&Location::parent(), &ancestry.interior) - .unwrap(); - - let derived = xcm_builder::Account32Hash::< - relay_chain::KusamaNetwork, - relay_chain::AccountId, - >::convert_location(&descend_origin_multilocation) - .unwrap(); - - Relay::execute_with(|| { - // free execution, full amount received - assert_ok!(RelayBalances::transfer_allow_death( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - derived.clone(), - 4000004100u128, - )); - // derived account has all funds - assert!(RelayBalances::free_balance(&derived) == 4000004100); - // sovereign account has 0 funds - assert!(RelayBalances::free_balance(¶_a_account()) == 0); - }); - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = ::PalletInfo::index::< - relay_chain::Balances, - >() - .unwrap() as u8; - - encoded.push(index); - - // Then call bytes - let mut call_bytes = pallet_balances::Call::::transfer_allow_death { - // 100 to sovereign - dest: para_a_account(), - value: 100u32.into(), - } - .encode(); - encoded.append(&mut call_bytes); - - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::transact_through_signed( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(xcm::VersionedLocation::from(Location::parent())), - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - Location::parent() - ))), - fee_amount: None - }, - encoded, - // 4000000000 for transfer + 4000 for XCM - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: None - }, - false - )); - }); - - Relay::execute_with(|| { - assert!(RelayBalances::free_balance(¶_a_account()) == 100); - - assert!(RelayBalances::free_balance(&derived) == 0); - }); -} - -#[test] -fn transact_through_signed_multilocation_custom_fee_and_weight() { - MockNet::reset(); - let mut ancestry = Location::parent(); - - ParaA::execute_with(|| { - ancestry = parachain::UniversalLocation::get().into(); - }); - - // Let's construct the Junction that we will append with DescendOrigin - let signed_origin: Junctions = [AccountKey20 { - network: None, - key: PARAALICE, - }] - .into(); - - let mut descend_origin_multilocation = parachain::SelfLocation::get(); - descend_origin_multilocation - .append_with(signed_origin) - .unwrap(); - - // To convert it to what the relay will see instead of us - descend_origin_multilocation - .reanchor(&Location::parent(), &ancestry.interior) - .unwrap(); - - let derived = xcm_builder::Account32Hash::< - relay_chain::KusamaNetwork, - relay_chain::AccountId, - >::convert_location(&descend_origin_multilocation) - .unwrap(); - - Relay::execute_with(|| { - // free execution, full amount received - assert_ok!(RelayBalances::transfer_allow_death( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - derived.clone(), - 4000004100u128, - )); - // derived account has all funds - assert!(RelayBalances::free_balance(&derived) == 4000004100); - // sovereign account has 0 funds - assert!(RelayBalances::free_balance(¶_a_account()) == 0); - }); - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = ::PalletInfo::index::< - relay_chain::Balances, - >() - .unwrap() as u8; - - encoded.push(index); - - // Then call bytes - let mut call_bytes = pallet_balances::Call::::transfer_allow_death { - // 100 to sovereign - dest: para_a_account(), - value: 100u32.into(), - } - .encode(); - encoded.append(&mut call_bytes); - - let total_weight = 4000004000u64; - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::transact_through_signed( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(xcm::VersionedLocation::from(Location::parent())), - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - Location::parent() - ))), - fee_amount: Some(total_weight as u128) - }, - encoded, - // 4000000000 for transfer + 4000 for XCM - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: Some(Limited(total_weight.into())) - }, - false - )); - }); - - Relay::execute_with(|| { - assert!(RelayBalances::free_balance(¶_a_account()) == 100); - - assert!(RelayBalances::free_balance(&derived) == 0); - }); -} - -#[test] -fn transact_through_signed_multilocation_custom_fee_and_weight_refund() { - MockNet::reset(); - let mut ancestry = Location::parent(); - - ParaA::execute_with(|| { - ancestry = parachain::UniversalLocation::get().into(); - }); - - // Let's construct the Junction that we will append with DescendOrigin - let signed_origin: Junctions = [AccountKey20 { - network: None, - key: PARAALICE, - }] - .into(); - - let mut descend_origin_multilocation = parachain::SelfLocation::get(); - descend_origin_multilocation - .append_with(signed_origin) - .unwrap(); - - // To convert it to what the relay will see instead of us - descend_origin_multilocation - .reanchor(&Location::parent(), &ancestry.interior) - .unwrap(); - - let derived = xcm_builder::Account32Hash::< - relay_chain::KusamaNetwork, - relay_chain::AccountId, - >::convert_location(&descend_origin_multilocation) - .unwrap(); - - Relay::execute_with(|| { - // free execution, full amount received - assert_ok!(RelayBalances::transfer_allow_death( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - derived.clone(), - 4000009100u128, - )); - // derived account has all funds - assert!(RelayBalances::free_balance(&derived) == 4000009100); - // sovereign account has 0 funds - assert!(RelayBalances::free_balance(¶_a_account()) == 0); - }); - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = ::PalletInfo::index::< - relay_chain::Balances, - >() - .unwrap() as u8; - - encoded.push(index); - - // Then call bytes - let mut call_bytes = pallet_balances::Call::::transfer_allow_death { - // 100 to sovereign - dest: para_a_account(), - value: 100u32.into(), - } - .encode(); - encoded.append(&mut call_bytes); - - let total_weight = 4000009000u64; - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::transact_through_signed( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(xcm::VersionedLocation::from(Location::parent())), - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - Location::parent() - ))), - fee_amount: Some(total_weight as u128) - }, - encoded, - // 4000000000 for transfer + 9000 for XCM - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: Some(Limited(total_weight.into())) - }, - true - )); - }); - - Relay::execute_with(|| { - // 100 transferred - assert_eq!(RelayBalances::free_balance(¶_a_account()), 100); - - // 4000009000 refunded - assert_eq!(RelayBalances::free_balance(&derived), 4000009000); - }); -} - -#[test] -fn transact_through_signed_multilocation_para_to_para() { - MockNet::reset(); - let mut ancestry = Location::parent(); - - let para_b_location = Location::new(1, [Parachain(2)]); - - let para_b_balances = Location::new(1, [Parachain(2), PalletInstance(1u8)]); - - ParaA::execute_with(|| { - // Root can set transact info - assert_ok!(XcmTransactor::set_transact_info( - parachain::RuntimeOrigin::root(), - // ParaB - Box::new(xcm::VersionedLocation::from(para_b_location.clone())), - // Para charges 1000 for every instruction, and we have 3, so 3 - 3.into(), - 20000000000.into(), - // 4 instructions in transact through signed - Some(4.into()) - )); - // Root can set transact info - // Set fee per second using weight-trader (replaces old set_fee_per_second) - set_fee_per_second_for_location( - para_b_balances.clone(), - parachain::ParaTokensPerSecond::get(), - ) - .expect("must succeed"); - ancestry = parachain::UniversalLocation::get().into(); - }); - - // Let's construct the Junction that we will append with DescendOrigin - let signed_origin: Junctions = [AccountKey20 { - network: None, - key: PARAALICE, - }] - .into(); - - let mut descend_origin_location = parachain::SelfLocation::get(); - descend_origin_location.append_with(signed_origin).unwrap(); - - // To convert it to what the paraB will see instead of us - descend_origin_location - .reanchor(¶_b_location, &ancestry.interior) - .unwrap(); - - let derived = xcm_builder::HashedDescription::< - parachain::AccountId, - xcm_builder::DescribeFamily, - >::convert_location(&descend_origin_location) - .unwrap(); - - ParaB::execute_with(|| { - // free execution, full amount received - assert_ok!(ParaBalances::transfer_allow_death( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - derived.clone(), - 4000000104u128, - )); - // derived account has all funds - assert!(ParaBalances::free_balance(&derived) == 4000000104); - // sovereign account has 0 funds - assert!(ParaBalances::free_balance(¶_a_account_20()) == 0); - }); - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = - ::PalletInfo::index::() - .unwrap() as u8; - - encoded.push(index); - - // Then call bytes - let mut call_bytes = pallet_balances::Call::::transfer_allow_death { - // 100 to sovereign - dest: para_a_account_20(), - value: 100u32.into(), - } - .encode(); - encoded.append(&mut call_bytes); - - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::transact_through_signed( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(xcm::VersionedLocation::from(para_b_location)), - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - para_b_balances - ))), - fee_amount: None - }, - encoded, - // 4000000000 for transfer + 4000 for XCM - // 1-1 to fee - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: None - }, - false - )); - }); - - ParaB::execute_with(|| { - assert!(ParaBalances::free_balance(&derived) == 0); - - assert!(ParaBalances::free_balance(¶_a_account_20()) == 100); - }); -} - -#[test] -fn transact_through_signed_multilocation_para_to_para_refund() { - MockNet::reset(); - let mut ancestry = Location::parent(); - - let para_b_location = Location::new(1, [Parachain(2)]); - - let para_b_balances = Location::new(1, [Parachain(2), PalletInstance(1u8)]); - - ParaA::execute_with(|| { - // Set fee per second using weight-trader (replaces old set_fee_per_second) - set_fee_per_second_for_location( - para_b_balances.clone(), - parachain::ParaTokensPerSecond::get(), - ) - .expect("must succeed"); - ancestry = parachain::UniversalLocation::get().into(); - }); - - // Let's construct the Junction that we will append with DescendOrigin - let signed_origin: Junctions = [AccountKey20 { - network: None, - key: PARAALICE, - }] - .into(); - - let mut descend_origin_location = parachain::SelfLocation::get(); - descend_origin_location.append_with(signed_origin).unwrap(); - - // To convert it to what the paraB will see instead of us - descend_origin_location - .reanchor(¶_b_location, &ancestry.interior) - .unwrap(); - - let derived = xcm_builder::HashedDescription::< - parachain::AccountId, - xcm_builder::DescribeFamily, - >::convert_location(&descend_origin_location) - .unwrap(); - - ParaB::execute_with(|| { - // free execution, full amount received - assert_ok!(ParaBalances::transfer_allow_death( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - derived.clone(), - 4000009100u128, - )); - // derived account has all funds - assert!(ParaBalances::free_balance(&derived) == 4000009100); - // sovereign account has 0 funds - assert!(ParaBalances::free_balance(¶_a_account_20()) == 0); - }); - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = - ::PalletInfo::index::() - .unwrap() as u8; - - encoded.push(index); - - // Then call bytes - let mut call_bytes = pallet_balances::Call::::transfer_allow_death { - // 100 to sovereign - dest: para_a_account_20(), - value: 100u32.into(), - } - .encode(); - encoded.append(&mut call_bytes); - - let overall_weight = 4000009000u64; - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::transact_through_signed( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(xcm::VersionedLocation::from(para_b_location)), - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - para_b_balances - ))), - fee_amount: Some(overall_weight as u128) - }, - encoded, - // 4000000000 for transfer + 9000 for XCM - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: Some(Limited(overall_weight.into())) - }, - true - )); - }); - - ParaB::execute_with(|| { - // Check the derived account was refunded - assert_eq!(ParaBalances::free_balance(&derived), 3826174993); - - // Check the transfer was executed - assert_eq!(ParaBalances::free_balance(¶_a_account_20()), 100); - }); -} - -#[test] -fn transact_through_signed_multilocation_para_to_para_ethereum() { - MockNet::reset(); - let mut ancestry = Location::parent(); - - let para_b_location = Location::new(1, [Parachain(2)]); - - let para_b_balances = Location::new(1, [Parachain(2), PalletInstance(1u8)]); - - ParaA::execute_with(|| { - // Root can set transact info - assert_ok!(XcmTransactor::set_transact_info( - parachain::RuntimeOrigin::root(), - // ParaB - Box::new(xcm::VersionedLocation::from(para_b_location.clone())), - // Para charges 1000 for every instruction, and we have 3, so 3 - 3.into(), - 20000000000.into(), - // 4 instructions in transact through signed - Some(4.into()) - )); - // Root can set transact info - // Set fee per second using weight-trader (replaces old set_fee_per_second) - set_fee_per_second_for_location( - para_b_balances.clone(), - parachain::ParaTokensPerSecond::get(), - ) - .expect("must succeed"); - ancestry = parachain::UniversalLocation::get().into(); - }); - - // Let's construct the Junction that we will append with DescendOrigin - let signed_origin: Junctions = [AccountKey20 { - network: None, - key: PARAALICE, - }] - .into(); - - let mut descend_origin_location = parachain::SelfLocation::get(); - descend_origin_location.append_with(signed_origin).unwrap(); - - // To convert it to what the paraB will see instead of us - descend_origin_location - .reanchor(¶_b_location, &ancestry.interior) - .unwrap(); - - let derived = xcm_builder::HashedDescription::< - parachain::AccountId, - xcm_builder::DescribeFamily, - >::convert_location(&descend_origin_location) - .unwrap(); - - let mut parachain_b_alice_balances_before = 0; - ParaB::execute_with(|| { - assert_ok!(ParaBalances::transfer_allow_death( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - derived.clone(), - 4000000104u128, - )); - // derived account has all funds - assert!(ParaBalances::free_balance(&derived) == 4000000104); - // sovereign account has 0 funds - assert!(ParaBalances::free_balance(¶_a_account_20()) == 0); - - parachain_b_alice_balances_before = ParaBalances::free_balance(&PARAALICE.into()) - }); - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = - ::PalletInfo::index::() - .unwrap() as u8; - - encoded.push(index); - - use sp_core::U256; - // Let's do a EVM transfer - let eth_tx = - xcm_primitives::EthereumXcmTransaction::V1(xcm_primitives::EthereumXcmTransactionV1 { - gas_limit: U256::from(21000), - fee_payment: xcm_primitives::EthereumXcmFee::Auto, - action: pallet_ethereum::TransactionAction::Call(PARAALICE.into()), - value: U256::from(100), - input: BoundedVec::< - u8, - ConstU32<{ xcm_primitives::MAX_ETHEREUM_XCM_INPUT_SIZE }> - >::try_from(vec![]).unwrap(), - access_list: None, - }); - - // Then call bytes - let mut call_bytes = pallet_ethereum_xcm::Call::::transact { - xcm_transaction: eth_tx, - } - .encode(); - encoded.append(&mut call_bytes); - - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::transact_through_signed( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(xcm::VersionedLocation::from(para_b_location)), - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - para_b_balances - ))), - fee_amount: None - }, - encoded, - // 4000000000 for transfer + 4000 for XCM - // 1-1 to fee - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: None - }, - false - )); - }); - - ParaB::execute_with(|| { - // Make sure the EVM transfer went through - assert!( - ParaBalances::free_balance(&PARAALICE.into()) - == parachain_b_alice_balances_before + 100 - ); - }); -} - -#[test] -fn transact_through_signed_multilocation_para_to_para_ethereum_no_proxy_fails() { - MockNet::reset(); - let mut ancestry = Location::parent(); - - let para_b_location = Location::new(1, [Parachain(2)]); - - let para_b_balances = Location::new(1, [Parachain(2), PalletInstance(1u8)]); - - ParaA::execute_with(|| { - // Root can set transact info - assert_ok!(XcmTransactor::set_transact_info( - parachain::RuntimeOrigin::root(), - // ParaB - Box::new(xcm::VersionedLocation::from(para_b_location.clone())), - // Para charges 1000 for every instruction, and we have 3, so 3 - 3.into(), - 20000000000.into(), - // 4 instructions in transact through signed - Some(4.into()) - )); - // Root can set transact info - // Set fee per second using weight-trader (replaces old set_fee_per_second) - set_fee_per_second_for_location( - para_b_balances.clone(), - parachain::ParaTokensPerSecond::get(), - ) - .expect("must succeed"); - ancestry = parachain::UniversalLocation::get().into(); - }); - - // Let's construct the Junction that we will append with DescendOrigin - let signed_origin: Junctions = [AccountKey20 { - network: None, - key: PARAALICE, - }] - .into(); - - let mut descend_origin_location = parachain::SelfLocation::get(); - descend_origin_location.append_with(signed_origin).unwrap(); - - // To convert it to what the paraB will see instead of us - descend_origin_location - .reanchor(¶_b_location, &ancestry.interior) - .unwrap(); - - let derived = xcm_builder::HashedDescription::< - parachain::AccountId, - xcm_builder::DescribeFamily, - >::convert_location(&descend_origin_location) - .unwrap(); - - let mut parachain_b_alice_balances_before = 0; - ParaB::execute_with(|| { - assert_ok!(ParaBalances::transfer_allow_death( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - derived.clone(), - 4000000104u128, - )); - // derived account has all funds - assert!(ParaBalances::free_balance(&derived) == 4000000104); - // sovereign account has 0 funds - assert!(ParaBalances::free_balance(¶_a_account_20()) == 0); - - parachain_b_alice_balances_before = ParaBalances::free_balance(&PARAALICE.into()) - }); - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = - ::PalletInfo::index::() - .unwrap() as u8; - - encoded.push(index); - - use sp_core::U256; - // Let's do a EVM transfer - let eth_tx = - xcm_primitives::EthereumXcmTransaction::V1(xcm_primitives::EthereumXcmTransactionV1 { - gas_limit: U256::from(21000), - fee_payment: xcm_primitives::EthereumXcmFee::Auto, - action: pallet_ethereum::TransactionAction::Call(PARAALICE.into()), - value: U256::from(100), - input: BoundedVec::< - u8, - ConstU32<{ xcm_primitives::MAX_ETHEREUM_XCM_INPUT_SIZE }> - >::try_from(vec![]).unwrap(), - access_list: None, - }); - - // Then call bytes - let mut call_bytes = pallet_ethereum_xcm::Call::::transact_through_proxy { - transact_as: PARAALICE.into(), - xcm_transaction: eth_tx, - } - .encode(); - encoded.append(&mut call_bytes); - - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::transact_through_signed( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(xcm::VersionedLocation::from(para_b_location)), - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - para_b_balances - ))), - fee_amount: None - }, - encoded, - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: None - }, - false - )); - }); - - ParaB::execute_with(|| { - // Make sure the EVM transfer wasn't executed - assert!(ParaBalances::free_balance(&PARAALICE.into()) == parachain_b_alice_balances_before); - }); -} - -#[test] -fn transact_through_signed_multilocation_para_to_para_ethereum_proxy_succeeds() { - MockNet::reset(); - let mut ancestry = Location::parent(); - - let para_b_location = Location::new(1, [Parachain(2)]); - - let para_b_balances = Location::new(1, [Parachain(2), PalletInstance(1u8)]); - - ParaA::execute_with(|| { - // Root can set transact info - assert_ok!(XcmTransactor::set_transact_info( - parachain::RuntimeOrigin::root(), - // ParaB - Box::new(xcm::VersionedLocation::from(para_b_location.clone())), - // Para charges 1000 for every instruction, and we have 3, so 3 - 3.into(), - 20000000000.into(), - // 4 instructions in transact through signed - Some(4.into()) - )); - // Root can set transact info - // Set fee per second using weight-trader (replaces old set_fee_per_second) - set_fee_per_second_for_location( - para_b_balances.clone(), - parachain::ParaTokensPerSecond::get(), - ) - .expect("must succeed"); - ancestry = parachain::UniversalLocation::get().into(); - }); - - // Let's construct the Junction that we will append with DescendOrigin - let signed_origin: Junctions = [AccountKey20 { - network: None, - key: PARAALICE, - }] - .into(); - - let mut descend_origin_location = parachain::SelfLocation::get(); - descend_origin_location.append_with(signed_origin).unwrap(); - - // To convert it to what the paraB will see instead of us - descend_origin_location - .reanchor(¶_b_location, &ancestry.interior) - .unwrap(); - - let derived = xcm_builder::HashedDescription::< - parachain::AccountId, - xcm_builder::DescribeFamily, - >::convert_location(&descend_origin_location) - .unwrap(); - - let transfer_recipient = evm_account(); - let mut transfer_recipient_balance_before = 0; - ParaB::execute_with(|| { - assert_ok!(ParaBalances::transfer_allow_death( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - derived.clone(), - 4000000104u128, - )); - // derived account has all funds - assert!(ParaBalances::free_balance(&derived) == 4000000104); - // sovereign account has 0 funds - assert!(ParaBalances::free_balance(¶_a_account_20()) == 0); - - transfer_recipient_balance_before = ParaBalances::free_balance(&transfer_recipient.into()); - - // Add proxy ALICE -> derived - let _ = parachain::Proxy::add_proxy_delegate( - &PARAALICE.into(), - derived, - parachain::ProxyType::Any, - 0, - ); - }); - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = - ::PalletInfo::index::() - .unwrap() as u8; - - encoded.push(index); - - use sp_core::U256; - // Let's do a EVM transfer - let eth_tx = - xcm_primitives::EthereumXcmTransaction::V2(xcm_primitives::EthereumXcmTransactionV2 { - gas_limit: U256::from(21000), - action: pallet_ethereum::TransactionAction::Call(transfer_recipient.into()), - value: U256::from(100), - input: BoundedVec::< - u8, - ConstU32<{ xcm_primitives::MAX_ETHEREUM_XCM_INPUT_SIZE }> - >::try_from(vec![]).unwrap(), - access_list: None, - }); - - // Then call bytes - let mut call_bytes = pallet_ethereum_xcm::Call::::transact_through_proxy { - transact_as: PARAALICE.into(), - xcm_transaction: eth_tx, - } - .encode(); - encoded.append(&mut call_bytes); - - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::transact_through_signed( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(xcm::VersionedLocation::from(para_b_location)), - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - para_b_balances - ))), - fee_amount: None - }, - encoded, - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: None - }, - false - )); - }); - - ParaB::execute_with(|| { - // Make sure the EVM transfer was executed - assert!( - ParaBalances::free_balance(&transfer_recipient.into()) - == transfer_recipient_balance_before + 100 - ); - }); -} - -#[test] -fn hrmp_init_accept_through_root() { - MockNet::reset(); - - Relay::execute_with(|| { - assert_ok!(RelayBalances::transfer_allow_death( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - para_a_account(), - 1000u128 - )); - assert_ok!(RelayBalances::transfer_allow_death( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - para_b_account(), - 1000u128 - )); - }); - - ParaA::execute_with(|| { - let total_fee = 1_000u128; - let total_weight: u64 = 1_000_000_000; - let tx_weight: u64 = 500_000_000; - // Root can send hrmp init channel - assert_ok!(XcmTransactor::hrmp_manage( - parachain::RuntimeOrigin::root(), - HrmpOperation::InitOpen(HrmpInitParams { - para_id: 2u32.into(), - proposed_max_capacity: 1, - proposed_max_message_size: 1 - }), - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - Location::parent() - ))), - fee_amount: Some(total_fee) - }, - TransactWeights { - transact_required_weight_at_most: tx_weight.into(), - overall_weight: Some(Limited(total_weight.into())) - } - )); - }); - Relay::execute_with(|| { - let expected_event: relay_chain::RuntimeEvent = - polkadot_runtime_parachains::hrmp::Event::OpenChannelRequested { - sender: 1u32.into(), - recipient: 2u32.into(), - proposed_max_capacity: 1u32, - proposed_max_message_size: 1u32, - } - .into(); - assert!(relay_chain::relay_events().contains(&expected_event)); - }); - ParaB::execute_with(|| { - let total_fee = 1_000u128; - let total_weight: u64 = 1_000_000_000; - let tx_weight: u64 = 500_000_000; - // Root can send hrmp accept channel - assert_ok!(XcmTransactor::hrmp_manage( - parachain::RuntimeOrigin::root(), - HrmpOperation::Accept { - para_id: 1u32.into() - }, - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - Location::parent() - ))), - fee_amount: Some(total_fee) - }, - TransactWeights { - transact_required_weight_at_most: tx_weight.into(), - overall_weight: Some(Limited(total_weight.into())) - } - )); - }); - - Relay::execute_with(|| { - let expected_event: relay_chain::RuntimeEvent = - polkadot_runtime_parachains::hrmp::Event::OpenChannelAccepted { - sender: 1u32.into(), - recipient: 2u32.into(), - } - .into(); - assert!(relay_chain::relay_events().contains(&expected_event)); - }); -} - -#[test] -fn hrmp_close_works() { - MockNet::reset(); - - Relay::execute_with(|| { - assert_ok!(RelayBalances::transfer_allow_death( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - para_a_account(), - 1000u128 - )); - assert_ok!(Hrmp::force_open_hrmp_channel( - relay_chain::RuntimeOrigin::root(), - 1u32.into(), - 2u32.into(), - 1u32, - 1u32 - )); - assert_ok!(Hrmp::force_process_hrmp_open( - relay_chain::RuntimeOrigin::root(), - 1u32 - )); - }); - - ParaA::execute_with(|| { - let total_fee = 1_000u128; - let total_weight: u64 = 1_000_000_000; - let tx_weight: u64 = 500_000_000; - // Root can send hrmp close - assert_ok!(XcmTransactor::hrmp_manage( - parachain::RuntimeOrigin::root(), - HrmpOperation::Close(HrmpChannelId { - sender: 1u32.into(), - recipient: 2u32.into() - }), - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - Location::parent() - ))), - fee_amount: Some(total_fee) - }, - TransactWeights { - transact_required_weight_at_most: tx_weight.into(), - overall_weight: Some(Limited(total_weight.into())) - } - )); - }); - Relay::execute_with(|| { - let expected_event: relay_chain::RuntimeEvent = - polkadot_runtime_parachains::hrmp::Event::ChannelClosed { - by_parachain: 1u32.into(), - channel_id: HrmpChannelId { - sender: 1u32.into(), - recipient: 2u32.into(), - }, - } - .into(); - assert!(relay_chain::relay_events().contains(&expected_event)); - }); -} - -use crate::xcm_mock::parachain::XcmWeightTrader; -use parity_scale_codec::{Decode, Encode}; -use sp_io::hashing::blake2_256; - -// Helper to derive accountIds -pub fn derivative_account_id(who: sp_runtime::AccountId32, index: u16) -> sp_runtime::AccountId32 { - let entropy = (b"modlpy/utilisuba", who, index).using_encoded(blake2_256); - sp_runtime::AccountId32::decode(&mut &entropy[..]).expect("valid account id") -} diff --git a/runtime/moonriver/Cargo.toml b/runtime/moonriver/Cargo.toml index 7c81aebe4da..270be10c3cf 100644 --- a/runtime/moonriver/Cargo.toml +++ b/runtime/moonriver/Cargo.toml @@ -204,7 +204,9 @@ cumulus-test-relay-sproof-builder = { workspace = true } sp-timestamp = { workspace = true } polkadot-runtime-parachains = { workspace = true } -xcm-simulator = { workspace = true } +xcm-emulator = { workspace = true } +westend-runtime = { workspace = true } +asset-hub-westend-runtime = { workspace = true } precompile-utils = { workspace = true, features = ["std", "testing"] } moonbeam-tests-primitives = { workspace = true } diff --git a/runtime/moonriver/tests/xcm_config/barriers.rs b/runtime/moonriver/tests/xcm_config/barriers.rs new file mode 100644 index 00000000000..4e5352657a9 --- /dev/null +++ b/runtime/moonriver/tests/xcm_config/barriers.rs @@ -0,0 +1,404 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Tests for XcmBarrier configuration. +//! +//! The barrier determines which XCM messages are allowed to execute. +//! Moonriver's barrier allows: +//! - TakeWeightCredit: Messages that consume credited weight +//! - AllowKnownQueryResponses: Expected query responses +//! - AllowTopLevelPaidExecutionFrom: Paid execution from any origin +//! - AllowSubscriptionsFrom: Version subscription messages + +use crate::xcm_common::*; +use moonriver_runtime::{Runtime, RuntimeCall}; +use parity_scale_codec::Encode; +use xcm::latest::prelude::*; +use xcm_executor::traits::QueryHandler; + +#[test] +fn barrier_allows_paid_execution_from_relay() { + ExtBuilder::default().build().execute_with(|| { + let origin = Location::parent(); + let message: Xcm = Xcm(vec![ + WithdrawAsset((Location::parent(), ONE_DOT).into()), + BuyExecution { + fees: (Location::parent(), ONE_DOT).into(), + weight_limit: WeightLimit::Unlimited, + }, + DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: ALICE, + }], + ), + }, + ]); + + let outcome = execute_xcm(origin, message); + // Should not be blocked by barrier (may fail later due to no funds, but not barrier) + assert!(!is_barrier_error(&outcome)); + }); +} + +#[test] +fn barrier_allows_paid_execution_from_sibling() { + ExtBuilder::default().build().execute_with(|| { + let origin = Location::new(1, [Parachain(2000)]); + let message: Xcm = Xcm(vec![ + WithdrawAsset((Location::parent(), ONE_DOT).into()), + BuyExecution { + fees: (Location::parent(), ONE_DOT).into(), + weight_limit: WeightLimit::Unlimited, + }, + DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: BOB, + }], + ), + }, + ]); + + let outcome = execute_xcm(origin, message); + assert!(!is_barrier_error(&outcome)); + }); +} + +#[test] +fn barrier_passes_unpaid_with_weight_credit() { + ExtBuilder::default().build().execute_with(|| { + // TakeWeightCredit is the first barrier and passes when the message + // weight is within the pre-credited amount. `execute_xcm` passes + // Weight::zero() as credit, so unpaid messages are rejected there. + // Use `execute_xcm_with_credit` with a generous credit instead. + let origin = Location::parent(); + let message: Xcm = Xcm(vec![DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: ALICE, + }], + ), + }]); + + let outcome = execute_xcm_with_credit(origin, message, Weight::MAX); + // TakeWeightCredit allows this to pass the barrier (may fail later for other reasons) + assert!( + !is_barrier_error(&outcome), + "TakeWeightCredit should allow messages with credited weight" + ); + }); +} + +#[test] +fn barrier_allows_subscription_messages() { + ExtBuilder::default().build().execute_with(|| { + let origin = Location::parent(); + // SubscribeVersion is allowed by AllowSubscriptionsFrom + let message: Xcm = Xcm(vec![SubscribeVersion { + query_id: 0, + max_response_weight: Weight::from_parts(1_000_000, 64 * 1024), + }]); + + let outcome = execute_xcm(origin, message); + // Should not be a barrier error - subscriptions are allowed + assert!(!is_barrier_error(&outcome)); + }); +} + +#[test] +fn barrier_allows_unsubscribe_messages() { + ExtBuilder::default().build().execute_with(|| { + let origin = Location::parent(); + let message: Xcm = Xcm(vec![UnsubscribeVersion]); + + let outcome = execute_xcm(origin, message); + assert!(!is_barrier_error(&outcome)); + }); +} + +#[test] +fn barrier_allows_paid_execution_with_descend_origin() { + ExtBuilder::default().build().execute_with(|| { + // Test that WithComputedOrigin allows descending origin + let origin = Location::parent(); + let message: Xcm = Xcm(vec![ + DescendOrigin( + [AccountId32 { + network: None, + id: [1u8; 32], + }] + .into(), + ), + WithdrawAsset((Location::parent(), ONE_DOT).into()), + BuyExecution { + fees: (Location::parent(), ONE_DOT).into(), + weight_limit: WeightLimit::Unlimited, + }, + DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: ALICE, + }], + ), + }, + ]); + + let outcome = execute_xcm(origin, message); + assert!(!is_barrier_error(&outcome)); + }); +} + +#[test] +fn barrier_allows_set_topic() { + ExtBuilder::default().build().execute_with(|| { + // SetTopic is wrapped by TrailingSetTopicAsId so should be handled + let origin = Location::parent(); + let message: Xcm = Xcm(vec![ + WithdrawAsset((Location::parent(), ONE_DOT).into()), + BuyExecution { + fees: (Location::parent(), ONE_DOT).into(), + weight_limit: WeightLimit::Unlimited, + }, + SetTopic([0u8; 32]), + ]); + + let outcome = execute_xcm(origin, message); + assert!(!is_barrier_error(&outcome)); + }); +} + +#[test] +fn barrier_with_computed_origin_rejects_when_depth_limit_exceeded() { + ExtBuilder::default().build().execute_with(|| { + // WithComputedOrigin is configured with ConstU32<8>, meaning it will skip at most 8 + // DescendOrigin (or similar) instructions when computing the origin. If the message + // contains more than 8 such instructions, WithComputedOrigin cannot reach the inner + // barriers (AllowTopLevelPaidExecutionFrom, AllowSubscriptionsFrom) and the message + // is rejected. + let origin = Location::parent(); + + // Build a message with 9 DescendOrigin instructions, exceeding the limit of 8 + let mut instructions: Vec> = Vec::new(); + for i in 0..9 { + instructions.push(DescendOrigin( + [AccountId32 { + network: None, + id: [i as u8; 32], + }] + .into(), + )); + } + instructions.push(WithdrawAsset((Location::parent(), ONE_DOT).into())); + instructions.push(BuyExecution { + fees: (Location::parent(), ONE_DOT).into(), + weight_limit: WeightLimit::Unlimited, + }); + + let message: Xcm = Xcm(instructions); + let outcome = execute_xcm(origin, message); + assert!( + is_barrier_error(&outcome), + "Message exceeding WithComputedOrigin depth limit of 8 should be rejected" + ); + }); +} + +#[test] +fn barrier_with_computed_origin_allows_at_depth_limit() { + ExtBuilder::default().build().execute_with(|| { + // WithComputedOrigin is configured with ConstU32<8>. A message with exactly 8 + // DescendOrigin instructions should still be processed by the inner barriers. + let origin = Location::parent(); + + let mut instructions: Vec> = Vec::new(); + for i in 0..8 { + instructions.push(DescendOrigin( + [AccountId32 { + network: None, + id: [i as u8; 32], + }] + .into(), + )); + } + instructions.push(WithdrawAsset((Location::parent(), ONE_DOT).into())); + instructions.push(BuyExecution { + fees: (Location::parent(), ONE_DOT).into(), + weight_limit: WeightLimit::Unlimited, + }); + + let message: Xcm = Xcm(instructions); + let outcome = execute_xcm(origin, message); + // Should pass the barrier (may fail later for other reasons like no funds) + assert!( + !is_barrier_error(&outcome), + "Message within WithComputedOrigin depth limit of 8 should pass the barrier" + ); + }); +} + +#[test] +fn barrier_allows_paid_execution_from_account_key20() { + ExtBuilder::default().build().execute_with(|| { + let origin = Location::new( + 0, + [AccountKey20 { + network: Some(NetworkId::Polkadot), + key: ALICE, + }], + ); + let message: Xcm = Xcm(vec![ + WithdrawAsset((Location::parent(), ONE_DOT).into()), + BuyExecution { + fees: (Location::parent(), ONE_DOT).into(), + weight_limit: WeightLimit::Unlimited, + }, + DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: BOB, + }], + ), + }, + ]); + + let outcome = execute_xcm(origin, message); + assert!( + !is_barrier_error(&outcome), + "Paid execution from AccountKey20 should pass the barrier" + ); + }); +} + +#[test] +fn barrier_rejects_unpaid_execution_from_sibling() { + ExtBuilder::default().build().execute_with(|| { + let origin = Location::new(1, [Parachain(2000)]); + let message: Xcm = Xcm(vec![ + UnpaidExecution { + weight_limit: Unlimited, + check_origin: None, + }, + DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: ALICE, + }], + ), + }, + ]); + + let outcome = execute_xcm(origin, message); + assert!( + is_barrier_error(&outcome), + "UnpaidExecution from sibling should be rejected by barrier" + ); + }); +} + +#[test] +fn barrier_rejects_unpaid_transact_from_sibling() { + ExtBuilder::default().build().execute_with(|| { + let origin = Location::new(1, [Parachain(2000)]); + let encoded_call = RuntimeCall::System(frame_system::Call::remark_with_event { + remark: vec![1, 2, 3], + }) + .encode(); + + let message: Xcm = Xcm(vec![ + UnpaidExecution { + weight_limit: Unlimited, + check_origin: None, + }, + Transact { + origin_kind: OriginKind::SovereignAccount, + call: encoded_call.into(), + fallback_max_weight: Some(Weight::from_parts(100_000_000, 10_000)), + }, + ]); + + let outcome = execute_xcm(origin, message); + assert!( + is_barrier_error(&outcome), + "UnpaidExecution + Transact from sibling should be rejected" + ); + }); +} + +#[test] +fn barrier_allows_known_query_response() { + ExtBuilder::default().build().execute_with(|| { + let relay_origin = Location::parent(); + + let query_id = pallet_xcm::Pallet::::new_query( + relay_origin.clone(), + 100u32.into(), + Location::here(), + ); + + let message: Xcm = Xcm(vec![QueryResponse { + query_id, + response: Response::Null, + max_weight: Weight::from_parts(1_000_000, 64 * 1024), + querier: Some(Location::here()), + }]); + + let outcome = execute_xcm(relay_origin, message); + assert!( + !is_barrier_error(&outcome), + "Known query response should pass AllowKnownQueryResponses barrier" + ); + }); +} + +#[test] +fn barrier_rejects_unknown_query_response() { + ExtBuilder::default().build().execute_with(|| { + let origin = Location::parent(); + let unknown_query_id = 999_999u64; + + let message: Xcm = Xcm(vec![QueryResponse { + query_id: unknown_query_id, + response: Response::Null, + max_weight: Weight::from_parts(1_000_000, 64 * 1024), + querier: Some(Location::here()), + }]); + + let outcome = execute_xcm(origin, message); + assert!( + is_barrier_error(&outcome), + "Unknown query response should be rejected by barrier" + ); + }); +} diff --git a/runtime/moonriver/tests/xcm_config/location.rs b/runtime/moonriver/tests/xcm_config/location.rs new file mode 100644 index 00000000000..6260570e89e --- /dev/null +++ b/runtime/moonriver/tests/xcm_config/location.rs @@ -0,0 +1,181 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Tests for LocationToAccountId configuration. +//! +//! LocationToAccountId converts XCM Locations to AccountIds. Moonriver uses: +//! - ParentIsPreset: Relay chain maps to a preset account +//! - SiblingParachainConvertsVia: Sibling parachains map to sovereign accounts +//! - AccountKey20Aliases: AccountKey20 junctions map directly to AccountId +//! - HashedDescription: Other locations map via a hashed description +//! - ExternalConsensusLocationsConverterFor: Bridged locations map to accounts + +use crate::xcm_common::*; +use moonriver_runtime::{ + xcm_config::{LocationToAccountId, LocationToH160}, + AccountId, +}; +use polkadot_parachain::primitives::Sibling; +use sp_core::H160; +use sp_runtime::traits::AccountIdConversion; +use xcm::latest::prelude::*; +use xcm_executor::traits::ConvertLocation; + +#[test] +fn location_converts_relay_to_account() { + ExtBuilder::default().build().execute_with(|| { + let relay_location = Location::parent(); + let account = LocationToAccountId::convert_location(&relay_location); + + assert!( + account.is_some(), + "Relay location should convert to account" + ); + + // ParentIsPreset decodes b"Parent" padded with zeros into AccountId (H160). + let relay_account = account.unwrap(); + let expected: [u8; 20] = { + let mut buf = [0u8; 20]; + buf[..6].copy_from_slice(b"Parent"); + buf + }; + assert_eq!( + relay_account, + AccountId::from(expected), + "Relay sovereign should be derived from b\"Parent\" via ParentIsPreset" + ); + }); +} + +#[test] +fn location_converts_sibling_parachain_to_sovereign_account() { + ExtBuilder::default().build().execute_with(|| { + // SiblingParachainConvertsVia derives a sovereign account from the parachain ID + // using Sibling's AccountIdConversion::into_account_truncating. + let sibling_para_id = 2000u32; + let sibling_location = Location::new(1, [Parachain(sibling_para_id)]); + let account = LocationToAccountId::convert_location(&sibling_location); + + let expected: AccountId = Sibling::from(sibling_para_id).into_account_truncating(); + assert_eq!( + account, + Some(expected), + "Sibling parachain should convert to sovereign account derived from para ID" + ); + }); +} + +#[test] +fn location_converts_different_siblings_to_different_accounts() { + ExtBuilder::default().build().execute_with(|| { + let account_a = LocationToAccountId::convert_location(&Location::new(1, [Parachain(2000)])); + let account_b = LocationToAccountId::convert_location(&Location::new(1, [Parachain(3000)])); + + assert!( + account_a.is_some(), + "Sibling 2000 should convert to account" + ); + assert!( + account_b.is_some(), + "Sibling 3000 should convert to account" + ); + assert_ne!( + account_a, account_b, + "Different sibling parachains should have different sovereign accounts" + ); + }); +} + +#[test] +fn location_converts_account_key20_directly() { + ExtBuilder::default().build().execute_with(|| { + let expected_account = ALICE; + let location = Location::new( + 0, + [AccountKey20 { + network: Some(NetworkId::Kusama), + key: expected_account, + }], + ); + + let account = LocationToAccountId::convert_location(&location); + + assert!(account.is_some(), "AccountKey20 should convert to account"); + assert_eq!( + account.unwrap(), + AccountId::from(expected_account), + "AccountKey20 should map directly to the same account" + ); + }); +} + +#[test] +fn location_rejects_unsupported_multi_junction_patterns() { + ExtBuilder::default().build().execute_with(|| { + // HashedDescription<_, DescribeFamily> only describes + // locations whose tail (after the family prefix) is a single terminal junction. + // A sibling with multiple interior junctions beyond Parachain is not describable + // by DescribeAllTerminal and no other converter handles it, so it must return None. + let complex_location = + Location::new(1, [Parachain(2000), PalletInstance(10), GeneralIndex(42)]); + + assert_eq!( + LocationToAccountId::convert_location(&complex_location), + None, + "Multi-junction sibling location should not convert to an account" + ); + }); +} + +#[test] +fn location_converts_bridged_parachain() { + ExtBuilder::default().build().execute_with(|| { + // A parachain from another consensus (bridged) + let bridged_location = + Location::new(2, [GlobalConsensus(NetworkId::Polkadot), Parachain(1000)]); + + let account = LocationToAccountId::convert_location(&bridged_location); + + // ExternalConsensusLocationsConverterFor should handle this + assert!( + account.is_some(), + "Bridged parachain should convert to account" + ); + }); +} + +#[test] +fn location_to_h160_converts_account_key20() { + ExtBuilder::default().build().execute_with(|| { + // LocationToH160 wraps LocationToAccountId and converts to H160, + // used by EVM-related XCM operations. + let location = Location::new( + 0, + [AccountKey20 { + network: Some(NetworkId::Kusama), + key: ALICE, + }], + ); + + let h160 = LocationToH160::convert_location(&location); + + assert_eq!( + h160, + Some(H160::from(ALICE)), + "LocationToH160 should convert AccountKey20 to the matching H160" + ); + }); +} diff --git a/runtime/moonriver/tests/xcm_config/main.rs b/runtime/moonriver/tests/xcm_config/main.rs new file mode 100644 index 00000000000..cb415d9b0e6 --- /dev/null +++ b/runtime/moonriver/tests/xcm_config/main.rs @@ -0,0 +1,32 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! XCM Configuration Tests (Level 1) — Moonriver + +#![cfg(test)] + +#[path = "../common/mod.rs"] +mod common; + +mod xcm_common; + +mod barriers; +mod location; +mod origin; +mod reserves; +mod traders; +mod transactors; +mod weigher; diff --git a/runtime/moonriver/tests/xcm_config/origin.rs b/runtime/moonriver/tests/xcm_config/origin.rs new file mode 100644 index 00000000000..962346b11aa --- /dev/null +++ b/runtime/moonriver/tests/xcm_config/origin.rs @@ -0,0 +1,177 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Tests for XcmOriginToTransactDispatchOrigin and SafeCallFilter configuration. +//! +//! XcmOriginToTransactDispatchOrigin converts XCM locations + OriginKind into +//! dispatch origins for Transact. Moonriver uses: +//! - SovereignSignedViaLocation: SovereignAccount kind → Signed origin +//! - RelayChainAsNative: Native kind from relay → relay origin +//! - SiblingParachainAsNative: Native kind from sibling → sibling origin +//! - XcmPassthrough: Xcm kind → pallet_xcm origin +//! - SignedAccountKey20AsNative: Native kind from AccountKey20 → Signed origin +//! +//! SafeCallFilter determines which calls are allowed via XCM Transact. + +use crate::xcm_common::*; +use moonriver_runtime::{ + xcm_config::{SafeCallFilter, XcmOriginToTransactDispatchOrigin}, + RuntimeCall, RuntimeOrigin, +}; +use xcm::latest::prelude::*; +use xcm_executor::traits::ConvertOrigin; + +#[test] +fn origin_converts_relay_with_sovereign_kind() { + ExtBuilder::default().build().execute_with(|| { + // SovereignSignedViaLocation converts relay location + SovereignAccount kind + // into a Signed origin using the relay's sovereign account. + let relay = Location::parent(); + + let result = + >::convert_origin( + relay, + OriginKind::SovereignAccount, + ); + + assert!( + result.is_ok(), + "Relay + SovereignAccount should convert to dispatch origin" + ); + }); +} + +#[test] +fn origin_converts_sibling_with_sovereign_kind() { + ExtBuilder::default().build().execute_with(|| { + let sibling = Location::new(1, [Parachain(2000)]); + + let result = + >::convert_origin( + sibling, + OriginKind::SovereignAccount, + ); + + assert!( + result.is_ok(), + "Sibling + SovereignAccount should convert to dispatch origin" + ); + }); +} + +#[test] +fn origin_converts_relay_with_native_kind() { + ExtBuilder::default().build().execute_with(|| { + // RelayChainAsNative converts relay location + Native kind into the + // relay chain origin (used for governance-like calls). + let relay = Location::parent(); + + let result = + >::convert_origin( + relay, + OriginKind::Native, + ); + + assert!( + result.is_ok(), + "Relay + Native should convert via RelayChainAsNative" + ); + }); +} + +#[test] +fn origin_converts_relay_with_xcm_kind() { + ExtBuilder::default().build().execute_with(|| { + // XcmPassthrough converts any location + Xcm kind into a pallet_xcm origin. + let relay = Location::parent(); + + let result = + >::convert_origin( + relay, + OriginKind::Xcm, + ); + + assert!( + result.is_ok(), + "Relay + Xcm should convert via XcmPassthrough" + ); + }); +} + +#[test] +fn origin_converts_account_key20_with_native_kind() { + ExtBuilder::default().build().execute_with(|| { + // SignedAccountKey20AsNative converts AccountKey20 + Native kind into a + // Signed origin with the 20-byte account. + let account_location = Location::new( + 0, + [AccountKey20 { + network: Some(NetworkId::Kusama), + key: ALICE, + }], + ); + + let result = + >::convert_origin( + account_location, + OriginKind::Native, + ); + + assert!( + result.is_ok(), + "AccountKey20 + Native should convert via SignedAccountKey20AsNative" + ); + }); +} + +#[test] +fn origin_rejects_superuser_kind() { + ExtBuilder::default().build().execute_with(|| { + // No converter handles Superuser kind, so it should be rejected. + let relay = Location::parent(); + + let result = + >::convert_origin( + relay, + OriginKind::Superuser, + ); + + assert!(result.is_err(), "Superuser kind should not be convertible"); + }); +} + +#[test] +fn safe_call_filter_allows_all_calls() { + ExtBuilder::default().build().execute_with(|| { + use frame_support::traits::Contains; + + // Moonriver's SafeCallFilter currently allows all calls (returns true). + // This is intentional — filtering is handled at the EVM level. + // Verify with calls from two distinct pallets to confirm the filter + // is truly permissive, not a pallet-specific whitelist. + let remark_call = RuntimeCall::System(frame_system::Call::remark { remark: vec![] }); + assert!( + SafeCallFilter::contains(&remark_call), + "SafeCallFilter should allow System::remark" + ); + + let utility_call = RuntimeCall::Utility(pallet_utility::Call::batch { calls: vec![] }); + assert!( + SafeCallFilter::contains(&utility_call), + "SafeCallFilter should allow Utility::batch" + ); + }); +} diff --git a/runtime/moonriver/tests/xcm_config/reserves.rs b/runtime/moonriver/tests/xcm_config/reserves.rs new file mode 100644 index 00000000000..c6b5cadaaa5 --- /dev/null +++ b/runtime/moonriver/tests/xcm_config/reserves.rs @@ -0,0 +1,268 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Tests for IsReserve (Reserves) configuration. +//! +//! The Reserves type determines which assets are recognized as reserve assets +//! and which origin is allowed to act as reserve for those assets. +//! +//! Moonriver's Reserves configuration allows: +//! - IsBridgedConcreteAssetFrom: Bridged assets from Asset Hub +//! - IsBridgedConcreteAssetFrom: Assets from Moonbeam +//! - Case: DOT from Asset Hub +//! - MultiNativeAsset>: Self-reserve + +use crate::xcm_common::*; +use frame_support::traits::ContainsPair; +use moonriver_runtime::xcm_config::{AssetHubLocation, XcmExecutorConfig}; +use xcm::latest::prelude::*; +use xcm_primitives::IsBridgedConcreteAssetFrom; + +/// The actual `IsReserve` type wired into the XCM executor. +type IsReserve = ::IsReserve; + +const ASSET_HUB_PARA_ID: u32 = 1000; + +#[test] +fn reserves_accepts_dot_from_asset_hub() { + ExtBuilder::default().build().execute_with(|| { + // DOT asset coming from Asset Hub should be accepted + let dot_asset = Asset { + id: AssetId(Location::parent()), + fun: Fungible(ONE_DOT), + }; + let asset_hub_origin = Location::new(1, [Parachain(ASSET_HUB_PARA_ID)]); + + // RelayChainNativeAssetFromAssetHub case should match this + type RelayFromAssetHub = + xcm_builder::Case; + + assert!( + RelayFromAssetHub::contains(&dot_asset, &asset_hub_origin), + "DOT from Asset Hub should be accepted as reserve" + ); + assert!( + IsReserve::contains(&dot_asset, &asset_hub_origin), + "IsReserve must accept DOT from Asset Hub (runtime wiring)" + ); + }); +} + +#[test] +fn reserves_accepts_bridged_assets_from_asset_hub() { + ExtBuilder::default().build().execute_with(|| { + // Bridged asset from another consensus (parents: 2) + let bridged_asset = Asset { + id: AssetId(Location::new( + 2, + [GlobalConsensus(NetworkId::Kusama), Parachain(1000)], + )), + fun: Fungible(1_000_000), + }; + let asset_hub_origin = AssetHubLocation::get(); + + // IsBridgedConcreteAssetFrom should match + assert!( + IsBridgedConcreteAssetFrom::::contains( + &bridged_asset, + &asset_hub_origin + ), + "Bridged assets from Asset Hub should be accepted" + ); + assert!( + IsReserve::contains(&bridged_asset, &asset_hub_origin), + "IsReserve must accept bridged assets from Asset Hub (runtime wiring)" + ); + }); +} + +#[test] +fn reserves_rejects_bridged_assets_from_wrong_origin() { + ExtBuilder::default().build().execute_with(|| { + // Bridged asset from another consensus + let bridged_asset = Asset { + id: AssetId(Location::new( + 2, + [GlobalConsensus(NetworkId::Kusama), Parachain(1000)], + )), + fun: Fungible(1_000_000), + }; + // Wrong origin - not Asset Hub + let wrong_origin = Location::new(1, [Parachain(2000)]); + + assert!( + !IsBridgedConcreteAssetFrom::::contains( + &bridged_asset, + &wrong_origin + ), + "Bridged assets from wrong origin should be rejected" + ); + }); +} + +#[test] +fn reserves_rejects_non_bridged_assets_via_bridged_filter() { + ExtBuilder::default().build().execute_with(|| { + // Non-bridged asset (parents: 1, not 2) + let local_asset = Asset { + id: AssetId(Location::new(1, [Parachain(1000)])), + fun: Fungible(1_000_000), + }; + let asset_hub_origin = AssetHubLocation::get(); + + // IsBridgedConcreteAssetFrom requires parents > 1 + assert!( + !IsBridgedConcreteAssetFrom::::contains( + &local_asset, + &asset_hub_origin + ), + "Non-bridged assets should not match bridged asset filter" + ); + }); +} + +#[test] +fn reserves_accepts_self_reserve() { + ExtBuilder::default().build().execute_with(|| { + use frame_support::traits::PalletInfoAccess; + use moonriver_runtime::xcm_config::SelfLocationAbsolute; + use moonriver_runtime::Balances; + use xcm_primitives::{AbsoluteAndRelativeReserve, MultiNativeAsset}; + + let self_reserve = Location::new(0, [PalletInstance(Balances::index() as u8)]); + + let native_asset = Asset { + id: AssetId(self_reserve), + fun: Fungible(1_000_000_000_000_000_000), // 1 MOVR + }; + + // MultiNativeAsset accepts an asset when the origin matches the asset's + // reserve. For our own native token the reserve is ourselves + // (Location::here()), so origin = here() must pass. + let self_origin = Location::here(); + + assert!( + MultiNativeAsset::>::contains( + &native_asset, + &self_origin + ), + "Self reserve asset should be accepted when origin is here()" + ); + assert!( + IsReserve::contains(&native_asset, &self_origin), + "IsReserve must accept self-reserve (runtime wiring)" + ); + }); +} + +#[test] +fn reserves_accepts_sibling_native_asset() { + ExtBuilder::default().build().execute_with(|| { + // Native asset from a sibling parachain + let sibling_asset = Asset { + id: AssetId(Location::new(1, [Parachain(2000), PalletInstance(10)])), + fun: Fungible(1_000_000), + }; + let sibling_origin = Location::new(1, [Parachain(2000)]); + + // MultiNativeAsset should accept this - the origin matches the asset's reserve + // This is checked by matching asset's reserve location to the origin + use moonriver_runtime::xcm_config::SelfLocationAbsolute; + use xcm_primitives::{AbsoluteAndRelativeReserve, MultiNativeAsset}; + + // The reserve of sibling asset is the sibling chain itself + // MultiNativeAsset will check if origin matches reserve + assert!( + MultiNativeAsset::>::contains( + &sibling_asset, + &sibling_origin + ), + "Sibling native asset should be accepted when origin matches reserve" + ); + assert!( + IsReserve::contains(&sibling_asset, &sibling_origin), + "IsReserve must accept sibling native asset (runtime wiring)" + ); + }); +} + +#[test] +fn reserves_rejects_asset_with_mismatched_origin() { + ExtBuilder::default().build().execute_with(|| { + // Asset claims to be from parachain 2000 + let asset = Asset { + id: AssetId(Location::new(1, [Parachain(2000), PalletInstance(10)])), + fun: Fungible(1_000_000), + }; + // But origin is from parachain 3000 + let wrong_origin = Location::new(1, [Parachain(3000)]); + + use moonriver_runtime::xcm_config::SelfLocationAbsolute; + use xcm_primitives::{AbsoluteAndRelativeReserve, MultiNativeAsset}; + + assert!( + !MultiNativeAsset::>::contains( + &asset, + &wrong_origin + ), + "Asset from mismatched origin should be rejected" + ); + }); +} + +#[test] +fn reserves_accepts_dot_from_relay() { + ExtBuilder::default().build().execute_with(|| { + let dot_asset = Asset { + id: AssetId(Location::parent()), + fun: Fungible(ONE_DOT), + }; + let relay_origin = Location::parent(); + + use moonriver_runtime::xcm_config::SelfLocationAbsolute; + use xcm_primitives::{AbsoluteAndRelativeReserve, MultiNativeAsset}; + + assert!( + MultiNativeAsset::>::contains( + &dot_asset, + &relay_origin + ), + "DOT from relay should be accepted as reserve" + ); + assert!( + IsReserve::contains(&dot_asset, &relay_origin), + "IsReserve must accept DOT from relay (runtime wiring)" + ); + }); +} + +#[test] +fn teleport_always_rejected() { + ExtBuilder::default().build().execute_with(|| { + type IsTeleporter = ::IsTeleporter; + + let dot = Asset { + id: AssetId(Location::parent()), + fun: Fungible(ONE_DOT), + }; + let relay_origin = Location::parent(); + + assert!( + !IsTeleporter::contains(&dot, &relay_origin), + "IsTeleporter should reject every asset/origin pair" + ); + }); +} diff --git a/runtime/moonriver/tests/xcm_config/traders.rs b/runtime/moonriver/tests/xcm_config/traders.rs new file mode 100644 index 00000000000..5d70224560a --- /dev/null +++ b/runtime/moonriver/tests/xcm_config/traders.rs @@ -0,0 +1,286 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Tests for the XCM Trader (pallet_xcm_weight_trader::Trader) configuration. +//! +//! The trader is responsible for converting weight to fee and accepting payment +//! in different assets. Moonriver uses a custom trader that: +//! - Accepts the native token (MOVR) at standard rates +//! - Accepts registered foreign assets at configured relative prices + +use crate::xcm_common::*; +use frame_support::traits::PalletInfoAccess; +use moonriver_runtime::{Balances, Runtime, Treasury}; +use pallet_xcm_weight_trader::{Pallet as XcmWeightTrader, Trader}; +use sp_weights::Weight; +use xcm::latest::prelude::*; +use xcm::VersionedAssetId; +use xcm_executor::traits::WeightTrader; +use xcm_executor::AssetsInHolding; + +fn native_location() -> Location { + Location::new(0, [PalletInstance(Balances::index() as u8)]) +} + +#[test] +fn trader_accepts_native_token() { + ExtBuilder::default().build().execute_with(|| { + let mut trader = Trader::::new(); + let weight_to_buy = Weight::from_parts(1_000_000_000, 64 * 1024); + + // Create payment in native token + let mut payment = AssetsInHolding::new(); + payment.subsume(Asset { + id: AssetId(native_location()), + fun: Fungible(ONE_MOVR), + }); + + let context = XcmContext::with_message_id([0u8; 32]); + let result = trader.buy_weight(weight_to_buy, payment.clone(), &context); + + // Should succeed - native token is always accepted + assert!(result.is_ok(), "Native token should be accepted for fees"); + }); +} + +#[test] +fn trader_computes_native_fee_correctly() { + ExtBuilder::default().build().execute_with(|| { + let weight = Weight::from_parts(1_000_000_000, 64 * 1024); + let native_loc = native_location(); + + // Compute fee for native token using public API + let versioned_asset_id = VersionedAssetId::V5(AssetId(native_loc)); + let fee_result = + XcmWeightTrader::::query_weight_to_asset_fee(weight, versioned_asset_id); + + assert!(fee_result.is_ok(), "Should compute fee for native token"); + let fee = fee_result.unwrap(); + assert!(fee > 0, "Fee should be non-zero"); + }); +} + +#[test] +fn trader_rejects_unsupported_asset() { + ExtBuilder::default().build().execute_with(|| { + let mut trader = Trader::::new(); + let weight_to_buy = Weight::from_parts(1_000_000_000, 64 * 1024); + + // Try to pay with unsupported asset + let unsupported_asset_location = Location::new(1, [Parachain(9999), PalletInstance(99)]); + let mut payment = AssetsInHolding::new(); + payment.subsume(Asset { + id: AssetId(unsupported_asset_location.clone()), + fun: Fungible(1_000_000_000_000), + }); + + let context = XcmContext::with_message_id([0u8; 32]); + let result = trader.buy_weight(weight_to_buy, payment, &context); + + // Should fail - asset not registered + assert!(result.is_err(), "Unsupported asset should be rejected"); + assert_eq!(result.unwrap_err(), XcmError::AssetNotFound); + }); +} + +#[test] +fn trader_accepts_registered_foreign_asset() { + // Register DOT as supported asset + let dot_location = Location::parent(); + + ExtBuilder::default() + .with_xcm_assets(vec![XcmAssetInitialization { + asset_id: 1, + xcm_location: dot_location.clone(), + decimals: 10, + name: "Polkadot", + symbol: "DOT", + balances: vec![], + }]) + .build() + .execute_with(|| { + // First verify the asset is registered and queryable + let versioned_asset_id = VersionedAssetId::V5(AssetId(dot_location.clone())); + let weight = Weight::from_parts(1_000_000_000, 64 * 1024); + let fee_result = + XcmWeightTrader::::query_weight_to_asset_fee(weight, versioned_asset_id); + + // If the query succeeds, the asset is properly registered + assert!( + fee_result.is_ok(), + "Registered foreign asset should be queryable" + ); + + // Now test the trader directly + let mut trader = Trader::::new(); + let weight_to_buy = Weight::from_parts(1_000_000_000, 64 * 1024); + + // Pay with DOT - need sufficient amount to cover the computed fee + let fee = fee_result.unwrap(); + let mut payment = AssetsInHolding::new(); + payment.subsume(Asset { + id: AssetId(dot_location.clone()), + fun: Fungible(fee * 2), // Double to ensure enough + }); + + let context = XcmContext::with_message_id([0u8; 32]); + let result = trader.buy_weight(weight_to_buy, payment, &context); + + // Should succeed - DOT is registered in XcmWeightTrader + assert!( + result.is_ok(), + "Registered foreign asset should be accepted: {:?}", + result + ); + }); +} + +#[test] +fn trader_computes_foreign_asset_fee_with_relative_price() { + let dot_location = Location::parent(); + + ExtBuilder::default() + .with_xcm_assets(vec![XcmAssetInitialization { + asset_id: 1, + xcm_location: dot_location.clone(), + decimals: 10, + name: "Polkadot", + symbol: "DOT", + balances: vec![], + }]) + .build() + .execute_with(|| { + let weight = Weight::from_parts(1_000_000_000, 64 * 1024); + + // Compute fee for DOT using public API + let versioned_asset_id = VersionedAssetId::V5(AssetId(dot_location.clone())); + let fee_result = + XcmWeightTrader::::query_weight_to_asset_fee(weight, versioned_asset_id); + + assert!( + fee_result.is_ok(), + "Should compute fee for registered asset" + ); + let fee = fee_result.unwrap(); + // Fee should be computed based on relative price + assert!(fee > 0, "Fee should be non-zero"); + }); +} + +#[test] +fn trader_cannot_buy_weight_twice() { + ExtBuilder::default().build().execute_with(|| { + let mut trader = Trader::::new(); + let weight_to_buy = Weight::from_parts(1_000_000_000, 64 * 1024); + let context = XcmContext::with_message_id([0u8; 32]); + + // First purchase + let mut payment1 = AssetsInHolding::new(); + payment1.subsume(Asset { + id: AssetId(native_location()), + fun: Fungible(ONE_MOVR), + }); + let first_result = trader.buy_weight(weight_to_buy, payment1, &context); + assert!( + first_result.is_ok(), + "First buy_weight must succeed for the second-purchase test to be meaningful" + ); + + // Second purchase should fail + let mut payment2 = AssetsInHolding::new(); + payment2.subsume(Asset { + id: AssetId(native_location()), + fun: Fungible(ONE_MOVR), + }); + let result = trader.buy_weight(weight_to_buy, payment2, &context); + + assert!(result.is_err(), "Second buy_weight should fail"); + assert_eq!(result.unwrap_err(), XcmError::NotWithdrawable); + }); +} + +#[test] +fn trader_refunds_unused_weight() { + ExtBuilder::default().build().execute_with(|| { + let mut trader = Trader::::new(); + let weight_bought = Weight::from_parts(2_000_000_000, 128 * 1024); + let weight_used = Weight::from_parts(1_000_000_000, 64 * 1024); + let context = XcmContext::with_message_id([0u8; 32]); + + // Buy more weight than needed + let mut payment = AssetsInHolding::new(); + payment.subsume(Asset { + id: AssetId(native_location()), + fun: Fungible(ONE_MOVR * 10), // Plenty of funds + }); + + let buy_result = trader.buy_weight(weight_bought, payment, &context); + assert!(buy_result.is_ok()); + + // Refund unused weight + let unused_weight = weight_bought.saturating_sub(weight_used); + let refund = trader.refund_weight(unused_weight, &context); + + // Must get a refund + let refunded_asset = refund.expect("refund_weight must return Some for unused weight"); + assert_eq!( + refunded_asset.id, + AssetId(native_location()), + "Refunded asset must be the native token" + ); + match refunded_asset.fun { + Fungible(amount) => { + assert!(amount > 0, "Refunded amount must be non-zero"); + } + _ => panic!("Expected fungible refund"), + } + }); +} + +#[test] +fn trader_handles_insufficient_payment() { + ExtBuilder::default().build().execute_with(|| { + let mut trader = Trader::::new(); + let weight_to_buy = Weight::from_parts(1_000_000_000_000, 64 * 1024); // Very large weight + let context = XcmContext::with_message_id([0u8; 32]); + + // Try to pay with very small amount + let mut payment = AssetsInHolding::new(); + payment.subsume(Asset { + id: AssetId(native_location()), + fun: Fungible(1), // Tiny amount + }); + + let result = trader.buy_weight(weight_to_buy, payment, &context); + + // Should fail - insufficient payment + assert!(result.is_err(), "Insufficient payment should be rejected"); + assert_eq!(result.unwrap_err(), XcmError::TooExpensive); + }); +} + +#[test] +fn xcm_fees_go_to_treasury() { + ExtBuilder::default().build().execute_with(|| { + use moonriver_runtime::xcm_config::XcmFeesAccount; + + assert_eq!( + XcmFeesAccount::get(), + Treasury::account_id(), + "XCM fee destination should be the treasury account" + ); + }); +} diff --git a/runtime/moonriver/tests/xcm_config/transactors.rs b/runtime/moonriver/tests/xcm_config/transactors.rs new file mode 100644 index 00000000000..2265136c9b6 --- /dev/null +++ b/runtime/moonriver/tests/xcm_config/transactors.rs @@ -0,0 +1,358 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Tests for AssetTransactors configuration. +//! +//! AssetTransactors handle the deposit and withdrawal of assets via XCM. +//! Moonriver uses a tuple of transactors: +//! - LocalAssetTransactor: Handles native token (MOVR) using pallet_balances +//! - EvmForeignAssets: Handles registered foreign assets +//! - Erc20XcmBridge: Handles ERC20 tokens via the bridge + +use crate::xcm_common::*; +use frame_support::traits::{Currency, PalletInfoAccess}; +use moonriver_runtime::{AccountId, Balances}; +use sp_core::U256; +use xcm::latest::prelude::*; +use xcm_executor::traits::TransactAsset; + +fn alice_account() -> AccountId { + AccountId::from(ALICE) +} + +fn bob_account() -> AccountId { + AccountId::from(BOB) +} + +fn native_asset_location() -> Location { + Location::new(0, [PalletInstance(Balances::index() as u8)]) +} + +#[test] +fn local_transactor_deposits_native_token() { + ExtBuilder::default() + .with_balances(vec![(alice_account(), ONE_MOVR * 100)]) + .build() + .execute_with(|| { + use moonriver_runtime::xcm_config::AssetTransactors; + + let initial_balance = Balances::free_balance(bob_account()); + + let asset = Asset { + id: AssetId(native_asset_location()), + fun: Fungible(ONE_MOVR), + }; + let destination = Location::new( + 0, + [AccountKey20 { + network: None, + key: BOB, + }], + ); + + // Deposit native asset to Bob + let result = + ::deposit_asset(&asset, &destination, None); + + assert!(result.is_ok(), "Deposit should succeed"); + let final_balance = Balances::free_balance(bob_account()); + assert_eq!( + final_balance, + initial_balance + ONE_MOVR, + "Balance should increase by deposited amount" + ); + }); +} + +#[test] +fn local_transactor_withdraws_native_token() { + ExtBuilder::default() + .with_balances(vec![(alice_account(), ONE_MOVR * 100)]) + .build() + .execute_with(|| { + use moonriver_runtime::xcm_config::AssetTransactors; + + let initial_balance = Balances::free_balance(alice_account()); + + let asset = Asset { + id: AssetId(native_asset_location()), + fun: Fungible(ONE_MOVR), + }; + let source = Location::new( + 0, + [AccountKey20 { + network: None, + key: ALICE, + }], + ); + + // Withdraw native asset from Alice + let result = ::withdraw_asset(&asset, &source, None); + + assert!(result.is_ok(), "Withdraw should succeed"); + let final_balance = Balances::free_balance(alice_account()); + assert_eq!( + final_balance, + initial_balance - ONE_MOVR, + "Balance should decrease by withdrawn amount" + ); + }); +} + +#[test] +fn local_transactor_fails_withdraw_insufficient_balance() { + ExtBuilder::default() + .with_balances(vec![(alice_account(), ONE_MOVR)]) // Only 1 MOVR + .build() + .execute_with(|| { + use moonriver_runtime::xcm_config::AssetTransactors; + + let asset = Asset { + id: AssetId(native_asset_location()), + fun: Fungible(ONE_MOVR * 100), // Try to withdraw 100 MOVR + }; + let source = Location::new( + 0, + [AccountKey20 { + network: None, + key: ALICE, + }], + ); + + let result = ::withdraw_asset(&asset, &source, None); + + assert!( + result.is_err(), + "Withdraw should fail with insufficient balance" + ); + }); +} + +#[test] +fn foreign_asset_transactor_deposits_registered_asset() { + let dot_location = Location::parent(); + + ExtBuilder::default() + .with_xcm_assets(vec![XcmAssetInitialization { + asset_id: 1, + xcm_location: dot_location.clone(), + decimals: 10, + name: "Polkadot", + symbol: "DOT", + balances: vec![], + }]) + .build() + .execute_with(|| { + use moonriver_runtime::xcm_config::AssetTransactors; + + let balance_before = moonriver_runtime::EvmForeignAssets::balance(1, bob_account()) + .unwrap_or(U256::zero()); + + let asset = Asset { + id: AssetId(dot_location.clone()), + fun: Fungible(ONE_DOT), + }; + let destination = Location::new( + 0, + [AccountKey20 { + network: None, + key: BOB, + }], + ); + + // Deposit DOT to Bob + let result = + ::deposit_asset(&asset, &destination, None); + + assert!( + result.is_ok(), + "Deposit of registered foreign asset should succeed" + ); + + let balance_after = moonriver_runtime::EvmForeignAssets::balance(1, bob_account()) + .expect("balance query should succeed after deposit"); + assert_eq!( + balance_after - balance_before, + U256::from(ONE_DOT), + "Bob's foreign asset balance should increase by the deposited amount" + ); + }); +} + +#[test] +fn transactor_fails_for_unregistered_asset() { + ExtBuilder::default().build().execute_with(|| { + use moonriver_runtime::xcm_config::AssetTransactors; + + // Unregistered asset location + let unknown_asset = Asset { + id: AssetId(Location::new(1, [Parachain(9999), PalletInstance(99)])), + fun: Fungible(1_000_000), + }; + let destination = Location::new( + 0, + [AccountKey20 { + network: None, + key: BOB, + }], + ); + + let result = + ::deposit_asset(&unknown_asset, &destination, None); + + // Should fail - asset not registered + assert!(result.is_err(), "Deposit of unregistered asset should fail"); + }); +} + +#[test] +fn transactor_withdraws_registered_foreign_asset() { + let dot_location = Location::parent(); + + ExtBuilder::default() + .with_xcm_assets(vec![XcmAssetInitialization { + asset_id: 1, + xcm_location: dot_location.clone(), + decimals: 10, + name: "Polkadot", + symbol: "DOT", + balances: vec![(bob_account(), ONE_DOT * 10)], + }]) + .build() + .execute_with(|| { + use moonriver_runtime::xcm_config::AssetTransactors; + + let balance_before = moonriver_runtime::EvmForeignAssets::balance(1, bob_account()) + .expect("balance query should succeed"); + + let asset = Asset { + id: AssetId(dot_location.clone()), + fun: Fungible(ONE_DOT), + }; + let source = Location::new( + 0, + [AccountKey20 { + network: None, + key: BOB, + }], + ); + + let result = ::withdraw_asset(&asset, &source, None); + + assert!( + result.is_ok(), + "Withdraw of registered foreign asset should succeed: {:?}", + result + ); + + let balance_after = moonriver_runtime::EvmForeignAssets::balance(1, bob_account()) + .expect("balance query should succeed after withdraw"); + assert_eq!( + balance_before - balance_after, + U256::from(ONE_DOT), + "Bob's foreign asset balance should decrease by the withdrawn amount" + ); + }); +} + +#[test] +fn transactor_handles_erc20_bridge_asset() { + ExtBuilder::default() + .with_balances(vec![(alice_account(), ONE_MOVR * 100)]) + .build() + .execute_with(|| { + use moonriver_runtime::xcm_config::AssetTransactors; + use moonriver_runtime::Erc20XcmBridge; + + let bridge_pallet_location = { + use frame_support::traits::PalletInfoAccess; + Location::new( + 0, + [PalletInstance( + ::index() as u8, + )], + ) + }; + + let fake_contract: [u8; 20] = [0xAA; 20]; + let erc20_asset_location = Location::new( + 0, + [ + PalletInstance(bridge_pallet_location.first_interior().map_or(0, |j| { + if let Junction::PalletInstance(idx) = j { + *idx + } else { + 0 + } + })), + AccountKey20 { + network: None, + key: fake_contract, + }, + ], + ); + + let asset = Asset { + id: AssetId(erc20_asset_location), + fun: Fungible(1_000), + }; + let destination = Location::new( + 0, + [AccountKey20 { + network: None, + key: BOB, + }], + ); + + let result = + ::deposit_asset(&asset, &destination, None); + + // Either Ok or Err — the important thing is no panic. + let _ = result; + }); +} + +#[test] +fn transactor_handles_relay_sovereign_account() { + ExtBuilder::default() + .with_balances(vec![(alice_account(), ONE_MOVR * 100)]) + .build() + .execute_with(|| { + use moonriver_runtime::xcm_config::{AssetTransactors, LocationToAccountId}; + use xcm_executor::traits::ConvertLocation; + + // The relay chain's sovereign account + let relay_location = Location::parent(); + let sovereign_account = LocationToAccountId::convert_location(&relay_location).unwrap(); + + // Give the sovereign account some funds + let _ = Balances::deposit_creating(&sovereign_account, ONE_MOVR * 10); + + let asset = Asset { + id: AssetId(native_asset_location()), + fun: Fungible(ONE_MOVR), + }; + + // Withdraw from relay sovereign account + let result = + ::withdraw_asset(&asset, &relay_location, None); + + assert!( + result.is_ok(), + "Should withdraw from relay sovereign account" + ); + }); +} diff --git a/runtime/moonriver/tests/xcm_config/weigher.rs b/runtime/moonriver/tests/xcm_config/weigher.rs new file mode 100644 index 00000000000..0a8d460c582 --- /dev/null +++ b/runtime/moonriver/tests/xcm_config/weigher.rs @@ -0,0 +1,154 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Tests for XcmWeigher configuration. +//! +//! The weigher calculates the weight of XCM messages based on the instructions +//! they contain. Moonriver uses WeightInfoBounds with custom XcmWeight implementation. + +use crate::xcm_common::*; +use moonriver_runtime::{xcm_config::XcmWeigher, RuntimeCall}; +use parity_scale_codec::Encode; +use sp_runtime::traits::Zero; +use sp_weights::Weight; +use xcm::latest::prelude::*; +use xcm_executor::traits::WeightBounds; + +#[test] +fn weigher_calculates_weight_for_simple_message() { + ExtBuilder::default().build().execute_with(|| { + let message: Xcm = Xcm(vec![ClearOrigin]); + + let weight = XcmWeigher::weight(&mut message.clone(), Weight::MAX); + + assert!(weight.is_ok(), "Should calculate weight for simple message"); + let w = weight.unwrap(); + assert!(!w.is_zero(), "Weight should be non-zero"); + }); +} + +#[test] +fn weigher_calculates_weight_for_transfer_message() { + ExtBuilder::default().build().execute_with(|| { + let message: Xcm = Xcm(vec![ + WithdrawAsset((Location::parent(), 1_000_000_000_000u128).into()), + BuyExecution { + fees: (Location::parent(), 1_000_000_000_000u128).into(), + weight_limit: WeightLimit::Unlimited, + }, + DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: ALICE, + }], + ), + }, + ]); + + let weight = XcmWeigher::weight(&mut message.clone(), Weight::MAX); + + assert!( + weight.is_ok(), + "Should calculate weight for transfer message" + ); + let w = weight.unwrap(); + assert!(w.ref_time() > 0, "Weight ref_time should be positive"); + }); +} + +#[test] +fn weigher_weight_increases_with_more_instructions() { + ExtBuilder::default().build().execute_with(|| { + let simple_message: Xcm = Xcm(vec![ClearOrigin]); + + let complex_message: Xcm = Xcm(vec![ + ClearOrigin, + ClearOrigin, + ClearOrigin, + ClearOrigin, + ClearOrigin, + ]); + + let simple_weight = XcmWeigher::weight(&mut simple_message.clone(), Weight::MAX).unwrap(); + let complex_weight = XcmWeigher::weight(&mut complex_message.clone(), Weight::MAX).unwrap(); + + assert!( + complex_weight.ref_time() > simple_weight.ref_time(), + "More instructions should result in higher weight" + ); + }); +} + +#[test] +fn weigher_respects_max_instructions() { + ExtBuilder::default().build().execute_with(|| { + // MaxInstructions is 100 in xcm_config + // Create a message with more than 100 instructions + let instructions: Vec> = (0..150).map(|_| ClearOrigin).collect(); + let message: Xcm = Xcm(instructions); + + let weight = XcmWeigher::weight(&mut message.clone(), Weight::MAX); + + // Should fail because it exceeds MaxInstructions + assert!( + weight.is_err(), + "Message exceeding MaxInstructions should fail weighing" + ); + }); +} + +#[test] +fn weigher_handles_transact_instruction() { + ExtBuilder::default().build().execute_with(|| { + // Transact instruction has variable weight based on the encoded call + let encoded_call = RuntimeCall::System(frame_system::Call::remark { + remark: vec![1, 2, 3], + }) + .encode(); + let message: Xcm = Xcm(vec![Transact { + origin_kind: OriginKind::Xcm, + call: encoded_call.into(), + fallback_max_weight: Some(Weight::from_parts(100_000_000, 10_000)), + }]); + + let weight = XcmWeigher::weight(&mut message.clone(), Weight::MAX); + + assert!( + weight.is_ok(), + "Should calculate weight for Transact instruction" + ); + }); +} + +#[test] +fn message_queue_heap_size_sufficient_for_xcm() { + ExtBuilder::default().build().execute_with(|| { + // MessageQueueHeapSize is used as MaxPageSize and HeapSize for pallet_message_queue. + // It must be large enough to hold any valid XCM message, including those + // received via HRMP. A minimum of 100KB ensures compatibility. + use moonriver_runtime::xcm_config::MessageQueueHeapSize; + + let heap_size = MessageQueueHeapSize::get(); + + assert!( + heap_size >= 100 * 1024, + "MessageQueueHeapSize ({heap_size}) should be at least 100KB for HRMP compatibility" + ); + }); +} diff --git a/runtime/moonriver/tests/xcm_config/xcm_common.rs b/runtime/moonriver/tests/xcm_config/xcm_common.rs new file mode 100644 index 00000000000..47aeb5c512c --- /dev/null +++ b/runtime/moonriver/tests/xcm_config/xcm_common.rs @@ -0,0 +1,152 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Common test utilities for XCM configuration tests. + +#![allow(dead_code)] + +pub use crate::common::*; + +use moonriver_runtime::{currency::MOVR, xcm_config::XcmExecutorConfig, RuntimeCall}; +use parity_scale_codec::Encode; +use sp_weights::Weight; +use xcm::latest::prelude::*; +use xcm_executor::XcmExecutor; + +pub const ONE_DOT: u128 = 10_000_000_000; // DOT has 10 decimals +pub const ONE_MOVR: u128 = MOVR; + +/// Execute an XCM message and return the outcome. +/// +/// This uses the real Moonriver XcmExecutorConfig to test XCM behavior. +pub fn execute_xcm(origin: Location, message: Xcm) -> Outcome { + let hash = message.using_encoded(sp_io::hashing::blake2_256); + XcmExecutor::::prepare_and_execute( + origin, + message, + &mut hash.clone(), + Weight::MAX, + Weight::zero(), + ) +} + +/// Execute an XCM message with a weight limit and return the outcome. +pub fn execute_xcm_with_weight( + origin: Location, + message: Xcm, + weight_limit: Weight, +) -> Outcome { + let hash = message.using_encoded(sp_io::hashing::blake2_256); + XcmExecutor::::prepare_and_execute( + origin, + message, + &mut hash.clone(), + weight_limit, + Weight::zero(), + ) +} + +/// Execute an XCM message with pre-credited weight. +/// +/// `TakeWeightCredit` barrier passes when the message weight is within the +/// credited amount; use this to test that barrier path. +pub fn execute_xcm_with_credit( + origin: Location, + message: Xcm, + weight_credit: Weight, +) -> Outcome { + let hash = message.using_encoded(sp_io::hashing::blake2_256); + XcmExecutor::::prepare_and_execute( + origin, + message, + &mut hash.clone(), + Weight::MAX, + weight_credit, + ) +} + +/// Helper to check if an outcome is a barrier error. +/// +/// Barrier rejections can surface as either `Outcome::Error` or +/// `Outcome::Incomplete` (when the executor begins processing before the +/// barrier rejects at instruction index 0), so both variants are matched. +pub fn is_barrier_error(outcome: &Outcome) -> bool { + matches!( + outcome, + Outcome::Error(ref e) if e.error == XcmError::Barrier + ) || matches!( + outcome, + Outcome::Incomplete { ref error, .. } if error.error == XcmError::Barrier + ) +} + +/// Helper to check if execution completed successfully +pub fn is_complete(outcome: &Outcome) -> bool { + matches!(outcome, Outcome::Complete { .. }) +} + +/// Helper to check if execution is incomplete (partially executed) +pub fn is_incomplete(outcome: &Outcome) -> bool { + matches!(outcome, Outcome::Incomplete { .. }) +} + +/// Create a simple asset from location and amount +pub fn asset(location: Location, amount: u128) -> Asset { + Asset { + id: AssetId(location), + fun: Fungible(amount), + } +} + +/// Relay chain native asset (DOT) +pub fn relay_asset(amount: u128) -> Asset { + asset(Location::parent(), amount) +} + +/// Asset from a sibling parachain's native token +pub fn sibling_asset(para_id: u32, pallet_index: u8, amount: u128) -> Asset { + asset( + Location::new(1, [Parachain(para_id), PalletInstance(pallet_index)]), + amount, + ) +} + +/// Build a message with paid execution +pub fn paid_message(fees: Asset, instructions: Vec>) -> Xcm { + let mut all_instructions = vec![ + WithdrawAsset(fees.clone().into()), + BuyExecution { + fees, + weight_limit: WeightLimit::Unlimited, + }, + ]; + all_instructions.extend(instructions); + Xcm(all_instructions) +} + +/// Build a deposit asset instruction to an account +pub fn deposit_to_account(account: [u8; 20]) -> Instruction { + DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: account, + }], + ), + } +} diff --git a/runtime/moonriver/tests/xcm_emulator/asset_hub.rs b/runtime/moonriver/tests/xcm_emulator/asset_hub.rs new file mode 100644 index 00000000000..520058ea7b7 --- /dev/null +++ b/runtime/moonriver/tests/xcm_emulator/asset_hub.rs @@ -0,0 +1,306 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Asset Hub ↔ Moonbeam transfer tests using xcm-emulator. +//! +//! These tests exercise cross-chain transfers between the real +//! `asset-hub-westend-runtime` (para 1000) and the real `moonriver-runtime` +//! (para 2004), with Westend as relay. + +use crate::network::*; +use frame_support::{assert_ok, traits::fungible::Inspect}; +use sp_core::U256; +use xcm::latest::prelude::*; +use xcm_emulator::TestExt; + +// =========================================================================== +// Setup helpers +// =========================================================================== + +/// Register DOT on Moonbeam, open HRMP between Asset Hub and Moonbeam. +fn setup_asset_hub_and_moonriver() { + init_network(); + + moonriver_execute_with(|| register_dot_asset(DOT_ASSET_ID)); + + WestendRelay::::execute_with(|| { + open_hrmp_channels(ASSET_HUB_PARA_ID, MOONRIVER_PARA_ID); + }); +} + +/// Fund ALITH on Moonbeam with DOT from the relay. +fn fund_moonriver_alith_with_dot(amount: u128) { + WestendRelay::::execute_with(|| { + assert_ok!( + westend_runtime::XcmPallet::transfer_assets_using_type_and_then( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE.clone()), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(MOONRIVER_PARA_ID)] + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(amount), + }]))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedAssetId::from(AssetId(Location::here()))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: ALITH, + }], + ), + }]))), + WeightLimit::Unlimited, + ) + ); + }); +} + +// =========================================================================== +// Tests +// =========================================================================== + +/// Transfer DOT from the relay to Asset Hub, confirming the real +/// asset-hub-westend-runtime processes DMP correctly. +#[test] +fn transfer_dot_from_relay_to_asset_hub() { + init_network(); + + let recipient = sp_runtime::AccountId32::new([2u8; 32]); + + let balance_before = asset_hub_execute_with(|| { + >::balance(&recipient) + }); + + // Send DOT from relay to Asset Hub (DOT is the native token on both). + WestendRelay::::execute_with(|| { + assert_ok!(westend_runtime::XcmPallet::limited_teleport_assets( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE.clone()), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(ASSET_HUB_PARA_ID)], + ))), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [AccountId32 { + network: None, + id: recipient.clone().into(), + }], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(ONE_DOT * 10), + }]))), + 0, + WeightLimit::Unlimited, + )); + }); + + let balance_after = asset_hub_execute_with(|| { + >::balance(&recipient) + }); + assert!( + balance_after > balance_before, + "Asset Hub account should have received DOT: before={balance_before}, after={balance_after}" + ); +} + +/// Transfer DOT from the relay to both Asset Hub (teleport) and Moonbeam +/// (reserve), confirming both chains can hold DOT originated from the +/// same relay in the same network. +#[test] +fn relay_funds_both_asset_hub_and_moonriver() { + setup_asset_hub_and_moonriver(); + + let ah_recipient = sp_runtime::AccountId32::new([2u8; 32]); + + // Fund Asset Hub via teleport. + WestendRelay::::execute_with(|| { + assert_ok!(westend_runtime::XcmPallet::limited_teleport_assets( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE.clone()), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(ASSET_HUB_PARA_ID)], + ))), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [AccountId32 { + network: None, + id: ah_recipient.clone().into(), + }], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(ONE_DOT * 10), + }]))), + 0, + WeightLimit::Unlimited, + )); + }); + + // Fund Moonbeam via reserve. + fund_moonriver_alith_with_dot(ONE_DOT * 10); + + // Both chains should have DOT. + let ah_balance = asset_hub_execute_with(|| { + >::balance(&ah_recipient) + }); + assert!( + ah_balance > 0, + "Asset Hub should have DOT (got {ah_balance})" + ); + + let moonriver_balance = moonriver_execute_with(|| { + moonriver_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonriver_runtime::AccountId::from(ALITH), + ) + .unwrap_or_default() + }); + assert!( + moonriver_balance > U256::zero(), + "Moonriver should have DOT (got {moonriver_balance})" + ); +} + +/// Transfer a trust-backed asset (e.g. USDT) from Asset Hub to Moonbeam. +/// Asset Hub is the reserve for trust-backed assets, so this is a +/// reserve-backed transfer. +#[test] +fn transfer_trust_backed_asset_from_asset_hub_to_moonriver() { + setup_asset_hub_and_moonriver(); + + // Create and mint a trust-backed asset (id=1984, "USDT") on Asset Hub. + let asset_id: u32 = 1984; + let asset_owner = sp_runtime::AccountId32::new([1u8; 32]); + let mint_amount: u128 = 1_000_000_000; // 1000 USDT (6 decimals) + + asset_hub_execute_with(|| { + assert_ok!(asset_hub_westend_runtime::Assets::force_create( + asset_hub_westend_runtime::RuntimeOrigin::root(), + asset_id.into(), + asset_owner.clone().into(), + true, + 1_000, // min_balance + )); + assert_ok!(asset_hub_westend_runtime::Assets::mint( + asset_hub_westend_runtime::RuntimeOrigin::signed(asset_owner.clone()), + asset_id.into(), + asset_owner.clone().into(), + mint_amount, + )); + }); + + // Register this asset on Moonbeam as a foreign asset. + // From Moonbeam's perspective: ../Parachain(1000)/PalletInstance(50)/GeneralIndex(1984) + const USDT_FOREIGN_ID: u128 = 10; + moonriver_execute_with(|| { + let usdt_location = xcm::latest::Location::new( + 1, + [ + Parachain(ASSET_HUB_PARA_ID), + PalletInstance(50u8), // pallet_assets instance 1 + GeneralIndex(asset_id as u128), + ], + ); + + frame_support::assert_ok!(moonriver_runtime::EvmForeignAssets::create_foreign_asset( + moonriver_runtime::RuntimeOrigin::root(), + USDT_FOREIGN_ID, + usdt_location.clone(), + 6, // USDT decimals + b"USDT".to_vec().try_into().unwrap(), + b"Tether USD".to_vec().try_into().unwrap(), + )); + + frame_support::assert_ok!(moonriver_runtime::XcmWeightTrader::add_asset( + moonriver_runtime::RuntimeOrigin::root(), + usdt_location, + 10_000_000_000_000_000_000_000_000_000u128, + )); + }); + + // Also need DOT on Asset Hub sender for fees. Fund via relay teleport. + WestendRelay::::execute_with(|| { + assert_ok!(westend_runtime::XcmPallet::limited_teleport_assets( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE.clone()), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(ASSET_HUB_PARA_ID)], + ))), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [AccountId32 { + network: None, + id: asset_owner.clone().into(), + }], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(ONE_DOT * 100), + }]))), + 0, + WeightLimit::Unlimited, + )); + }); + + // Transfer USDT from Asset Hub to Moonbeam. + // Asset Hub is the reserve for this trust-backed asset. + let transfer_amount: u128 = 500_000_000; // 500 USDT + + asset_hub_execute_with(|| { + let usdt_on_ah = Location::new(0, [PalletInstance(50u8), GeneralIndex(asset_id as u128)]); + + assert_ok!(asset_hub_westend_runtime::PolkadotXcm::transfer_assets( + asset_hub_westend_runtime::RuntimeOrigin::signed(asset_owner.clone()), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(MOONRIVER_PARA_ID)], + ))), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [AccountKey20 { + network: None, + key: ALITH, + }], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(usdt_on_ah), + fun: Fungible(transfer_amount), + }]))), + 0, + WeightLimit::Unlimited, + )); + }); + + // ALITH on Moonbeam should have received USDT as a foreign asset. + let alith_usdt = moonriver_execute_with(|| { + moonriver_runtime::EvmForeignAssets::balance( + USDT_FOREIGN_ID, + moonriver_runtime::AccountId::from(ALITH), + ) + .unwrap_or_default() + }); + assert!( + alith_usdt > U256::zero(), + "ALITH should have received USDT on Moonbeam (got {alith_usdt})" + ); +} diff --git a/runtime/moonriver/tests/xcm_emulator/main.rs b/runtime/moonriver/tests/xcm_emulator/main.rs new file mode 100644 index 00000000000..12354a0d43a --- /dev/null +++ b/runtime/moonriver/tests/xcm_emulator/main.rs @@ -0,0 +1,35 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! XCM Emulator Integration Tests (Level 2A) +//! +//! Uses the real `moonriver_runtime` connected to `westend_runtime` as relay +//! and a sibling `moonriver_runtime` instance. Tests exercise: +//! +//! - Transfers: relay→para, para→relay, para→para (DMP/UMP/XCMP) +//! - Fee collection: treasury receives execution fees, insufficient fees fail +//! - Transact: sovereign transact to relay +//! - HRMP: open, accept, close channels via `pallet_xcm_transactor` +//! - Account sufficiency: fresh accounts receive foreign assets + +#![cfg(test)] + +mod asset_hub; +mod network; +mod relay; +mod transact; +mod transfers; +mod versioning; diff --git a/runtime/moonriver/tests/xcm_emulator/network.rs b/runtime/moonriver/tests/xcm_emulator/network.rs new file mode 100644 index 00000000000..f178f082f8f --- /dev/null +++ b/runtime/moonriver/tests/xcm_emulator/network.rs @@ -0,0 +1,457 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Network declaration for xcm-emulator. +//! +//! Wires a Westend relay chain, the real Moonriver runtime (para 2004), +//! and a sibling Moonriver instance (para 2005) into a single test network. + +use crate::relay; + +use frame_support::traits::OnInitialize; +use nimbus_primitives::{NimbusId, NIMBUS_ENGINE_ID}; +use parity_scale_codec::Encode; +use sp_runtime::{Digest, DigestItem}; +use xcm_emulator::decl_test_networks; +use xcm_emulator::decl_test_parachains; +use xcm_emulator::decl_test_relay_chains; +use xcm_emulator::BlockProducer; +use xcm_emulator::TestExt; + +/// `BlockProducer` for Moonriver's Nimbus-based consensus. +/// +/// xcm-emulator defaults to an Aura-backed producer; Moonriver does not use +/// pallet-aura, so we plug in a Nimbus pre-runtime digest with a 6s block +/// interval (matching `moonriver_runtime::MILLISECS_PER_BLOCK`). +pub struct MoonriverBlockProducer; + +impl BlockProducer for MoonriverBlockProducer { + fn slot_duration() -> u64 { + moonriver_runtime::MILLISECS_PER_BLOCK + } + + fn pre_runtime_digest(_relay_block_number: u32) -> Digest { + let author = NimbusId::from(sp_core::sr25519::Public::from_raw([1u8; 32])); + Digest { + logs: vec![DigestItem::PreRuntime(NIMBUS_ENGINE_ID, author.encode())], + } + } +} + +pub const ASSET_HUB_PARA_ID: u32 = 1000; +pub const MOONRIVER_PARA_ID: u32 = 2004; +pub const SIBLING_PARA_ID: u32 = 2005; + +// ---- Well-known test accounts (20-byte) ------------------------------------ +pub const ALITH: [u8; 20] = [1u8; 20]; +pub const BALTATHAR: [u8; 20] = [2u8; 20]; +// ---- Well-known relay accounts (32-byte) ----------------------------------- +pub const RELAY_ALICE: sp_runtime::AccountId32 = sp_runtime::AccountId32::new([1u8; 32]); + +// ---- Asset ID constants ---------------------------------------------------- +pub const DOT_ASSET_ID: u128 = 1; +pub const MOVR_ASSET_ID: u128 = 2; + +// ---- DOT constants --------------------------------------------------------- +pub const ONE_DOT: u128 = 10_000_000_000; // 10 decimals + +// --------------------------------------------------------------------------- +// Relay chain declaration (Westend runtime) +// --------------------------------------------------------------------------- +decl_test_relay_chains! { + #[api_version(15)] + pub struct WestendRelay { + genesis = relay::relay_genesis(), + on_init = (), + runtime = westend_runtime, + core = { + SovereignAccountOf: westend_runtime::xcm_config::LocationConverter, + }, + pallets = { + XcmPallet: westend_runtime::XcmPallet, + Balances: westend_runtime::Balances, + Hrmp: westend_runtime::Hrmp, + Utility: westend_runtime::Utility, + } + } +} + +// --------------------------------------------------------------------------- +// Moonriver parachain declaration (para 2004) +// --------------------------------------------------------------------------- +decl_test_parachains! { + pub struct MoonriverPara { + genesis = moonriver_genesis(MOONRIVER_PARA_ID), + on_init = { + crate::network::satisfy_moonriver_inherents(); + }, + runtime = moonriver_runtime, + core = { + XcmpMessageHandler: moonriver_runtime::XcmpQueue, + LocationToAccountId: moonriver_runtime::xcm_config::LocationToAccountId, + ParachainInfo: moonriver_runtime::ParachainInfo, + MessageOrigin: cumulus_primitives_core::AggregateMessageOrigin, + BlockProducer: crate::network::MoonriverBlockProducer, + }, + pallets = { + PolkadotXcm: moonriver_runtime::PolkadotXcm, + Balances: moonriver_runtime::Balances, + EvmForeignAssets: moonriver_runtime::EvmForeignAssets, + XcmWeightTrader: moonriver_runtime::XcmWeightTrader, + XcmTransactor: moonriver_runtime::XcmTransactor, + Treasury: moonriver_runtime::Treasury, + EthereumXcm: moonriver_runtime::EthereumXcm, + Proxy: moonriver_runtime::Proxy, + EVM: moonriver_runtime::EVM, + } + } +} + +// --------------------------------------------------------------------------- +// Sibling parachain declaration (para 2005) — another Moonriver instance +// --------------------------------------------------------------------------- +decl_test_parachains! { + pub struct SiblingPara { + genesis = moonriver_genesis(SIBLING_PARA_ID), + on_init = { + crate::network::satisfy_moonriver_inherents(); + }, + runtime = moonriver_runtime, + core = { + XcmpMessageHandler: moonriver_runtime::XcmpQueue, + LocationToAccountId: moonriver_runtime::xcm_config::LocationToAccountId, + ParachainInfo: moonriver_runtime::ParachainInfo, + MessageOrigin: cumulus_primitives_core::AggregateMessageOrigin, + BlockProducer: crate::network::MoonriverBlockProducer, + }, + pallets = { + PolkadotXcm: moonriver_runtime::PolkadotXcm, + Balances: moonriver_runtime::Balances, + EvmForeignAssets: moonriver_runtime::EvmForeignAssets, + XcmWeightTrader: moonriver_runtime::XcmWeightTrader, + XcmTransactor: moonriver_runtime::XcmTransactor, + Treasury: moonriver_runtime::Treasury, + EthereumXcm: moonriver_runtime::EthereumXcm, + Proxy: moonriver_runtime::Proxy, + EVM: moonriver_runtime::EVM, + } + } +} + +// --------------------------------------------------------------------------- +// Asset Hub Westend declaration (para 1000, real asset-hub-westend-runtime) +// --------------------------------------------------------------------------- +decl_test_parachains! { + pub struct AssetHubPara { + genesis = asset_hub_genesis(), + on_init = { + asset_hub_westend_runtime::AuraExt::on_initialize(1); + }, + runtime = asset_hub_westend_runtime, + core = { + XcmpMessageHandler: asset_hub_westend_runtime::XcmpQueue, + LocationToAccountId: asset_hub_westend_runtime::xcm_config::LocationToAccountId, + ParachainInfo: asset_hub_westend_runtime::ParachainInfo, + MessageOrigin: cumulus_primitives_core::AggregateMessageOrigin, + }, + pallets = { + PolkadotXcm: asset_hub_westend_runtime::PolkadotXcm, + Balances: asset_hub_westend_runtime::Balances, + Assets: asset_hub_westend_runtime::Assets, + ForeignAssets: asset_hub_westend_runtime::ForeignAssets, + } + } +} + +// --------------------------------------------------------------------------- +// Network declaration +// --------------------------------------------------------------------------- +decl_test_networks! { + pub struct PolkadotMoonbeamNet { + relay_chain = WestendRelay, + parachains = vec![ + AssetHubPara, + MoonriverPara, + SiblingPara, + ], + bridge = () + } +} + +// =========================================================================== +// Helpers +// =========================================================================== + +/// Execute a closure on the Moonriver parachain (para 2004), automatically +/// satisfying mandatory inherent checks. +pub fn moonriver_execute_with(f: impl FnOnce() -> R) -> R { + MoonriverPara::::execute_with(|| { + satisfy_moonriver_inherents(); + f() + }) +} + +/// Execute a closure on the Sibling parachain (para 2005), automatically +/// satisfying mandatory inherent checks. +pub fn sibling_execute_with(f: impl FnOnce() -> R) -> R { + SiblingPara::::execute_with(|| { + satisfy_moonriver_inherents(); + f() + }) +} + +/// Execute a closure on Asset Hub (para 1000). +pub fn asset_hub_execute_with(f: impl FnOnce() -> R) -> R { + AssetHubPara::::execute_with(f) +} + +/// Patch storage to satisfy Moonriver's mandatory inherent checks. +/// Called automatically by [`moonriver_execute_with`] / [`sibling_execute_with`]. +pub(crate) fn satisfy_moonriver_inherents() { + pallet_author_inherent::Author::::put( + moonriver_runtime::AccountId::from([1u8; 20]), + ); + pallet_author_inherent::InherentIncluded::::put(true); + + frame_support::storage::unhashed::put( + &frame_support::storage::storage_prefix(b"Randomness", b"InherentIncluded"), + &(), + ); + frame_support::storage::unhashed::kill(&frame_support::storage::storage_prefix( + b"Randomness", + b"NotFirstBlock", + )); +} + +/// Initialise network and clear `NotFirstBlock` on all parachains. +pub fn init_network() { + // Trigger `Parachain::init()` on every chain by executing on relay. + WestendRelay::::execute_with(|| {}); + + // Clear NotFirstBlock so VRF verification is skipped in subsequent blocks. + MoonriverPara::::ext_wrapper(|| { + frame_support::storage::unhashed::kill(&frame_support::storage::storage_prefix( + b"Randomness", + b"NotFirstBlock", + )); + }); + SiblingPara::::ext_wrapper(|| { + frame_support::storage::unhashed::kill(&frame_support::storage::storage_prefix( + b"Randomness", + b"NotFirstBlock", + )); + }); +} + +/// Register DOT as a foreign asset on a Moonbeam-runtime chain and configure +/// its price in the XCM weight trader. Call inside `moonriver_execute_with` or +/// `sibling_execute_with`. +pub fn register_dot_asset(asset_id: u128) { + let dot_location = xcm::latest::Location::parent(); + + frame_support::assert_ok!(moonriver_runtime::EvmForeignAssets::create_foreign_asset( + moonriver_runtime::RuntimeOrigin::root(), + asset_id, + dot_location.clone(), + 10, + b"DOT".to_vec().try_into().unwrap(), + b"Polkadot".to_vec().try_into().unwrap(), + )); + + // relative_price large enough so that 10 DOT covers XCM execution fees. + frame_support::assert_ok!(moonriver_runtime::XcmWeightTrader::add_asset( + moonriver_runtime::RuntimeOrigin::root(), + dot_location, + 10_000_000_000_000_000_000_000_000_000u128, // 10^28 + )); +} + +/// Configure `pallet_xcm_transactor` relay indices for Westend. +/// Call inside `moonriver_execute_with` or `sibling_execute_with`. +pub fn set_westend_relay_indices() { + use frame_support::traits::PalletInfoAccess; + use pallet_xcm_transactor::relay_indices::RelayChainIndices; + + // Validate pallet indices against the Westend runtime so we fail fast if + // they drift after a relay upgrade. + let staking_idx = westend_runtime::Staking::index() as u8; + let utility_idx = westend_runtime::Utility::index() as u8; + let hrmp_idx = westend_runtime::Hrmp::index() as u8; + + assert_eq!(staking_idx, 6u8, "Westend Staking pallet index has changed"); + assert_eq!( + utility_idx, 16u8, + "Westend Utility pallet index has changed" + ); + assert_eq!(hrmp_idx, 51u8, "Westend Hrmp pallet index has changed"); + + let indices = RelayChainIndices { + staking: staking_idx, + utility: utility_idx, + hrmp: hrmp_idx, + // Call indices within staking pallet: + bond: 0u8, + bond_extra: 1u8, + unbond: 2u8, + withdraw_unbonded: 3u8, + validate: 4u8, + nominate: 5u8, + chill: 6u8, + set_payee: 7u8, + set_controller: 8u8, + rebond: 19u8, + // Utility::as_derivative + as_derivative: 1u8, + // HRMP call indices: + init_open_channel: 0u8, + accept_open_channel: 1u8, + close_channel: 2u8, + cancel_open_request: 6u8, + }; + + pallet_xcm_transactor::RelayIndices::::put(indices); +} + +/// Open HRMP channels between two parachains on the relay. +/// Must be called inside `WestendRelay::execute_with`. +pub fn open_hrmp_channels(sender: u32, recipient: u32) { + use frame_support::assert_ok; + + assert_ok!(westend_runtime::Hrmp::force_open_hrmp_channel( + westend_runtime::RuntimeOrigin::root(), + sender.into(), + recipient.into(), + 8, // max_capacity + 1024, // max_message_size + )); + assert_ok!(westend_runtime::Hrmp::force_open_hrmp_channel( + westend_runtime::RuntimeOrigin::root(), + recipient.into(), + sender.into(), + 8, + 1024, + )); + assert_ok!(westend_runtime::Hrmp::force_process_hrmp_open( + westend_runtime::RuntimeOrigin::root(), + 2, + )); +} + +// --------------------------------------------------------------------------- +// Moonbeam genesis helper +// --------------------------------------------------------------------------- + +fn asset_hub_genesis() -> sp_core::storage::Storage { + use sp_runtime::BuildStorage; + + let endowment: u128 = 1_000_000_000_000_000; // 1 000 WND (12 decimals) + + let mut t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + + parachain_info::GenesisConfig:: { + parachain_id: ASSET_HUB_PARA_ID.into(), + _config: Default::default(), + } + .assimilate_storage(&mut t) + .unwrap(); + + // Pre-fund Asset Hub's XCM checking account so relay→AH teleports of the + // native (WND) asset don't fail with `NotWithdrawable`. Asset Hub tracks + // teleported-out WND in this account; in a fresh test genesis it starts + // at zero, so an incoming teleport cannot withdraw against it. + let checking_account: sp_runtime::AccountId32 = + sp_runtime::traits::AccountIdConversion::into_account_truncating(&frame_support::PalletId( + *b"py/xcmch", + )); + + pallet_balances::GenesisConfig:: { + balances: vec![ + (sp_runtime::AccountId32::new([1u8; 32]), endowment), + (sp_runtime::AccountId32::new([2u8; 32]), endowment), + (checking_account, endowment), + ], + ..Default::default() + } + .assimilate_storage(&mut t) + .unwrap(); + + // Populate at least one Aura authority. Asset Hub runs on pallet-aura + // and `FindAuthor` panics on `slot % authorities_len()` if it's empty. + xcm_emulator::pallet_aura::GenesisConfig:: { + authorities: vec![parachains_common::AuraId::from( + sp_core::sr25519::Public::from_raw([1u8; 32]), + )], + } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_xcm::GenesisConfig:: { + safe_xcm_version: Some(xcm::latest::VERSION), + ..Default::default() + } + .assimilate_storage(&mut t) + .unwrap(); + + t +} + +fn moonriver_genesis(para_id: u32) -> sp_core::storage::Storage { + use moonriver_runtime::{currency::MOVR, AccountId, Runtime}; + use sp_runtime::BuildStorage; + + let mut t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + + parachain_info::GenesisConfig:: { + parachain_id: para_id.into(), + _config: Default::default(), + } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_balances::GenesisConfig:: { + balances: vec![ + (AccountId::from(ALITH), MOVR * 10_000), + (AccountId::from(BALTATHAR), MOVR * 10_000), + ], + dev_accounts: None, + } + .assimilate_storage(&mut t) + .unwrap(); + + // Map the NimbusId emitted by `MoonriverBlockProducer` to ALITH so + // `pallet_author_inherent::FindAuthor` can resolve a block author. + pallet_author_mapping::GenesisConfig:: { + mappings: vec![( + NimbusId::from(sp_core::sr25519::Public::from_raw([1u8; 32])), + AccountId::from(ALITH), + )], + } + .assimilate_storage(&mut t) + .unwrap(); + + pallet_xcm::GenesisConfig:: { + safe_xcm_version: Some(xcm::latest::VERSION), + ..Default::default() + } + .assimilate_storage(&mut t) + .unwrap(); + + t +} diff --git a/runtime/moonriver/tests/xcm_emulator/relay.rs b/runtime/moonriver/tests/xcm_emulator/relay.rs new file mode 100644 index 00000000000..4a39c512576 --- /dev/null +++ b/runtime/moonriver/tests/xcm_emulator/relay.rs @@ -0,0 +1,94 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! Relay chain genesis for xcm-emulator tests. +//! +//! Uses the full `westend_runtime` so we get real DMP routing, HRMP, and +//! the `ParachainHost` runtime API the emulator requires. + +pub use westend_runtime; + +use parity_scale_codec::Encode; +use sp_core::storage::Storage; +use sp_runtime::{traits::AccountIdConversion, AccountId32, BuildStorage}; + +use crate::network::{ASSET_HUB_PARA_ID, MOONRIVER_PARA_ID, SIBLING_PARA_ID}; + +/// Build relay `Storage` with both parachains registered and funded. +pub fn relay_genesis() -> Storage { + let asset_hub_sovereign: AccountId32 = + polkadot_parachain::primitives::Id::from(ASSET_HUB_PARA_ID).into_account_truncating(); + let moonriver_sovereign: AccountId32 = + polkadot_parachain::primitives::Id::from(MOONRIVER_PARA_ID).into_account_truncating(); + let sibling_sovereign: AccountId32 = + polkadot_parachain::primitives::Id::from(SIBLING_PARA_ID).into_account_truncating(); + let endowment: u128 = 1_000_000_000_000_000; // 1 000 WND (12 decimals) + + let mut host_config = polkadot_runtime_parachains::configuration::HostConfiguration::default(); + host_config.max_downward_message_size = 1 << 20; + host_config.max_upward_message_size = 1 << 16; + host_config.max_upward_queue_count = 100; + host_config.max_upward_message_num_per_candidate = 10; + host_config.hrmp_max_message_num_per_candidate = 10; + host_config.hrmp_channel_max_capacity = 8; + host_config.hrmp_channel_max_total_size = 8 * 1024; + host_config.hrmp_channel_max_message_size = 1024; + host_config.hrmp_max_parachain_outbound_channels = 10; + host_config.hrmp_max_parachain_inbound_channels = 10; + + let genesis_config = westend_runtime::RuntimeGenesisConfig { + balances: westend_runtime::BalancesConfig { + balances: vec![ + (AccountId32::new([1u8; 32]), endowment), + (AccountId32::new([2u8; 32]), endowment), + (asset_hub_sovereign, endowment), + (moonriver_sovereign, endowment), + (sibling_sovereign, endowment), + ], + ..Default::default() + }, + configuration: westend_runtime::ConfigurationConfig { + config: host_config, + }, + ..Default::default() + }; + + let mut storage = genesis_config + .build_storage() + .expect("Should build relay genesis storage"); + + // Register both parachains so DMP and HRMP consider them valid. + use frame_support::storage::generator::StorageMap; + for para_id in [ASSET_HUB_PARA_ID, MOONRIVER_PARA_ID, SIBLING_PARA_ID] { + let pid = polkadot_parachain::primitives::Id::from(para_id); + let head_data = polkadot_parachain::primitives::HeadData(vec![0u8; 32]); + let key = polkadot_runtime_parachains::paras::Heads::::storage_map_final_key(pid); + storage.top.insert(key, head_data.encode()); + + // Also register the ParaLifecycle as Parachain so HRMP considers them valid. + // ParaLifecycles is pub(super) so we cannot use storage_map_final_key(); + // reconstruct the key manually (Twox64Concat hasher on ParaId). + let prefix = frame_support::storage::storage_prefix(b"Paras", b"ParaLifecycles"); + let encoded_id = pid.encode(); + let mut full_key = prefix.to_vec(); + full_key.extend(&sp_io::hashing::twox_64(&encoded_id)); + full_key.extend(&encoded_id); + let lifecycle = polkadot_runtime_parachains::paras::ParaLifecycle::Parachain; + storage.top.insert(full_key, lifecycle.encode()); + } + + storage +} diff --git a/runtime/moonriver/tests/xcm_emulator/transact.rs b/runtime/moonriver/tests/xcm_emulator/transact.rs new file mode 100644 index 00000000000..5501df620cb --- /dev/null +++ b/runtime/moonriver/tests/xcm_emulator/transact.rs @@ -0,0 +1,1209 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonriver. + +// Moonriver 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. + +// Moonriver 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 Moonriver. If not, see . + +//! XcmTransactor tests using the **real** Moonriver runtime against Westend relay. +//! +//! Covers: +//! - transact_through_sovereign (relay) — basic, fee_payer=None, custom fee/weight, refund +//! - transact_through_derivative (relay) — basic, custom fee/weight, refund +//! - transact_through_signed (relay) — basic, custom fee/weight, refund +//! - HRMP channel management (init/accept/close) + +use crate::network::*; +use frame_support::{ + assert_ok, + traits::fungible::{Inspect, Mutate}, +}; +use pallet_xcm_transactor::{Currency, CurrencyPayment, HrmpOperation, TransactWeights}; +use parity_scale_codec::Encode; +use sp_core::U256; +use xcm::latest::prelude::*; +use xcm_emulator::{RelayChain, TestExt}; +use xcm_executor::traits::ConvertLocation; + +// =========================================================================== +// Setup +// =========================================================================== + +fn setup_transactor() { + init_network(); + + moonriver_execute_with(|| { + register_dot_asset(DOT_ASSET_ID); + set_westend_relay_indices(); + + // Configure transact info for the relay destination. + assert_ok!(moonriver_runtime::XcmTransactor::set_transact_info( + moonriver_runtime::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::from(Location::parent())), + 3_000u64.into(), // extra_weight (relay charges per instruction) + 20_000_000_000u64.into(), // max_weight + // 4 instructions in transact_through_signed + Some(4_000u64.into()), + )); + }); + + // Fund Moonriver's sovereign on relay so it can pay fees for UMP transacts. + WestendRelay::::execute_with(|| { + // The sovereign is already funded via relay genesis (endowment). + }); +} + +/// Encode a `system::remark_with_event` call for the Westend relay. +fn relay_remark_call() -> Vec { + westend_runtime::RuntimeCall::System( + frame_system::Call::::remark_with_event { + remark: b"hello from Moonriver".to_vec(), + }, + ) + .encode() +} + +/// Derive the relay account for a signed XCM origin from a parachain user. +/// The XCM `DescendOrigin(AccountKey20(key))` shifts the origin to +/// `Parachain(para_id)/AccountKey20(key)`, which the relay's `LocationConverter` +/// hashes into a 32-byte account. +fn relay_derived_account(para_id: u32, key: [u8; 20]) -> sp_runtime::AccountId32 { + let location = Location::new(0, [Parachain(para_id), AccountKey20 { network: None, key }]); + westend_runtime::xcm_config::LocationConverter::convert_location(&location) + .expect("Should derive relay account from parachain signed origin") +} + +/// Assert that the relay processed a UMP message and emitted a Remarked event. +fn assert_relay_remark_executed() { + WestendRelay::::execute_with(|| { + let events = westend_runtime::System::events(); + + let was_processed = events.iter().any(|e| { + matches!( + &e.event, + westend_runtime::RuntimeEvent::MessageQueue( + pallet_message_queue::Event::Processed { success: true, .. } + ) + ) + }); + assert!( + was_processed, + "Relay should have successfully processed the UMP transact" + ); + + let has_remark = events.iter().any(|e| { + matches!( + &e.event, + westend_runtime::RuntimeEvent::System(frame_system::Event::Remarked { .. }) + ) + }); + assert!(has_remark, "Relay should have emitted a Remarked event"); + }); +} + +/// Send DOT from relay to Moonriver ALITH. +fn fund_moonriver_alith_with_dot(amount: u128) { + WestendRelay::::execute_with(|| { + assert_ok!( + westend_runtime::XcmPallet::transfer_assets_using_type_and_then( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(MOONRIVER_PARA_ID)] + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(amount), + }]))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedAssetId::from(AssetId(Location::here()))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: ALITH + }], + ), + }]))), + WeightLimit::Unlimited, + ) + ); + }); +} + +// =========================================================================== +// Transact through sovereign (para → relay) +// =========================================================================== + +#[test] +fn transact_through_sovereign_to_relay() { + setup_transactor(); + fund_moonriver_alith_with_dot(ONE_DOT * 1000); + + // Check the sovereign account balance on relay before transact. + let sovereign = WestendRelay::::execute_with(|| { + WestendRelay::::sovereign_account_id_of(Location::new( + 0, + [Parachain(MOONRIVER_PARA_ID)], + )) + }); + let sovereign_before = WestendRelay::::execute_with(|| { + >::balance(&sovereign) + }); + assert!( + sovereign_before > 0, + "Sovereign should be funded from genesis" + ); + + moonriver_execute_with(|| { + assert_ok!( + moonriver_runtime::XcmTransactor::transact_through_sovereign( + moonriver_runtime::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::from(Location::parent())), + Some(moonriver_runtime::AccountId::from(ALITH)), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent() + ))), + fee_amount: Some(ONE_DOT), // explicit fee + }, + relay_remark_call(), + OriginKind::SovereignAccount, + TransactWeights { + transact_required_weight_at_most: 1_000_000_000u64.into(), + overall_weight: Some(Limited(2_000_000_000u64.into())), + }, + false, + ) + ); + }); + + assert_relay_remark_executed(); + + // Verify the sovereign paid fees for the XCM execution. + let sovereign_after = WestendRelay::::execute_with(|| { + >::balance(&sovereign) + }); + assert!( + sovereign_after <= sovereign_before, + "Sovereign should have spent DOT: before={sovereign_before}, after={sovereign_after}" + ); +} + +// =========================================================================== +// HRMP: open and close channels via XcmTransactor +// =========================================================================== + +#[test] +fn hrmp_init_accept_close_via_xcm_transactor() { + init_network(); + + moonriver_execute_with(|| { + register_dot_asset(DOT_ASSET_ID); + set_westend_relay_indices(); + }); + sibling_execute_with(|| { + register_dot_asset(DOT_ASSET_ID); + set_westend_relay_indices(); + }); + + use pallet_xcm_transactor::{HrmpInitParams, HrmpOperation}; + + // Step 1: Moonriver requests to open channel to sibling. + moonriver_execute_with(|| { + assert_ok!(moonriver_runtime::XcmTransactor::hrmp_manage( + moonriver_runtime::RuntimeOrigin::root(), + HrmpOperation::InitOpen(HrmpInitParams { + para_id: SIBLING_PARA_ID.into(), + proposed_max_capacity: 8, + proposed_max_message_size: 1024, + }), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent() + ))), + fee_amount: Some(ONE_DOT * 100), + }, + TransactWeights { + transact_required_weight_at_most: 5_000_000_000u64.into(), + overall_weight: Some(Limited(10_000_000_000u64.into())), + }, + )); + }); + + // Verify the open-channel request arrived on relay. + WestendRelay::::execute_with(|| { + let events = westend_runtime::System::events(); + let has_open_request = events.iter().any(|e| { + matches!( + &e.event, + westend_runtime::RuntimeEvent::Hrmp( + polkadot_runtime_parachains::hrmp::Event::OpenChannelRequested { .. } + ) + ) + }); + assert!( + has_open_request, + "Relay should have emitted OpenChannelRequested" + ); + }); + + // Step 2: Sibling accepts the channel. + sibling_execute_with(|| { + assert_ok!(moonriver_runtime::XcmTransactor::hrmp_manage( + moonriver_runtime::RuntimeOrigin::root(), + HrmpOperation::Accept { + para_id: MOONRIVER_PARA_ID.into(), + }, + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent() + ))), + fee_amount: Some(ONE_DOT * 100), + }, + TransactWeights { + transact_required_weight_at_most: 5_000_000_000u64.into(), + overall_weight: Some(Limited(10_000_000_000u64.into())), + }, + )); + }); + + WestendRelay::::execute_with(|| { + let events = westend_runtime::System::events(); + let has_accept = events.iter().any(|e| { + matches!( + &e.event, + westend_runtime::RuntimeEvent::Hrmp( + polkadot_runtime_parachains::hrmp::Event::OpenChannelAccepted { .. } + ) + ) + }); + assert!(has_accept, "Relay should have emitted OpenChannelAccepted"); + }); + + // Step 3: Process the pending open requests and verify the channel is established. + WestendRelay::::execute_with(|| { + assert_ok!(westend_runtime::Hrmp::force_process_hrmp_open( + westend_runtime::RuntimeOrigin::root(), + 1, + )); + + use polkadot_runtime_parachains::hrmp; + let channel = + hrmp::HrmpChannels::::get(xcm_emulator::HrmpChannelId { + sender: MOONRIVER_PARA_ID.into(), + recipient: SIBLING_PARA_ID.into(), + }); + assert!( + channel.is_some(), + "HRMP channel Moonriver → Sibling should be established" + ); + }); +} + +// =========================================================================== +// HRMP: close channel via XcmTransactor +// =========================================================================== + +#[test] +fn hrmp_close_via_xcm_transactor() { + init_network(); + + moonriver_execute_with(|| { + register_dot_asset(DOT_ASSET_ID); + set_westend_relay_indices(); + }); + + // Force-open a channel so we can close it. + WestendRelay::::execute_with(|| { + open_hrmp_channels(MOONRIVER_PARA_ID, SIBLING_PARA_ID); + }); + + // Close the channel from Moonriver side via XcmTransactor. + moonriver_execute_with(|| { + assert_ok!(moonriver_runtime::XcmTransactor::hrmp_manage( + moonriver_runtime::RuntimeOrigin::root(), + HrmpOperation::Close(xcm_emulator::HrmpChannelId { + sender: MOONRIVER_PARA_ID.into(), + recipient: SIBLING_PARA_ID.into(), + }), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent(), + ))), + fee_amount: Some(ONE_DOT * 100), + }, + TransactWeights { + transact_required_weight_at_most: 5_000_000_000u64.into(), + overall_weight: Some(Limited(10_000_000_000u64.into())), + }, + )); + }); + + // Verify the close event on relay. + WestendRelay::::execute_with(|| { + let events = westend_runtime::System::events(); + let has_close = events.iter().any(|e| { + matches!( + &e.event, + westend_runtime::RuntimeEvent::Hrmp( + polkadot_runtime_parachains::hrmp::Event::ChannelClosed { .. } + ) + ) + }); + assert!(has_close, "Relay should have emitted ChannelClosed"); + }); +} + +// =========================================================================== +// Transact through sovereign: fee_payer = None +// =========================================================================== + +#[test] +fn transact_through_sovereign_fee_payer_none() { + setup_transactor(); + + // With fee_payer = None, no local withdraw happens — only the sovereign on + // relay pays. The sovereign must be funded from genesis. + moonriver_execute_with(|| { + assert_ok!( + moonriver_runtime::XcmTransactor::transact_through_sovereign( + moonriver_runtime::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::from(Location::parent())), + None, // no fee payer — no local withdraw + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent(), + ))), + fee_amount: Some(ONE_DOT), + }, + relay_remark_call(), + OriginKind::SovereignAccount, + TransactWeights { + transact_required_weight_at_most: 1_000_000_000u64.into(), + overall_weight: Some(Limited(2_000_000_000u64.into())), + }, + false, + ) + ); + }); + + assert_relay_remark_executed(); +} + +// =========================================================================== +// Transact through sovereign: custom fee & weight (no refund) +// =========================================================================== + +#[test] +fn transact_through_sovereign_custom_fee_weight() { + setup_transactor(); + fund_moonriver_alith_with_dot(ONE_DOT * 1000); + + moonriver_execute_with(|| { + assert_ok!( + moonriver_runtime::XcmTransactor::transact_through_sovereign( + moonriver_runtime::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::from(Location::parent())), + Some(moonriver_runtime::AccountId::from(ALITH)), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent(), + ))), + fee_amount: Some(ONE_DOT * 5), // explicit larger fee + }, + relay_remark_call(), + OriginKind::SovereignAccount, + TransactWeights { + transact_required_weight_at_most: 2_000_000_000u64.into(), + overall_weight: Some(Limited(4_000_000_000u64.into())), + }, + false, + ) + ); + }); + + assert_relay_remark_executed(); +} + +// =========================================================================== +// Transact through sovereign: custom fee, weight & refund +// =========================================================================== + +#[test] +fn transact_through_sovereign_custom_fee_weight_refund() { + setup_transactor(); + fund_moonriver_alith_with_dot(ONE_DOT * 1000); + + let sovereign_before = WestendRelay::::execute_with(|| { + let sovereign = WestendRelay::::sovereign_account_id_of( + Location::new(0, [Parachain(MOONRIVER_PARA_ID)]), + ); + >::balance(&sovereign) + }); + + moonriver_execute_with(|| { + assert_ok!( + moonriver_runtime::XcmTransactor::transact_through_sovereign( + moonriver_runtime::RuntimeOrigin::root(), + Box::new(xcm::VersionedLocation::from(Location::parent())), + Some(moonriver_runtime::AccountId::from(ALITH)), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent(), + ))), + fee_amount: Some(ONE_DOT * 10), // overpay to test refund + }, + relay_remark_call(), + OriginKind::SovereignAccount, + TransactWeights { + transact_required_weight_at_most: 2_000_000_000u64.into(), + overall_weight: Some(Limited(4_000_000_000u64.into())), + }, + true, // refund = true + ) + ); + }); + + assert_relay_remark_executed(); + + // With refund=true, leftover fees are deposited back to the sovereign. + // The sovereign should have lost less than the full 10 DOT fee. + let sovereign_after = WestendRelay::::execute_with(|| { + let sovereign = WestendRelay::::sovereign_account_id_of( + Location::new(0, [Parachain(MOONRIVER_PARA_ID)]), + ); + >::balance(&sovereign) + }); + let fee_spent = sovereign_before.saturating_sub(sovereign_after); + assert!( + fee_spent < ONE_DOT * 10, + "With refund, sovereign should spend less than the full fee: spent={fee_spent}" + ); +} + +// =========================================================================== +// Transact through signed (para → relay) +// =========================================================================== + +#[test] +fn transact_through_signed_to_relay() { + setup_transactor(); + fund_moonriver_alith_with_dot(ONE_DOT * 1000); + + let derived_account = relay_derived_account(MOONRIVER_PARA_ID, ALITH); + + // Fund the derived account on relay so it can pay XCM fees. + WestendRelay::::execute_with(|| { + assert_ok!(westend_runtime::Balances::transfer_allow_death( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE), + derived_account.clone().into(), + ONE_DOT * 100, + )); + }); + + moonriver_execute_with(|| { + assert_ok!(moonriver_runtime::XcmTransactor::transact_through_signed( + moonriver_runtime::RuntimeOrigin::signed(moonriver_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::parent())), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent(), + ))), + fee_amount: Some(ONE_DOT * 10), + }, + relay_remark_call(), + TransactWeights { + transact_required_weight_at_most: 1_000_000_000u64.into(), + overall_weight: Some(Limited(4_000_000_000u64.into())), + }, + false, + )); + }); + + assert_relay_remark_executed(); +} + +// =========================================================================== +// Transact through signed: custom fee & weight +// =========================================================================== + +#[test] +fn transact_through_signed_custom_fee_weight() { + setup_transactor(); + fund_moonriver_alith_with_dot(ONE_DOT * 1000); + + let derived_account = relay_derived_account(MOONRIVER_PARA_ID, ALITH); + + WestendRelay::::execute_with(|| { + assert_ok!(westend_runtime::Balances::transfer_allow_death( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE), + derived_account.clone().into(), + ONE_DOT * 100, + )); + }); + + moonriver_execute_with(|| { + assert_ok!(moonriver_runtime::XcmTransactor::transact_through_signed( + moonriver_runtime::RuntimeOrigin::signed(moonriver_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::parent())), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent(), + ))), + fee_amount: Some(ONE_DOT * 5), + }, + relay_remark_call(), + TransactWeights { + transact_required_weight_at_most: 2_000_000_000u64.into(), + overall_weight: Some(Limited(6_000_000_000u64.into())), + }, + false, + )); + }); + + assert_relay_remark_executed(); +} + +// =========================================================================== +// Transact through signed: custom fee, weight & refund +// =========================================================================== + +#[test] +fn transact_through_signed_custom_fee_weight_refund() { + setup_transactor(); + fund_moonriver_alith_with_dot(ONE_DOT * 1000); + + let derived_account = relay_derived_account(MOONRIVER_PARA_ID, ALITH); + + WestendRelay::::execute_with(|| { + assert_ok!(westend_runtime::Balances::transfer_allow_death( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE), + derived_account.clone().into(), + ONE_DOT * 100, + )); + }); + + let derived_before = WestendRelay::::execute_with(|| { + >::balance(&derived_account) + }); + + moonriver_execute_with(|| { + assert_ok!(moonriver_runtime::XcmTransactor::transact_through_signed( + moonriver_runtime::RuntimeOrigin::signed(moonriver_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::parent())), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent(), + ))), + fee_amount: Some(ONE_DOT * 20), // overpay + }, + relay_remark_call(), + TransactWeights { + transact_required_weight_at_most: 2_000_000_000u64.into(), + overall_weight: Some(Limited(6_000_000_000u64.into())), + }, + true, // refund = true + )); + }); + + assert_relay_remark_executed(); + + // With refund, the derived account should get surplus back. + let derived_after = WestendRelay::::execute_with(|| { + >::balance(&derived_account) + }); + let fee_spent = derived_before.saturating_sub(derived_after); + assert!( + fee_spent < ONE_DOT * 20, + "With refund, derived account should spend less than the full fee: spent={fee_spent}" + ); +} + +// =========================================================================== +// Transact through derivative +// =========================================================================== + +/// Setup for derivative transact tests. +/// Registers ALITH as the owner of derivative index 0 and funds the derivative +/// sub-account on the relay. +fn setup_derivative() { + setup_transactor(); + fund_moonriver_alith_with_dot(ONE_DOT * 1000); + + let derivative_index: u16 = 0; + + // Register ALITH as the owner of index 0. + moonriver_execute_with(|| { + assert_ok!(moonriver_runtime::XcmTransactor::register( + moonriver_runtime::RuntimeOrigin::root(), + moonriver_runtime::AccountId::from(ALITH), + derivative_index, + )); + }); + + // Fund the derivative account on relay. + // The derivative is computed from the sovereign account of Moonriver parachain. + WestendRelay::::execute_with(|| { + let sovereign = WestendRelay::::sovereign_account_id_of( + Location::new(0, [Parachain(MOONRIVER_PARA_ID)]), + ); + let derivative = pallet_utility::derivative_account_id(sovereign, derivative_index); + assert_ok!(westend_runtime::Balances::transfer_allow_death( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE), + derivative.into(), + ONE_DOT * 100, + )); + }); +} + +#[test] +fn transact_through_derivative_to_relay() { + setup_derivative(); + + moonriver_execute_with(|| { + assert_ok!( + moonriver_runtime::XcmTransactor::transact_through_derivative( + moonriver_runtime::RuntimeOrigin::signed(moonriver_runtime::AccountId::from(ALITH),), + moonriver_runtime::xcm_config::Transactors::Relay, + 0u16, // derivative index + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent() + ),)), + fee_amount: Some(ONE_DOT * 10), + }, + // Inner call (unwrapped — the pallet wraps it in as_derivative). + relay_remark_call(), + TransactWeights { + transact_required_weight_at_most: 2_000_000_000u64.into(), + overall_weight: Some(Limited(4_000_000_000u64.into())), + }, + false, + ) + ); + }); + + assert_relay_remark_executed(); +} + +#[test] +fn transact_through_derivative_custom_fee_weight() { + setup_derivative(); + + moonriver_execute_with(|| { + assert_ok!( + moonriver_runtime::XcmTransactor::transact_through_derivative( + moonriver_runtime::RuntimeOrigin::signed(moonriver_runtime::AccountId::from(ALITH),), + moonriver_runtime::xcm_config::Transactors::Relay, + 0u16, + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent() + ),)), + fee_amount: Some(ONE_DOT * 5), + }, + relay_remark_call(), + TransactWeights { + transact_required_weight_at_most: 3_000_000_000u64.into(), + overall_weight: Some(Limited(6_000_000_000u64.into())), + }, + false, + ) + ); + }); + + assert_relay_remark_executed(); +} + +#[test] +fn transact_through_derivative_custom_fee_weight_refund() { + setup_derivative(); + + let sovereign_before = WestendRelay::::execute_with(|| { + let sovereign = WestendRelay::::sovereign_account_id_of( + Location::new(0, [Parachain(MOONRIVER_PARA_ID)]), + ); + >::balance(&sovereign) + }); + + moonriver_execute_with(|| { + assert_ok!( + moonriver_runtime::XcmTransactor::transact_through_derivative( + moonriver_runtime::RuntimeOrigin::signed(moonriver_runtime::AccountId::from(ALITH),), + moonriver_runtime::xcm_config::Transactors::Relay, + 0u16, + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent() + ),)), + fee_amount: Some(ONE_DOT * 20), // overpay + }, + relay_remark_call(), + TransactWeights { + transact_required_weight_at_most: 2_000_000_000u64.into(), + overall_weight: Some(Limited(4_000_000_000u64.into())), + }, + true, // refund + ) + ); + }); + + assert_relay_remark_executed(); + + // With refund, surplus should be deposited back to the sovereign (SelfLocation). + let sovereign_after = WestendRelay::::execute_with(|| { + let sovereign = WestendRelay::::sovereign_account_id_of( + Location::new(0, [Parachain(MOONRIVER_PARA_ID)]), + ); + >::balance(&sovereign) + }); + let fee_spent = sovereign_before.saturating_sub(sovereign_after); + assert!( + fee_spent < ONE_DOT * 20, + "With refund, sovereign should spend less than the full fee: spent={fee_spent}" + ); +} + +// =========================================================================== +// Transact through signed: para → para +// =========================================================================== + +/// Setup for para-to-para transact tests via signed origin. +/// Opens HRMP channels between Moonriver and Sibling, registers DOT on both, +/// and funds the derived account on the sibling. +fn setup_para_to_para_signed() -> moonriver_runtime::AccountId { + init_network(); + + // Register DOT + relay indices on Moonriver. + moonriver_execute_with(|| { + register_dot_asset(DOT_ASSET_ID); + set_westend_relay_indices(); + }); + + // Open HRMP channels. + WestendRelay::::execute_with(|| { + open_hrmp_channels(MOONRIVER_PARA_ID, SIBLING_PARA_ID); + }); + + // Register DOT on sibling so it can accept DOT as XCM fee. + sibling_execute_with(|| { + register_dot_asset(DOT_ASSET_ID); + }); + + // Compute the derived account on the sibling for ALITH's signed origin from Moonriver. + // After DescendOrigin(AccountKey20(ALITH)), the sibling sees origin + // Location::new(1, [Parachain(2004), AccountKey20(ALITH)]). + let derived_on_sibling: moonriver_runtime::AccountId = sibling_execute_with(|| { + >::convert_location(&Location::new( + 1, + [ + Parachain(MOONRIVER_PARA_ID), + AccountKey20 { + network: None, + key: ALITH, + }, + ], + )) + .expect("Should derive sibling account for Moonriver ALITH") + }); + + // Fund the derived account on sibling with DOT (relay → sibling DMP). + WestendRelay::::execute_with(|| { + assert_ok!( + westend_runtime::XcmPallet::transfer_assets_using_type_and_then( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(SIBLING_PARA_ID)], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(ONE_DOT * 100), + }]))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedAssetId::from(AssetId(Location::here()))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: derived_on_sibling.into(), + }], + ), + }]))), + WeightLimit::Unlimited, + ) + ); + }); + + // Verify the derived account received DOT. + sibling_execute_with(|| { + let balance = + moonriver_runtime::EvmForeignAssets::balance(DOT_ASSET_ID, derived_on_sibling).unwrap(); + assert!( + balance > sp_core::U256::zero(), + "Derived account on sibling should have DOT" + ); + }); + + derived_on_sibling +} + +/// Encode a `system::remark_with_event` call for the sibling (Moonriver runtime). +fn sibling_remark_call() -> Vec { + moonriver_runtime::RuntimeCall::System( + frame_system::Call::::remark_with_event { + remark: b"hello from Moonriver to sibling".to_vec(), + }, + ) + .encode() +} + +/// Assert that the sibling processed the HRMP transact and emitted a Remarked event. +fn assert_sibling_remark_executed() { + sibling_execute_with(|| { + let events = moonriver_runtime::System::events(); + + let has_remark = events.iter().any(|e| { + matches!( + &e.event, + moonriver_runtime::RuntimeEvent::System(frame_system::Event::Remarked { .. }) + ) + }); + assert!( + has_remark, + "Sibling should have emitted Remarked event from transact" + ); + }); +} + +#[test] +fn transact_through_signed_para_to_para() { + setup_para_to_para_signed(); + + moonriver_execute_with(|| { + assert_ok!(moonriver_runtime::XcmTransactor::transact_through_signed( + moonriver_runtime::RuntimeOrigin::signed(moonriver_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(SIBLING_PARA_ID)], + ))), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent(), + ))), + fee_amount: Some(ONE_DOT * 10), + }, + sibling_remark_call(), + TransactWeights { + transact_required_weight_at_most: 1_000_000_000u64.into(), + overall_weight: Some(Limited(4_000_000_000u64.into())), + }, + false, + )); + }); + + assert_sibling_remark_executed(); +} + +#[test] +fn transact_through_signed_para_to_para_refund() { + let derived_on_sibling = setup_para_to_para_signed(); + + let dot_before = sibling_execute_with(|| { + moonriver_runtime::EvmForeignAssets::balance(DOT_ASSET_ID, derived_on_sibling).unwrap() + }); + + moonriver_execute_with(|| { + assert_ok!(moonriver_runtime::XcmTransactor::transact_through_signed( + moonriver_runtime::RuntimeOrigin::signed(moonriver_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(SIBLING_PARA_ID)], + ))), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent(), + ))), + fee_amount: Some(ONE_DOT * 20), // overpay + }, + sibling_remark_call(), + TransactWeights { + transact_required_weight_at_most: 1_000_000_000u64.into(), + // Refund appendix (RefundSurplus + DepositAsset) needs extra weight. + overall_weight: Some(Limited(8_000_000_000u64.into())), + }, + true, // refund = true + )); + }); + + assert_sibling_remark_executed(); + + // With refund, the derived account should get surplus back. + let dot_after = sibling_execute_with(|| { + moonriver_runtime::EvmForeignAssets::balance(DOT_ASSET_ID, derived_on_sibling).unwrap() + }); + let spent = dot_before.saturating_sub(dot_after); + assert!( + spent < sp_core::U256::from(ONE_DOT * 20), + "With refund, derived account should spend less than the full 20 DOT fee: spent={spent}" + ); +} + +// =========================================================================== +// Transact through signed: para → para (EthereumXcm) +// =========================================================================== + +/// Common setup for Ethereum XCM transact tests. +/// Returns the derived account on the sibling. +fn setup_para_to_para_ethereum() -> moonriver_runtime::AccountId { + let derived_on_sibling = setup_para_to_para_signed(); + + // The derived account needs GLMR on the sibling for EVM value transfers. + sibling_execute_with(|| { + >::mint_into( + &derived_on_sibling, + moonriver_runtime::currency::MOVR * 10, + ) + .expect("Should mint GLMR for derived account on sibling"); + }); + + derived_on_sibling +} + +/// Encode an `EthereumXcm::transact` call that does an EVM value transfer. +fn ethereum_xcm_transfer_call(recipient: sp_core::H160, value: u128) -> Vec { + use sp_runtime::BoundedVec; + + let eth_tx = + xcm_primitives::EthereumXcmTransaction::V1(xcm_primitives::EthereumXcmTransactionV1 { + gas_limit: U256::from(21000), + fee_payment: xcm_primitives::EthereumXcmFee::Auto, + action: pallet_ethereum::TransactionAction::Call(recipient), + value: U256::from(value), + input: BoundedVec::< + u8, + sp_core::ConstU32<{ xcm_primitives::MAX_ETHEREUM_XCM_INPUT_SIZE }>, + >::try_from(vec![]) + .unwrap(), + access_list: None, + }); + + moonriver_runtime::RuntimeCall::EthereumXcm(pallet_ethereum_xcm::Call::< + moonriver_runtime::Runtime, + >::transact { + xcm_transaction: eth_tx, + }) + .encode() +} + +/// EVM transfer to ALITH on sibling via EthereumXcm::transact. +#[test] +fn transact_through_signed_para_to_para_ethereum() { + let _derived = setup_para_to_para_ethereum(); + + let transfer_value = 100u128; + let alith_h160 = sp_core::H160::from(ALITH); + + let alith_balance_before = sibling_execute_with(|| { + >::balance(&moonriver_runtime::AccountId::from( + ALITH, + )) + }); + + moonriver_execute_with(|| { + assert_ok!(moonriver_runtime::XcmTransactor::transact_through_signed( + moonriver_runtime::RuntimeOrigin::signed(moonriver_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(SIBLING_PARA_ID)], + ))), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent(), + ))), + fee_amount: Some(ONE_DOT * 10), + }, + ethereum_xcm_transfer_call(alith_h160, transfer_value), + TransactWeights { + transact_required_weight_at_most: 4_000_000_000u64.into(), + overall_weight: Some(Limited(8_000_000_000u64.into())), + }, + false, + )); + }); + + let alith_balance_after = sibling_execute_with(|| { + >::balance(&moonriver_runtime::AccountId::from( + ALITH, + )) + }); + assert_eq!( + alith_balance_after - alith_balance_before, + transfer_value, + "ALITH should receive {transfer_value} WEI on sibling via EthereumXcm transact" + ); +} + +/// EthereumXcm::transact_through_proxy fails without a proxy set up. +#[test] +fn transact_through_signed_para_to_para_ethereum_no_proxy_fails() { + let _derived = setup_para_to_para_ethereum(); + + let alith_h160 = sp_core::H160::from(ALITH); + // Use a distinct recipient so a self-transfer cannot mask proxy rejection. + let recipient: [u8; 20] = [42u8; 20]; + let recipient_h160 = sp_core::H160::from(recipient); + let transfer_value = 100u128; + + // Encode a transact_through_proxy call without any proxy being set. + let eth_tx = + xcm_primitives::EthereumXcmTransaction::V2(xcm_primitives::EthereumXcmTransactionV2 { + gas_limit: U256::from(21000), + action: pallet_ethereum::TransactionAction::Call(recipient_h160), + value: U256::from(transfer_value), + input: sp_runtime::BoundedVec::try_from(vec![]).unwrap(), + access_list: None, + }); + + let proxy_call = moonriver_runtime::RuntimeCall::EthereumXcm(pallet_ethereum_xcm::Call::< + moonriver_runtime::Runtime, + >::transact_through_proxy { + transact_as: alith_h160, + xcm_transaction: eth_tx, + }) + .encode(); + + let recipient_balance_before = sibling_execute_with(|| { + >::balance(&moonriver_runtime::AccountId::from( + recipient, + )) + }); + + moonriver_execute_with(|| { + assert_ok!(moonriver_runtime::XcmTransactor::transact_through_signed( + moonriver_runtime::RuntimeOrigin::signed(moonriver_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(SIBLING_PARA_ID)], + ))), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent(), + ))), + fee_amount: Some(ONE_DOT * 10), + }, + proxy_call, + TransactWeights { + transact_required_weight_at_most: 4_000_000_000u64.into(), + overall_weight: Some(Limited(8_000_000_000u64.into())), + }, + false, + )); + }); + + // The EVM transfer should NOT have happened (proxy not set). + let recipient_balance_after = sibling_execute_with(|| { + >::balance(&moonriver_runtime::AccountId::from( + recipient, + )) + }); + assert_eq!( + recipient_balance_after, recipient_balance_before, + "Recipient balance should be unchanged — transact_through_proxy should fail without proxy" + ); +} + +/// EthereumXcm::transact_through_proxy succeeds with a proxy set up. +#[test] +fn transact_through_signed_para_to_para_ethereum_proxy_succeeds() { + let derived = setup_para_to_para_ethereum(); + + let recipient: [u8; 20] = [42u8; 20]; + let transfer_value = 100u128; + + // Set up proxy: ALITH delegates to the derived account on the sibling. + sibling_execute_with(|| { + assert_ok!(moonriver_runtime::Proxy::add_proxy( + moonriver_runtime::RuntimeOrigin::signed(moonriver_runtime::AccountId::from(ALITH)), + derived, + moonriver_runtime::ProxyType::Any, + 0, + )); + }); + + let recipient_balance_before = sibling_execute_with(|| { + >::balance(&moonriver_runtime::AccountId::from( + recipient, + )) + }); + + // Encode a transact_through_proxy call targeting ALITH as proxy principal, + // EVM transfer to `recipient`. + let eth_tx = + xcm_primitives::EthereumXcmTransaction::V2(xcm_primitives::EthereumXcmTransactionV2 { + gas_limit: U256::from(21000), + action: pallet_ethereum::TransactionAction::Call(sp_core::H160::from(recipient)), + value: U256::from(transfer_value), + input: sp_runtime::BoundedVec::try_from(vec![]).unwrap(), + access_list: None, + }); + + let proxy_call = moonriver_runtime::RuntimeCall::EthereumXcm(pallet_ethereum_xcm::Call::< + moonriver_runtime::Runtime, + >::transact_through_proxy { + transact_as: sp_core::H160::from(ALITH), + xcm_transaction: eth_tx, + }) + .encode(); + + moonriver_execute_with(|| { + assert_ok!(moonriver_runtime::XcmTransactor::transact_through_signed( + moonriver_runtime::RuntimeOrigin::signed(moonriver_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(SIBLING_PARA_ID)], + ))), + CurrencyPayment { + currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( + Location::parent(), + ))), + fee_amount: Some(ONE_DOT * 10), + }, + proxy_call, + TransactWeights { + transact_required_weight_at_most: 4_000_000_000u64.into(), + overall_weight: Some(Limited(8_000_000_000u64.into())), + }, + false, + )); + }); + + let recipient_balance_after = sibling_execute_with(|| { + >::balance(&moonriver_runtime::AccountId::from( + recipient, + )) + }); + assert_eq!( + recipient_balance_after - recipient_balance_before, + transfer_value, + "Recipient should receive {transfer_value} WEI via EthereumXcm proxy transact" + ); +} diff --git a/runtime/moonriver/tests/xcm_emulator/transfers.rs b/runtime/moonriver/tests/xcm_emulator/transfers.rs new file mode 100644 index 00000000000..70bc2f0d987 --- /dev/null +++ b/runtime/moonriver/tests/xcm_emulator/transfers.rs @@ -0,0 +1,1275 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonriver. + +// Moonriver 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. + +// Moonriver 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 Moonriver. If not, see . + +//! Transfer tests using xcm-emulator with the **real** Moonriver runtime. +//! +//! Covers: relay→para, para→relay, para→para transfers, fee behaviour, +//! account sufficiency, and error cases. + +use crate::network::*; +use frame_support::{ + assert_ok, + traits::{fungible::Inspect, tokens::fungible::Mutate}, +}; +use sp_core::U256; +use xcm::latest::prelude::*; +use xcm_emulator::TestExt; + +// =========================================================================== +// Setup helper +// =========================================================================== + +/// Full network init: register DOT on Moonriver, configure weight trader. +fn setup_relay_to_moonriver() { + init_network(); + moonriver_execute_with(|| { + register_dot_asset(DOT_ASSET_ID); + }); +} + +/// Full network init with sibling: register DOT on both paras, open HRMP channels. +fn setup_with_sibling() { + init_network(); + + moonriver_execute_with(|| { + register_dot_asset(DOT_ASSET_ID); + }); + sibling_execute_with(|| { + register_dot_asset(DOT_ASSET_ID); + }); + + // Open bi-directional HRMP channels between Moonriver (2004) and Sibling (2005). + WestendRelay::::execute_with(|| { + open_hrmp_channels(MOONRIVER_PARA_ID, SIBLING_PARA_ID); + }); +} + +// =========================================================================== +// Transfer: Relay → Moonriver (DMP) +// =========================================================================== + +#[test] +fn transfer_dot_from_relay_to_moonriver() { + setup_relay_to_moonriver(); + + let sender = RELAY_ALICE; + let beneficiary_key = ALITH; + + WestendRelay::::execute_with(|| { + let balance_before = >::balance(&sender); + + assert_ok!( + westend_runtime::XcmPallet::transfer_assets_using_type_and_then( + westend_runtime::RuntimeOrigin::signed(sender.clone()), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(MOONRIVER_PARA_ID)] + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(ONE_DOT * 10), + }]))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedAssetId::from(AssetId(Location::here()))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: beneficiary_key, + }], + ), + }]))), + WeightLimit::Unlimited, + ) + ); + + let balance_after = >::balance(&sender); + assert!( + balance_after < balance_before, + "Sender balance should decrease" + ); + }); + + moonriver_execute_with(|| { + let beneficiary = moonriver_runtime::AccountId::from(beneficiary_key); + let balance = moonriver_runtime::EvmForeignAssets::balance(DOT_ASSET_ID, beneficiary) + .expect("balance query should succeed"); + assert!( + balance > U256::zero(), + "Beneficiary should have DOT on Moonriver, got {balance}" + ); + }); +} + +// =========================================================================== +// Transfer: Moonriver → Relay (UMP) +// =========================================================================== + +#[test] +fn transfer_dot_from_moonriver_to_relay() { + setup_relay_to_moonriver(); + + // First: send DOT from relay to Moonriver so ALITH has some DOT. + WestendRelay::::execute_with(|| { + assert_ok!( + westend_runtime::XcmPallet::transfer_assets_using_type_and_then( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(MOONRIVER_PARA_ID)] + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(ONE_DOT * 100), + }]))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedAssetId::from(AssetId(Location::here()))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: ALITH + }], + ), + }]))), + WeightLimit::Unlimited, + ) + ); + }); + + let alith_dot_before = moonriver_execute_with(|| { + moonriver_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonriver_runtime::AccountId::from(ALITH), + ) + .unwrap() + }); + assert!(alith_dot_before > U256::zero(), "ALITH should have DOT"); + + // Record relay-side balance of a relay account before the return transfer. + let relay_bob = sp_runtime::AccountId32::new([2u8; 32]); + let relay_bob_before = WestendRelay::::execute_with(|| { + >::balance(&relay_bob) + }); + + // Now send DOT back from Moonriver to relay via PolkadotXcm. + // DOT's reserve is the relay, so we use DestinationReserve transfer type. + moonriver_execute_with(|| { + let dot_location = Location::parent(); + let dest = Location::parent(); + let beneficiary = Location::new( + 0, + [AccountId32 { + network: None, + id: relay_bob.clone().into(), + }], + ); + let amount = ONE_DOT * 5; + + assert_ok!( + moonriver_runtime::PolkadotXcm::transfer_assets_using_type_and_then( + moonriver_runtime::RuntimeOrigin::signed(moonriver_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(dest)), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(dot_location.clone()), + fun: Fungible(amount), + }]))), + Box::new(xcm_executor::traits::TransferType::DestinationReserve), + Box::new(xcm::VersionedAssetId::from(AssetId(dot_location))), + Box::new(xcm_executor::traits::TransferType::DestinationReserve), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary, + }]))), + WeightLimit::Unlimited, + ) + ); + }); + + // Verify relay account received DOT (minus fees). + let relay_bob_after = WestendRelay::::execute_with(|| { + >::balance(&relay_bob) + }); + assert!( + relay_bob_after > relay_bob_before, + "Relay Bob should have more DOT: before={relay_bob_before}, after={relay_bob_after}" + ); +} + +// =========================================================================== +// Fee behaviour: insufficient fees +// =========================================================================== + +#[test] +fn error_when_not_paying_enough_fees() { + setup_relay_to_moonriver(); + + // Send a tiny amount (1 unit) from relay — should fail to pay Moonriver execution fees. + WestendRelay::::execute_with(|| { + assert_ok!( + westend_runtime::XcmPallet::transfer_assets_using_type_and_then( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(MOONRIVER_PARA_ID)] + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(1), // way too little for fees + }]))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedAssetId::from(AssetId(Location::here()))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: ALITH + }], + ), + }]))), + WeightLimit::Unlimited, + ) + ); + }); + + // ALITH should NOT have received the token (execution failed). + moonriver_execute_with(|| { + let balance = moonriver_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonriver_runtime::AccountId::from(ALITH), + ) + .unwrap(); + assert_eq!( + balance, + U256::zero(), + "Should not receive DOT when fees are insufficient" + ); + }); +} + +// =========================================================================== +// Fee behaviour: fees go to treasury +// =========================================================================== + +#[test] +fn fees_collected_by_treasury() { + setup_relay_to_moonriver(); + + let treasury_dot_before = moonriver_execute_with(|| { + let treasury = moonriver_runtime::Treasury::account_id(); + moonriver_runtime::EvmForeignAssets::balance(DOT_ASSET_ID, treasury).unwrap_or(U256::zero()) + }); + + // Send DOT from relay to Moonriver (fees will be charged). + WestendRelay::::execute_with(|| { + assert_ok!( + westend_runtime::XcmPallet::transfer_assets_using_type_and_then( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(MOONRIVER_PARA_ID)] + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(ONE_DOT * 10), + }]))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedAssetId::from(AssetId(Location::here()))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: BALTATHAR + }], + ), + }]))), + WeightLimit::Unlimited, + ) + ); + }); + + moonriver_execute_with(|| { + let treasury = moonriver_runtime::Treasury::account_id(); + let treasury_dot_after = + moonriver_runtime::EvmForeignAssets::balance(DOT_ASSET_ID, treasury) + .unwrap_or(U256::zero()); + assert!( + treasury_dot_after > treasury_dot_before, + "Treasury should collect fees: before={treasury_dot_before}, after={treasury_dot_after}" + ); + + // And beneficiary should have gotten the rest (not the full amount). + let beneficiary_balance = moonriver_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonriver_runtime::AccountId::from(BALTATHAR), + ) + .unwrap(); + assert!( + beneficiary_balance > U256::zero(), + "Beneficiary received DOT" + ); + assert!( + beneficiary_balance < U256::from(ONE_DOT * 10), + "Beneficiary received less than sent (fees deducted)" + ); + }); +} + +// =========================================================================== +// Account sufficiency: non-existent account receives foreign asset +// =========================================================================== + +#[test] +fn receive_asset_for_non_existent_account() { + setup_relay_to_moonriver(); + + let fresh_account: [u8; 20] = [42u8; 20]; + + WestendRelay::::execute_with(|| { + assert_ok!( + westend_runtime::XcmPallet::transfer_assets_using_type_and_then( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(MOONRIVER_PARA_ID)] + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(ONE_DOT * 10), + }]))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedAssetId::from(AssetId(Location::here()))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: fresh_account, + }], + ), + }]))), + WeightLimit::Unlimited, + ) + ); + }); + + moonriver_execute_with(|| { + let balance = moonriver_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonriver_runtime::AccountId::from(fresh_account), + ) + .unwrap(); + assert!( + balance > U256::zero(), + "Fresh (non-existent) account should receive DOT via XCM" + ); + }); +} + +// =========================================================================== +// Transfer: Para → Para via relay (XCMP/HRMP) +// =========================================================================== + +#[test] +fn transfer_dot_from_moonriver_to_sibling() { + setup_with_sibling(); + + // First fund Moonriver ALITH with DOT from relay. + WestendRelay::::execute_with(|| { + assert_ok!( + westend_runtime::XcmPallet::transfer_assets_using_type_and_then( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(MOONRIVER_PARA_ID)] + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(ONE_DOT * 100), + }]))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedAssetId::from(AssetId(Location::here()))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: ALITH + }], + ), + }]))), + WeightLimit::Unlimited, + ) + ); + }); + + // Verify ALITH got DOT on Moonriver. + let alith_dot = moonriver_execute_with(|| { + moonriver_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonriver_runtime::AccountId::from(ALITH), + ) + .unwrap() + }); + assert!(alith_dot > U256::zero(), "ALITH should have DOT"); + + // Now send DOT from Moonriver to Sibling via reserve transfer through relay. + // DOT's reserve is the relay (parent), so we use RemoteReserve. + // The custom_xcm_on_dest must include BuyExecution since the sibling's + // barrier requires paid execution. + moonriver_execute_with(|| { + let dest = Location::new(1, [Parachain(SIBLING_PARA_ID)]); + let beneficiary = Location::new( + 0, + [AccountKey20 { + network: None, + key: BALTATHAR, + }], + ); + let dot_location = Location::parent(); + // Send a large amount so enough survives relay fees for the sibling. + let amount = ONE_DOT * 50; + + assert_ok!( + moonriver_runtime::PolkadotXcm::transfer_assets_using_type_and_then( + moonriver_runtime::RuntimeOrigin::signed(moonriver_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(dest)), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(dot_location.clone()), + fun: Fungible(amount), + }]))), + Box::new(xcm_executor::traits::TransferType::RemoteReserve( + xcm::VersionedLocation::from(Location::parent()) + )), + Box::new(xcm::VersionedAssetId::from(AssetId(dot_location.clone()))), + Box::new(xcm_executor::traits::TransferType::RemoteReserve( + xcm::VersionedLocation::from(Location::parent()) + )), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![ + BuyExecution { + // Use a small fee amount that will definitely be in holding + // after the relay takes its share. + fees: Asset { + id: AssetId(dot_location), + fun: Fungible(ONE_DOT / 10), + }, + weight_limit: WeightLimit::Unlimited, + }, + DepositAsset { + assets: Wild(All), + beneficiary, + }, + ]))), + WeightLimit::Unlimited, + ) + ); + }); + + // Trigger message routing on the relay so the DMP is delivered to sibling. + WestendRelay::::execute_with(|| {}); + + // Verify BALTATHAR received DOT on the sibling. + sibling_execute_with(|| { + let balance = moonriver_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonriver_runtime::AccountId::from(BALTATHAR), + ) + .unwrap(); + assert!( + balance > U256::zero(), + "BALTATHAR should have DOT on sibling, got {balance}" + ); + }); +} + +// =========================================================================== +// EVM account with native balance receives foreign assets +// =========================================================================== + +#[test] +fn evm_account_receives_foreign_asset() { + setup_relay_to_moonriver(); + + // ALITH has GLMR from genesis. Send DOT and verify both balances coexist. + WestendRelay::::execute_with(|| { + assert_ok!( + westend_runtime::XcmPallet::transfer_assets_using_type_and_then( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(MOONRIVER_PARA_ID)], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(ONE_DOT * 10), + }]))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedAssetId::from(AssetId(Location::here()))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: ALITH + }], + ), + }]))), + WeightLimit::Unlimited, + ) + ); + }); + + moonriver_execute_with(|| { + // ALITH should have both native GLMR and foreign DOT. + let glmr = >::balance( + &moonriver_runtime::AccountId::from(ALITH), + ); + assert!(glmr > 0, "ALITH should still have GLMR"); + + let dot = moonriver_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonriver_runtime::AccountId::from(ALITH), + ) + .unwrap(); + assert!(dot > U256::zero(), "ALITH should also have DOT"); + }); +} + +// =========================================================================== +// Foreign assets survive native balance drainage +// =========================================================================== + +#[test] +fn foreign_assets_survive_native_balance_drain() { + setup_relay_to_moonriver(); + + let test_account: [u8; 20] = [77u8; 20]; + + // Give the test account some GLMR. + moonriver_execute_with(|| { + >::mint_into( + &moonriver_runtime::AccountId::from(test_account), + moonriver_runtime::currency::MOVR, + ) + .expect("Should mint GLMR"); + }); + + // Send DOT to the test account. + WestendRelay::::execute_with(|| { + assert_ok!( + westend_runtime::XcmPallet::transfer_assets_using_type_and_then( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(MOONRIVER_PARA_ID)], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::here()), + fun: Fungible(ONE_DOT * 10), + }]))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedAssetId::from(AssetId(Location::here()))), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: test_account + }], + ), + }]))), + WeightLimit::Unlimited, + ) + ); + }); + + // Drain all GLMR, then verify foreign asset is still accessible. + moonriver_execute_with(|| { + let balance = >::balance( + &moonriver_runtime::AccountId::from(test_account), + ); + let _ = >::burn_from( + &moonriver_runtime::AccountId::from(test_account), + balance, + frame_support::traits::tokens::Preservation::Expendable, + frame_support::traits::tokens::Precision::BestEffort, + frame_support::traits::tokens::Fortitude::Force, + ); + + let remaining = >::balance( + &moonriver_runtime::AccountId::from(test_account), + ); + assert_eq!(remaining, 0, "Native balance should be zero after drain"); + + // Foreign asset balance should still be accessible. + let dot = moonriver_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonriver_runtime::AccountId::from(test_account), + ) + .unwrap(); + assert!( + dot > U256::zero(), + "Foreign asset should survive native balance drain" + ); + }); +} + +// =========================================================================== +// Native asset (GLMR) para → para transfers +// =========================================================================== + +/// Register Moonriver's native GLMR as a foreign asset on the sibling and +/// configure the XCM weight trader price. +fn register_movr_on_sibling() { + sibling_execute_with(|| { + // From the sibling's perspective, Moonriver's native token lives at: + // ../Parachain(2004)/PalletInstance(10) (pallet_balances = index 10) + let glmr_location = + xcm::latest::Location::new(1, [Parachain(MOONRIVER_PARA_ID), PalletInstance(10u8)]); + + frame_support::assert_ok!(moonriver_runtime::EvmForeignAssets::create_foreign_asset( + moonriver_runtime::RuntimeOrigin::root(), + MOVR_ASSET_ID, + glmr_location.clone(), + 18, // GLMR has 18 decimals + b"MOVR".to_vec().try_into().unwrap(), + b"Moonriver".to_vec().try_into().unwrap(), + )); + + frame_support::assert_ok!(moonriver_runtime::XcmWeightTrader::add_asset( + moonriver_runtime::RuntimeOrigin::root(), + glmr_location, + 10_000_000_000_000_000_000_000_000_000u128, // 10^28 (generous relative price) + )); + }); +} + +/// Setup for GLMR para→para transfers: open HRMP, register DOT on Moonriver, +/// register GLMR on sibling. +fn setup_movr_para_to_para() { + setup_with_sibling(); + register_movr_on_sibling(); +} + +/// Transfer GLMR from Moonriver to Sibling (reserve-backed). +#[test] +fn transfer_movr_from_moonriver_to_sibling() { + setup_movr_para_to_para(); + + let alith_before = moonriver_execute_with(|| { + >::balance(&moonriver_runtime::AccountId::from( + ALITH, + )) + }); + + let amount = moonriver_runtime::currency::MOVR; // 1 GLMR + + moonriver_execute_with(|| { + assert_ok!(moonriver_runtime::PolkadotXcm::transfer_assets( + moonriver_runtime::RuntimeOrigin::signed(moonriver_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(SIBLING_PARA_ID)], + ))), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [AccountKey20 { + network: None, + key: BALTATHAR + }], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::new(0, [PalletInstance(10)])), + fun: Fungible(amount), + }]))), + 0, + WeightLimit::Unlimited, + )); + }); + + // ALITH should have less GLMR after the transfer. + let alith_after = moonriver_execute_with(|| { + >::balance(&moonriver_runtime::AccountId::from( + ALITH, + )) + }); + assert!( + alith_after < alith_before, + "ALITH should have less GLMR after transfer" + ); + assert!( + alith_before - alith_after >= amount, + "ALITH should have spent at least {amount}" + ); + + // BALTATHAR should have GLMR on sibling (as foreign asset). + sibling_execute_with(|| { + let balance = moonriver_runtime::EvmForeignAssets::balance( + MOVR_ASSET_ID, + moonriver_runtime::AccountId::from(BALTATHAR), + ) + .unwrap(); + assert!( + balance > U256::zero(), + "BALTATHAR should have GLMR on sibling" + ); + }); +} + +/// Roundtrip: GLMR from Moonriver → Sibling → back to Moonriver. +#[test] +fn transfer_movr_roundtrip_moonriver_sibling() { + setup_movr_para_to_para(); + + let alith_initial = moonriver_execute_with(|| { + >::balance(&moonriver_runtime::AccountId::from( + ALITH, + )) + }); + + let amount = moonriver_runtime::currency::MOVR; // 1 GLMR + + // Step 1: Send GLMR from Moonriver to Sibling (BALTATHAR). + moonriver_execute_with(|| { + assert_ok!(moonriver_runtime::PolkadotXcm::transfer_assets( + moonriver_runtime::RuntimeOrigin::signed(moonriver_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(SIBLING_PARA_ID)], + ))), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [AccountKey20 { + network: None, + key: BALTATHAR + }], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::new(0, [PalletInstance(10)])), + fun: Fungible(amount), + }]))), + 0, + WeightLimit::Unlimited, + )); + }); + + // Verify BALTATHAR got GLMR on sibling. + let glmr_on_sibling = sibling_execute_with(|| { + moonriver_runtime::EvmForeignAssets::balance( + MOVR_ASSET_ID, + moonriver_runtime::AccountId::from(BALTATHAR), + ) + .unwrap() + }); + assert!( + glmr_on_sibling > U256::zero(), + "BALTATHAR should have GLMR on sibling: {glmr_on_sibling}" + ); + + // Step 2: Send GLMR back from Sibling to Moonriver (ALITH). + // From the sibling's perspective, GLMR is at ../Parachain(2004)/PalletInstance(10). + sibling_execute_with(|| { + let glmr_location = Location::new(1, [Parachain(MOONRIVER_PARA_ID), PalletInstance(10)]); + + assert_ok!(moonriver_runtime::PolkadotXcm::transfer_assets( + moonriver_runtime::RuntimeOrigin::signed(moonriver_runtime::AccountId::from(BALTATHAR),), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(MOONRIVER_PARA_ID)], + ))), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [AccountKey20 { + network: None, + key: ALITH + }], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(glmr_location), + fun: Fungible(glmr_on_sibling.try_into().unwrap()), + }]))), + 0, + WeightLimit::Unlimited, + )); + }); + + // ALITH should have recovered most of the GLMR (minus fees on both hops). + let alith_final = moonriver_execute_with(|| { + >::balance(&moonriver_runtime::AccountId::from( + ALITH, + )) + }); + // After roundtrip, ALITH loses some to fees but should still have most. + let total_lost = alith_initial.saturating_sub(alith_final); + assert!( + total_lost < amount, + "Roundtrip should only lose fees, not the full amount: lost={total_lost}, sent={amount}" + ); +} + +/// GLMR transfer with trader: fees are deducted from GLMR on the sibling. +#[test] +fn transfer_movr_to_sibling_with_trader_fees() { + setup_movr_para_to_para(); + + let amount = moonriver_runtime::currency::MOVR * 100; // 100 GLMR + + moonriver_execute_with(|| { + assert_ok!(moonriver_runtime::PolkadotXcm::transfer_assets( + moonriver_runtime::RuntimeOrigin::signed(moonriver_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(SIBLING_PARA_ID)], + ))), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [AccountKey20 { + network: None, + key: BALTATHAR + }], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::new(0, [PalletInstance(10)])), + fun: Fungible(amount), + }]))), + 0, + WeightLimit::Unlimited, + )); + }); + + sibling_execute_with(|| { + let received = moonriver_runtime::EvmForeignAssets::balance( + MOVR_ASSET_ID, + moonriver_runtime::AccountId::from(BALTATHAR), + ) + .unwrap(); + + // BALTATHAR should receive less than the full amount (fees deducted). + assert!( + received > U256::zero() && received < U256::from(amount), + "Should receive less than full amount due to fees: received={received}, sent={amount}" + ); + + // Treasury should have received some GLMR as fees. + let treasury = moonriver_runtime::Treasury::account_id(); + let treasury_fee = + moonriver_runtime::EvmForeignAssets::balance(MOVR_ASSET_ID, treasury).unwrap(); + assert!( + treasury_fee > U256::zero(), + "Treasury should have collected GLMR fees" + ); + }); +} + +// =========================================================================== +// DOT transfers via RemoteReserve (relay as reserve) +// =========================================================================== + +/// Fund ALITH with DOT via relay DMP. +fn fund_moonriver_alith_with_dot(amount: u128) { + WestendRelay::::execute_with(|| { + let beneficiary = Location::new( + 0, + [AccountKey20 { + network: None, + key: ALITH, + }], + ); + let assets: xcm::VersionedAssets = (Location::here(), amount).into(); + let fees_id: xcm::VersionedAssetId = AssetId(Location::here()).into(); + let xcm_on_dest = Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary, + }]); + + assert_ok!( + westend_runtime::XcmPallet::transfer_assets_using_type_and_then( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE.clone()), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(MOONRIVER_PARA_ID)] + ))), + Box::new(assets), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(fees_id), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedXcm::V5(xcm_on_dest)), + WeightLimit::Unlimited, + ) + ); + }); +} + +/// Send DOT from Moonriver to a sibling using `RemoteReserve` through the +/// relay. DOT's reserve is the relay (parent), so a direct +/// `DestinationReserve` is invalid — the relay must mediate. +#[test] +fn transfer_dot_to_sibling_via_remote_reserve() { + setup_with_sibling(); + + let send_amount = ONE_DOT * 100; + fund_moonriver_alith_with_dot(send_amount); + + let alith_dot_before = moonriver_execute_with(|| { + moonriver_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonriver_runtime::AccountId::from(ALITH), + ) + .unwrap_or_default() + }); + assert!( + alith_dot_before > U256::zero(), + "ALITH should have DOT before transfer" + ); + + let transfer = ONE_DOT * 50; + + moonriver_execute_with(|| { + let dot_location = Location::parent(); + + assert_ok!( + moonriver_runtime::PolkadotXcm::transfer_assets_using_type_and_then( + moonriver_runtime::RuntimeOrigin::signed(moonriver_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(SIBLING_PARA_ID)], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(dot_location.clone()), + fun: Fungible(transfer), + }]))), + Box::new(xcm_executor::traits::TransferType::RemoteReserve( + xcm::VersionedLocation::from(Location::parent()), + )), + Box::new(xcm::VersionedAssetId::from(AssetId(dot_location.clone()))), + Box::new(xcm_executor::traits::TransferType::RemoteReserve( + xcm::VersionedLocation::from(Location::parent()), + )), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![ + BuyExecution { + fees: Asset { + id: AssetId(dot_location), + fun: Fungible(ONE_DOT / 10), + }, + weight_limit: WeightLimit::Unlimited, + }, + DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: BALTATHAR, + }], + ), + }, + ]))), + WeightLimit::Unlimited, + ) + ); + }); + + WestendRelay::::execute_with(|| {}); + + let alith_dot_after = moonriver_execute_with(|| { + moonriver_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonriver_runtime::AccountId::from(ALITH), + ) + .unwrap_or_default() + }); + assert!( + alith_dot_after < alith_dot_before, + "ALITH DOT should decrease after transfer" + ); + + let baltathar_dot = sibling_execute_with(|| { + moonriver_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonriver_runtime::AccountId::from(BALTATHAR), + ) + .unwrap_or_default() + }); + assert!( + baltathar_dot > U256::zero(), + "BALTATHAR should have DOT on sibling (got {baltathar_dot})" + ); +} + +/// Roundtrip: DOT from Moonriver → Sibling → back to Moonriver, both legs +/// using RemoteReserve through the relay. +#[test] +fn transfer_dot_roundtrip_via_remote_reserve() { + setup_with_sibling(); + + let send_amount = ONE_DOT * 100; + fund_moonriver_alith_with_dot(send_amount); + + let outbound = ONE_DOT * 50; + let dot_location = Location::parent(); + + // ── Moonriver → Sibling ──────────────────────────────────────────────── + moonriver_execute_with(|| { + assert_ok!( + moonriver_runtime::PolkadotXcm::transfer_assets_using_type_and_then( + moonriver_runtime::RuntimeOrigin::signed(moonriver_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(SIBLING_PARA_ID)], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(dot_location.clone()), + fun: Fungible(outbound), + }]))), + Box::new(xcm_executor::traits::TransferType::RemoteReserve( + xcm::VersionedLocation::from(Location::parent()), + )), + Box::new(xcm::VersionedAssetId::from(AssetId(dot_location.clone()))), + Box::new(xcm_executor::traits::TransferType::RemoteReserve( + xcm::VersionedLocation::from(Location::parent()), + )), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![ + BuyExecution { + fees: Asset { + id: AssetId(dot_location.clone()), + fun: Fungible(ONE_DOT / 10), + }, + weight_limit: WeightLimit::Unlimited, + }, + DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: BALTATHAR, + }], + ), + }, + ]))), + WeightLimit::Unlimited, + ) + ); + }); + + WestendRelay::::execute_with(|| {}); + + let baltathar_dot = sibling_execute_with(|| { + moonriver_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonriver_runtime::AccountId::from(BALTATHAR), + ) + .unwrap_or_default() + }); + assert!(baltathar_dot > U256::zero(), "Sibling should have DOT"); + + // ── Sibling → Moonriver ──────────────────────────────────────────────── + let return_amount_raw: u128 = baltathar_dot.try_into().unwrap(); + let return_half = return_amount_raw / 2; + + sibling_execute_with(|| { + assert_ok!( + moonriver_runtime::PolkadotXcm::transfer_assets_using_type_and_then( + moonriver_runtime::RuntimeOrigin::signed(moonriver_runtime::AccountId::from( + BALTATHAR, + )), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(MOONRIVER_PARA_ID)], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(dot_location.clone()), + fun: Fungible(return_half), + }]))), + Box::new(xcm_executor::traits::TransferType::RemoteReserve( + xcm::VersionedLocation::from(Location::parent()), + )), + Box::new(xcm::VersionedAssetId::from(AssetId(dot_location.clone()))), + Box::new(xcm_executor::traits::TransferType::RemoteReserve( + xcm::VersionedLocation::from(Location::parent()), + )), + Box::new(xcm::VersionedXcm::from(Xcm::<()>(vec![ + BuyExecution { + fees: Asset { + id: AssetId(dot_location), + fun: Fungible(ONE_DOT / 10), + }, + weight_limit: WeightLimit::Unlimited, + }, + DepositAsset { + assets: Wild(All), + beneficiary: Location::new( + 0, + [AccountKey20 { + network: None, + key: ALITH, + }], + ), + }, + ]))), + WeightLimit::Unlimited, + ) + ); + }); + + WestendRelay::::execute_with(|| {}); + + let alith_dot_final = moonriver_execute_with(|| { + moonriver_runtime::EvmForeignAssets::balance( + DOT_ASSET_ID, + moonriver_runtime::AccountId::from(ALITH), + ) + .unwrap_or_default() + }); + assert!( + alith_dot_final > U256::from(send_amount - outbound), + "ALITH should have more DOT than after the outbound leg (got {alith_dot_final})" + ); +} + +/// Transfer GLMR to a sibling as a self-reserve asset (GLMR pays its own +/// fees). Exercises `transfer_assets` with a single asset where the fee +/// asset and the transfer asset are the same. +#[test] +fn transfer_movr_self_reserve_to_sibling() { + setup_with_sibling(); + register_movr_on_sibling(); + + let glmr_amount = moonriver_runtime::currency::MOVR; + + moonriver_execute_with(|| { + assert_ok!(moonriver_runtime::PolkadotXcm::transfer_assets( + moonriver_runtime::RuntimeOrigin::signed(moonriver_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(SIBLING_PARA_ID)], + ))), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [AccountKey20 { + network: None, + key: BALTATHAR, + }], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::new(0, [PalletInstance(10)])), + fun: Fungible(glmr_amount), + }]))), + 0, + WeightLimit::Unlimited, + )); + }); + + let bal_glmr = sibling_execute_with(|| { + moonriver_runtime::EvmForeignAssets::balance( + MOVR_ASSET_ID, + moonriver_runtime::AccountId::from(BALTATHAR), + ) + .unwrap_or_default() + }); + assert!( + bal_glmr > U256::zero(), + "BALTATHAR should have received GLMR on sibling (got {bal_glmr})" + ); +} + +/// Receive a sibling-native foreign asset on Moonriver. +/// A sibling sends its own native token (another Moonriver instance's GLMR) +/// to Moonriver, which receives it as an EVM foreign asset. +#[test] +fn receive_sibling_native_asset() { + setup_with_sibling(); + + // On Moonriver, register the sibling's GLMR (PalletInstance(10) on para 2005) + // as a foreign asset with id=3. + const SIBLING_MOVR_ASSET_ID: u128 = 3; + moonriver_execute_with(|| { + let sibling_glmr_location = + xcm::latest::Location::new(1, [Parachain(SIBLING_PARA_ID), PalletInstance(10u8)]); + + frame_support::assert_ok!(moonriver_runtime::EvmForeignAssets::create_foreign_asset( + moonriver_runtime::RuntimeOrigin::root(), + SIBLING_MOVR_ASSET_ID, + sibling_glmr_location.clone(), + 18, + b"sGLMR".to_vec().try_into().unwrap(), + b"Sibling Glimmer".to_vec().try_into().unwrap(), + )); + + frame_support::assert_ok!(moonriver_runtime::XcmWeightTrader::add_asset( + moonriver_runtime::RuntimeOrigin::root(), + sibling_glmr_location, + 10_000_000_000_000_000_000_000_000_000u128, + )); + }); + + let amount = moonriver_runtime::currency::MOVR; + + sibling_execute_with(|| { + assert_ok!(moonriver_runtime::PolkadotXcm::transfer_assets( + moonriver_runtime::RuntimeOrigin::signed(moonriver_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(MOONRIVER_PARA_ID)], + ))), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [AccountKey20 { + network: None, + key: BALTATHAR, + }], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::new(0, [PalletInstance(10)])), + fun: Fungible(amount), + }]))), + 0, + WeightLimit::Unlimited, + )); + }); + + let bal = moonriver_execute_with(|| { + moonriver_runtime::EvmForeignAssets::balance( + SIBLING_MOVR_ASSET_ID, + moonriver_runtime::AccountId::from(BALTATHAR), + ) + .unwrap_or_default() + }); + assert!( + bal > U256::zero(), + "BALTATHAR should have sibling GLMR on Moonriver (got {bal})" + ); +} diff --git a/runtime/moonriver/tests/xcm_emulator/versioning.rs b/runtime/moonriver/tests/xcm_emulator/versioning.rs new file mode 100644 index 00000000000..045417ebd75 --- /dev/null +++ b/runtime/moonriver/tests/xcm_emulator/versioning.rs @@ -0,0 +1,207 @@ +// Copyright 2019-2025 Moonbeam Foundation. +// This file is part of Moonbeam. + +// Moonbeam 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. + +// Moonbeam 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 Moonbeam. If not, see . + +//! XCM version discovery / negotiation tests. +//! +//! Verifies that `SafeXcmVersion` is configured from genesis and that +//! Moonbeam discovers the XCM version of remote chains (relay and siblings) +//! after the first cross-chain interaction. +//! +//! Full runtime-upgrade version negotiation (as in the legacy mock tests) +//! is not feasible with xcm-emulator because there is no mock version +//! switcher. These tests cover the subset that works with the real runtime. + +use crate::network::*; +use frame_support::assert_ok; +use xcm::latest::prelude::*; +use xcm_emulator::TestExt; + +// =========================================================================== +// Helpers +// =========================================================================== + +/// Register Moonbeam GLMR as foreign asset on the current chain context. +fn register_glmr_foreign_asset(source_para_id: u32) { + let glmr_location = + xcm::latest::Location::new(1, [Parachain(source_para_id), PalletInstance(10u8)]); + + frame_support::assert_ok!(moonriver_runtime::EvmForeignAssets::create_foreign_asset( + moonriver_runtime::RuntimeOrigin::root(), + MOVR_ASSET_ID, + glmr_location.clone(), + 18, + b"MOVR".to_vec().try_into().unwrap(), + b"Moonriver".to_vec().try_into().unwrap(), + )); + + frame_support::assert_ok!(moonriver_runtime::XcmWeightTrader::add_asset( + moonriver_runtime::RuntimeOrigin::root(), + glmr_location, + 10_000_000_000_000_000_000_000_000_000u128, + )); +} + +// =========================================================================== +// Tests +// =========================================================================== + +/// Verify that Moonbeam subscribes to the relay's XCM version on first +/// interaction. After a DMP transfer the relay should know Moonbeam's +/// supported XCM version. +#[test] +fn xcm_version_discovery_with_relay() { + init_network(); + + moonriver_execute_with(|| { + register_dot_asset(DOT_ASSET_ID); + }); + + // Send DOT from relay to Moonbeam to trigger version discovery. + WestendRelay::::execute_with(|| { + let beneficiary = Location::new( + 0, + [AccountKey20 { + network: None, + key: ALITH, + }], + ); + let assets: xcm::VersionedAssets = (Location::here(), ONE_DOT * 5).into(); + let fees_id: xcm::VersionedAssetId = AssetId(Location::here()).into(); + let xcm_on_dest = Xcm::<()>(vec![DepositAsset { + assets: Wild(All), + beneficiary, + }]); + + assert_ok!( + westend_runtime::XcmPallet::transfer_assets_using_type_and_then( + westend_runtime::RuntimeOrigin::signed(RELAY_ALICE.clone()), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [Parachain(MOONRIVER_PARA_ID)] + ))), + Box::new(assets), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(fees_id), + Box::new(xcm_executor::traits::TransferType::LocalReserve), + Box::new(xcm::VersionedXcm::V5(xcm_on_dest)), + WeightLimit::Unlimited, + ) + ); + }); + + // Verify the relay can version-wrap an XCM destined for Moonriver. + // query_delivery_fees calls validate_send → ChildParachainRouter::validate → + // wrap_version, which requires SupportedVersion or SafeXcmVersion to be set. + WestendRelay::::execute_with(|| { + use xcm_runtime_apis::fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV2; + let fees = westend_runtime::Runtime::query_delivery_fees( + xcm::VersionedLocation::from(Location::new(0, [Parachain(MOONRIVER_PARA_ID)])), + xcm::VersionedXcm::from(Xcm::<()>(vec![ClearOrigin])), + xcm::VersionedAssetId::from(AssetId(Location::here())), + ); + assert!( + fees.is_ok(), + "Relay should resolve XCM version for Moonriver destination" + ); + }); + + // Verify Moonriver can version-wrap an XCM destined for the relay. + // SafeXcmVersion is set from genesis, which wrap_version uses as fallback. + moonriver_execute_with(|| { + use xcm_runtime_apis::fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV2; + let fees = moonriver_runtime::Runtime::query_delivery_fees( + xcm::VersionedLocation::from(Location::parent()), + xcm::VersionedXcm::from(Xcm::<()>(vec![ClearOrigin])), + xcm::VersionedAssetId::from(AssetId(Location::here())), + ); + assert!( + fees.is_ok(), + "Moonriver should resolve XCM version for relay destination" + ); + }); +} + +/// Verify that Moonbeam and a sibling negotiate XCM versions via HRMP. +#[test] +fn xcm_version_discovery_with_sibling() { + init_network(); + + moonriver_execute_with(|| register_dot_asset(DOT_ASSET_ID)); + sibling_execute_with(|| register_dot_asset(DOT_ASSET_ID)); + + WestendRelay::::execute_with(|| { + open_hrmp_channels(MOONRIVER_PARA_ID, SIBLING_PARA_ID); + }); + + // Register GLMR on sibling so we can do a transfer. + sibling_execute_with(|| register_glmr_foreign_asset(MOONRIVER_PARA_ID)); + + let amount = moonriver_runtime::currency::MOVR; + + // Transfer triggers version negotiation between the two parachains. + moonriver_execute_with(|| { + assert_ok!(moonriver_runtime::PolkadotXcm::transfer_assets( + moonriver_runtime::RuntimeOrigin::signed(moonriver_runtime::AccountId::from(ALITH)), + Box::new(xcm::VersionedLocation::from(Location::new( + 1, + [Parachain(SIBLING_PARA_ID)], + ))), + Box::new(xcm::VersionedLocation::from(Location::new( + 0, + [AccountKey20 { + network: None, + key: BALTATHAR, + }], + ))), + Box::new(xcm::VersionedAssets::from(Assets::from(vec![Asset { + id: AssetId(Location::new(0, [PalletInstance(10)])), + fun: Fungible(amount), + }]))), + 0, + WeightLimit::Unlimited, + )); + }); + + // Verify sibling can version-wrap an XCM destined for Moonriver. + // query_delivery_fees calls validate_send → XcmpQueue::validate → + // wrap_version, which requires SupportedVersion or SafeXcmVersion. + sibling_execute_with(|| { + use xcm_runtime_apis::fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV2; + let fees = moonriver_runtime::Runtime::query_delivery_fees( + xcm::VersionedLocation::from(Location::new(1, [Parachain(MOONRIVER_PARA_ID)])), + xcm::VersionedXcm::from(Xcm::<()>(vec![ClearOrigin])), + xcm::VersionedAssetId::from(AssetId(Location::here())), + ); + assert!( + fees.is_ok(), + "Sibling should resolve XCM version for Moonriver destination" + ); + }); + + // Verify Moonriver can version-wrap an XCM destined for the sibling. + moonriver_execute_with(|| { + use xcm_runtime_apis::fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV2; + let fees = moonriver_runtime::Runtime::query_delivery_fees( + xcm::VersionedLocation::from(Location::new(1, [Parachain(SIBLING_PARA_ID)])), + xcm::VersionedXcm::from(Xcm::<()>(vec![ClearOrigin])), + xcm::VersionedAssetId::from(AssetId(Location::here())), + ); + assert!( + fees.is_ok(), + "Moonriver should resolve XCM version for sibling destination" + ); + }); +} diff --git a/runtime/moonriver/tests/xcm_mock/mod.rs b/runtime/moonriver/tests/xcm_mock/mod.rs deleted file mode 100644 index 0657dc989cc..00000000000 --- a/runtime/moonriver/tests/xcm_mock/mod.rs +++ /dev/null @@ -1,288 +0,0 @@ -// Copyright 2021 Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -pub mod parachain; -pub mod relay_chain; -pub mod statemine_like; - -use cumulus_primitives_core::ParaId; -use pallet_xcm_transactor::relay_indices::*; -use sp_runtime::traits::AccountIdConversion; -use sp_runtime::{AccountId32, BuildStorage}; -use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain, TestExt}; - -use polkadot_runtime_parachains::configuration::{ - GenesisConfig as ConfigurationGenesisConfig, HostConfiguration, -}; -use polkadot_runtime_parachains::paras::{ - GenesisConfig as ParasGenesisConfig, ParaGenesisArgs, ParaKind, -}; -use sp_core::{H160, U256}; -use std::{collections::BTreeMap, str::FromStr}; - -pub const PARAALICE: [u8; 20] = [1u8; 20]; -pub const PARABOB: [u8; 20] = [2u8; 20]; -pub const RELAYALICE: AccountId32 = AccountId32::new([0u8; 32]); -pub const RELAYBOB: AccountId32 = AccountId32::new([2u8; 32]); - -pub fn para_a_account() -> AccountId32 { - ParaId::from(1).into_account_truncating() -} - -pub fn para_b_account() -> AccountId32 { - ParaId::from(2).into_account_truncating() -} - -pub fn para_a_account_20() -> parachain::AccountId { - ParaId::from(1).into_account_truncating() -} - -pub fn evm_account() -> H160 { - H160::from_str("1000000000000000000000000000000000000001").unwrap() -} - -pub fn mock_para_genesis_info() -> ParaGenesisArgs { - ParaGenesisArgs { - genesis_head: vec![1u8].into(), - validation_code: vec![1u8].into(), - para_kind: ParaKind::Parachain, - } -} - -pub fn mock_relay_config() -> HostConfiguration { - HostConfiguration:: { - hrmp_channel_max_capacity: u32::MAX, - hrmp_channel_max_total_size: u32::MAX, - hrmp_max_parachain_inbound_channels: 10, - hrmp_max_parachain_outbound_channels: 10, - hrmp_channel_max_message_size: u32::MAX, - // Changed to avoid arithmetic errors within hrmp_close - max_downward_message_size: 100_000u32, - ..Default::default() - } -} - -pub fn mock_xcm_transactor_storage() -> RelayChainIndices { - RelayChainIndices { - staking: 0u8, - utility: 5u8, - hrmp: 6u8, - bond: 0u8, - bond_extra: 1u8, - unbond: 2u8, - withdraw_unbonded: 3u8, - validate: 4u8, - nominate: 5u8, - chill: 6u8, - set_payee: 7u8, - set_controller: 8u8, - rebond: 19u8, - as_derivative: 1u8, - init_open_channel: 0u8, - accept_open_channel: 1u8, - close_channel: 2u8, - cancel_open_request: 6u8, - } -} - -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_parachain! { - pub struct Statemine { - Runtime = statemine_like::Runtime, - XcmpMessageHandler = statemine_like::MsgQueue, - DmpMessageHandler = statemine_like::MsgQueue, - new_ext = statemine_ext(1000), - } -} - -decl_test_relay_chain! { - pub struct Relay { - Runtime = relay_chain::Runtime, - RuntimeCall = relay_chain::RuntimeCall, - RuntimeEvent = relay_chain::RuntimeEvent, - XcmConfig = relay_chain::XcmConfig, - MessageQueue = relay_chain::MessageQueue, - System = relay_chain::System, - new_ext = relay_ext(vec![1, 2, 3, 1000]), - } -} - -decl_test_network! { - pub struct MockNet { - relay_chain = Relay, - parachains = vec![ - (1, ParaA), - (2, ParaB), - (3, ParaC), - (1000, Statemine), - ], - } -} - -pub const INITIAL_BALANCE: u128 = 10_000_000_000_000_000; - -pub const INITIAL_EVM_BALANCE: u128 = 0; -pub const INITIAL_EVM_NONCE: u32 = 1; - -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![(PARAALICE.into(), INITIAL_BALANCE)], - dev_accounts: None, - } - .assimilate_storage(&mut t) - .unwrap(); - - pallet_xcm_transactor::GenesisConfig:: { - relay_indices: mock_xcm_transactor_storage(), - ..Default::default() - } - .assimilate_storage(&mut t) - .unwrap(); - - // EVM accounts are self-sufficient. - let mut evm_accounts = BTreeMap::new(); - evm_accounts.insert( - evm_account(), - fp_evm::GenesisAccount { - nonce: U256::from(INITIAL_EVM_NONCE), - balance: U256::from(INITIAL_EVM_BALANCE), - storage: Default::default(), - code: vec![ - 0x00, // STOP - ], - }, - ); - - let genesis_config = pallet_evm::GenesisConfig:: { - accounts: evm_accounts, - ..Default::default() - }; - genesis_config.assimilate_storage(&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 statemine_ext(para_id: u32) -> sp_io::TestExternalities { - use statemine_like::{MsgQueue, Runtime, System}; - - let mut t = frame_system::GenesisConfig::::default() - .build_storage() - .unwrap(); - - pallet_balances::GenesisConfig:: { - balances: vec![ - (RELAYALICE.into(), INITIAL_BALANCE), - (RELAYBOB.into(), INITIAL_BALANCE), - ], - dev_accounts: None, - } - .assimilate_storage(&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(paras: Vec) -> sp_io::TestExternalities { - use relay_chain::{Runtime, System}; - - let mut t = frame_system::GenesisConfig::::default() - .build_storage() - .unwrap(); - - pallet_balances::GenesisConfig:: { - balances: vec![(RELAYALICE, INITIAL_BALANCE)], - dev_accounts: None, - } - .assimilate_storage(&mut t) - .unwrap(); - - let para_genesis: Vec<(ParaId, ParaGenesisArgs)> = paras - .iter() - .map(|¶_id| (para_id.into(), mock_para_genesis_info())) - .collect(); - - let genesis_config = ConfigurationGenesisConfig:: { - config: mock_relay_config(), - }; - genesis_config.assimilate_storage(&mut t).unwrap(); - - let genesis_config = ParasGenesisConfig:: { - paras: para_genesis, - ..Default::default() - }; - genesis_config.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 Hrmp = polkadot_runtime_parachains::hrmp::Pallet; - -pub type StatemineBalances = pallet_balances::Pallet; -pub type StatemineChainPalletXcm = pallet_xcm::Pallet; -pub type StatemineAssets = pallet_assets::Pallet; - -pub type ParachainPalletXcm = pallet_xcm::Pallet; - -pub type RelayBalances = pallet_balances::Pallet; -pub type ParaBalances = pallet_balances::Pallet; -pub type XcmTransactor = pallet_xcm_transactor::Pallet; diff --git a/runtime/moonriver/tests/xcm_mock/parachain.rs b/runtime/moonriver/tests/xcm_mock/parachain.rs deleted file mode 100644 index 66f464268ce..00000000000 --- a/runtime/moonriver/tests/xcm_mock/parachain.rs +++ /dev/null @@ -1,1007 +0,0 @@ -// Copyright 2021 Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -//! Parachain runtime mock. - -use frame_support::{ - construct_runtime, ensure, parameter_types, - traits::{ - fungible::NativeOrWithId, ConstU32, EitherOf, Everything, Get, InstanceFilter, Nothing, - PalletInfoAccess, - }, - weights::Weight, - PalletId, -}; -use frame_system::{pallet_prelude::BlockNumberFor, EnsureRoot}; -use moonbeam_runtime_common::{ - impl_asset_conversion::AssetRateConverter, impl_multiasset_paymaster::MultiAssetPaymaster, - xcm_origins::AllowSiblingParachains, -}; -use pallet_moonbeam_foreign_assets::{MapSuccessToGovernance, MapSuccessToXcm}; -use pallet_xcm::{migration::v1::VersionUncheckedMigrateToV1, EnsureXcm}; -use parity_scale_codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen}; -use sp_core::{H160, H256}; -use sp_runtime::{ - traits::{BlakeTwo256, Hash, IdentityLookup, MaybeEquivalence, Zero}, - Permill, -}; -use sp_std::{convert::TryFrom, prelude::*}; -use xcm::{latest::prelude::*, Version as XcmVersion, VersionedXcm}; - -use cumulus_primitives_core::relay_chain::HrmpChannelId; -use pallet_ethereum::PostLogContent; -use polkadot_core_primitives::BlockNumber as RelayBlockNumber; -use polkadot_parachain::primitives::{Id as ParaId, Sibling}; -use xcm::latest::{ - Error as XcmError, ExecuteXcm, - Junction::{PalletInstance, Parachain}, - Location, NetworkId, Outcome, Xcm, -}; -use xcm_builder::{ - AccountKey20Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, - AllowTopLevelPaidExecutionFrom, Case, EnsureXcmOrigin, FixedWeightBounds, FungibleAdapter, - IsConcrete, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, - SiblingParachainConvertsVia, SignedAccountKey20AsNative, SovereignSignedViaLocation, - TakeWeightCredit, WithComputedOrigin, -}; -use xcm_executor::{Config, XcmExecutor}; - -#[cfg(feature = "runtime-benchmarks")] -use moonbeam_runtime_common::benchmarking::BenchmarkHelper as ArgumentsBenchmarkHelper; -pub use moonriver_runtime::xcm_config::AssetType; -use scale_info::TypeInfo; -use xcm_simulator::{ - DmpMessageHandlerT as DmpMessageHandler, XcmpMessageFormat, - XcmpMessageHandlerT as XcmpMessageHandler, -}; - -pub type AccountId = moonbeam_core_primitives::AccountId; -pub type Balance = u128; -pub type AssetId = u128; -pub type BlockNumber = BlockNumberFor; - -parameter_types! { - pub const BlockHashCount: u32 = 250; -} - -impl frame_system::Config for Runtime { - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type RuntimeTask = RuntimeTask; - type Nonce = u64; - type Block = Block; - type Hash = H256; - type Hashing = ::sp_runtime::traits::BlakeTwo256; - type AccountId = AccountId; - type Lookup = IdentityLookup; - type RuntimeEvent = RuntimeEvent; - 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 = frame_support::traits::ConstU32<16>; - type SingleBlockMigrations = (); - type MultiBlockMigrator = (); - type PreInherents = (); - type PostInherents = (); - type PostTransactions = (); - type ExtensionsWeightInfo = (); -} - -parameter_types! { - pub ExistentialDeposit: Balance = 0; - pub const MaxLocks: u32 = 50; - pub const MaxReserves: u32 = 50; -} - -impl pallet_balances::Config for Runtime { - type MaxLocks = MaxLocks; - type Balance = Balance; - type RuntimeEvent = RuntimeEvent; - type DustRemoval = (); - type ExistentialDeposit = ExistentialDeposit; - type AccountStore = System; - type WeightInfo = (); - type MaxReserves = MaxReserves; - type ReserveIdentifier = [u8; 8]; - type RuntimeHoldReason = (); - type FreezeIdentifier = (); - type MaxFreezes = (); - type RuntimeFreezeReason = (); - type DoneSlashHandler = (); -} - -/// Type for specifying how a `Location` can be converted into an `AccountId`. This is used -/// when determining ownership of accounts for asset transacting and when attempting to use XCM -/// `Transact` in order to determine the dispatch Origin. -pub type LocationToAccountId = ( - // The parent (Relay-chain) origin converts to the default `AccountId`. - ParentIsPreset, - // Sibling parachain origins convert to AccountId via the `ParaId::into`. - SiblingParachainConvertsVia, - AccountKey20Aliases, - // Generate remote accounts according to polkadot standards - xcm_builder::HashedDescription< - AccountId, - xcm_builder::DescribeFamily, - >, -); - -/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, -/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can -/// biases the kind of local `Origin` it will become. -pub type XcmOriginToTransactDispatchOrigin = ( - // Sovereign account converter; this attempts to derive an `AccountId` from the origin location - // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for - // foreign chains who want to have a local sovereign account on this chain which they control. - SovereignSignedViaLocation, - // Native converter for Relay-chain (Parent) location; will converts to a `Relay` origin when - // recognised. - RelayChainAsNative, - // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when - // recognised. - SiblingParachainAsNative, - // Superuser converter for the Relay-chain (Parent) location. This will allow it to issue a - // transaction from the Root origin. - ParentAsSuperuser, - // Xcm origins can be represented natively under the Xcm pallet's Xcm origin. - pallet_xcm::XcmPassthrough, - SignedAccountKey20AsNative, -); - -parameter_types! { - pub const UnitWeightCost: Weight = Weight::from_parts(1u64, 1u64); - pub MaxInstructions: u32 = 100; -} - -pub type LocalAssetTransactor = FungibleAdapter< - // Use this currency: - Balances, - // Use this currency when it is a fungible asset matching any of the locations in - // SelfReserveRepresentations - IsConcrete, - // We can convert the Locations with our converter above: - LocationToAccountId, - // Our chain's account ID type (we can't get away without mentioning it explicitly): - AccountId, - // We dont allow teleport - (), ->; - -// We use both transactors -pub type AssetTransactors = (LocalAssetTransactor, EvmForeignAssets); - -pub type XcmRouter = super::ParachainXcmRouter; - -pub type XcmBarrier = ( - // Weight that is paid for may be consumed. - TakeWeightCredit, - // Expected responses are OK. - AllowKnownQueryResponses, - WithComputedOrigin< - ( - // If the message is one that immediately attemps to pay for execution, then allow it. - AllowTopLevelPaidExecutionFrom, - // Subscriptions for version tracking are OK. - AllowSubscriptionsFrom, - ), - UniversalLocation, - ConstU32<8>, - >, -); - -parameter_types! { - /// Xcm fees will go to the treasury account - pub XcmFeesAccount: AccountId = Treasury::account_id(); - /// Parachain token units per second of execution - pub ParaTokensPerSecond: u128 = 1000000000000; -} - -pub struct WeightToFee; -impl sp_weights::WeightToFee for WeightToFee { - type Balance = Balance; - - fn weight_to_fee(weight: &Weight) -> Self::Balance { - use sp_runtime::SaturatedConversion as _; - Self::Balance::saturated_from(weight.ref_time()) - .saturating_mul(ParaTokensPerSecond::get()) - .saturating_div(frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND as u128) - } -} - -parameter_types! { - pub RelayNetwork: NetworkId = moonriver_runtime::xcm_config::RelayNetwork::get(); - pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); - pub UniversalLocation: InteriorLocation = - [GlobalConsensus(RelayNetwork::get()), Parachain(MsgQueue::parachain_id().into())].into(); - pub SelfReserve: Location = Location { - parents:0, - interior: [ - PalletInstance(::index() as u8) - ].into() - }; - pub const MaxAssetsIntoHolding: u32 = 64; - - pub AssetHubLocation: Location = Location::new(1, [Parachain(1000)]); - pub RelayLocationFilter: AssetFilter = Wild(AllOf { - fun: WildFungible, - id: xcm::prelude::AssetId(Location::parent()), - }); - - pub RelayChainNativeAssetFromAssetHub: (AssetFilter, Location) = ( - RelayLocationFilter::get(), - AssetHubLocation::get() - ); -} - -use frame_system::RawOrigin; -use sp_runtime::traits::PostDispatchInfoOf; -use sp_runtime::DispatchErrorWithPostInfo; -use xcm_executor::traits::CallDispatcher; -moonbeam_runtime_common::impl_moonbeam_xcm_call!(); - -type Reserves = ( - // Relaychain (DOT) from Asset Hub - Case, - // Assets which the reserve is the same as the origin. - xcm_primitives::MultiNativeAsset< - xcm_primitives::AbsoluteAndRelativeReserve, - >, -); - -pub struct XcmConfig; -impl Config for XcmConfig { - type RuntimeCall = RuntimeCall; - type XcmSender = XcmRouter; - type AssetTransactor = AssetTransactors; - type OriginConverter = XcmOriginToTransactDispatchOrigin; - type IsReserve = Reserves; - type IsTeleporter = (); - type UniversalLocation = UniversalLocation; - type Barrier = XcmBarrier; - type Weigher = FixedWeightBounds; - type Trader = pallet_xcm_weight_trader::Trader; - type ResponseHandler = PolkadotXcm; - type SubscriptionService = PolkadotXcm; - type AssetTrap = PolkadotXcm; - type AssetClaims = PolkadotXcm; - type CallDispatcher = MoonbeamCall; - type AssetLocker = (); - type AssetExchanger = (); - type PalletInstancesInfo = (); - type MaxAssetsIntoHolding = MaxAssetsIntoHolding; - type FeeManager = (); - type MessageExporter = (); - type UniversalAliases = Nothing; - type SafeCallFilter = Everything; - type Aliasers = Nothing; - type TransactionalProcessor = (); - type HrmpNewChannelOpenRequestHandler = (); - type HrmpChannelAcceptedHandler = (); - type HrmpChannelClosingHandler = (); - type XcmRecorder = PolkadotXcm; - type XcmEventEmitter = PolkadotXcm; -} - -impl cumulus_pallet_xcm::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type XcmExecutor = XcmExecutor; -} - -// Our currencyId. We distinguish for now between SelfReserve, and Others, defined by their Id. -#[derive( - Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo, DecodeWithMemTracking, -)] -pub enum CurrencyId { - SelfReserve, - ForeignAsset(AssetId), -} - -// How to convert from CurrencyId to Location -pub struct CurrencyIdToLocation(sp_std::marker::PhantomData); -impl sp_runtime::traits::Convert> - for CurrencyIdToLocation -where - AssetXConverter: MaybeEquivalence, -{ - fn convert(currency: CurrencyId) -> Option { - match currency { - CurrencyId::SelfReserve => { - // For now and until Xtokens is adapted to handle 0.9.16 version we use - // the old anchoring here - // This is not a problem in either cases, since the view of the destination - // chain does not change - // TODO! change this to NewAnchoringSelfReserve once xtokens is adapted for it - let multi: Location = SelfReserve::get(); - Some(multi) - } - CurrencyId::ForeignAsset(asset) => AssetXConverter::convert_back(&asset), - } - } -} - -parameter_types! { - pub const BaseXcmWeight: Weight = Weight::from_parts(100u64, 100u64); - pub const MaxAssetsForTransfer: usize = 2; - pub SelfLocation: Location = Location::here(); - pub SelfLocationAbsolute: Location = Location { - parents:1, - interior: [ - Parachain(MsgQueue::parachain_id().into()) - ].into() - }; -} - -parameter_types! { - pub const ProposalBond: Permill = Permill::from_percent(5); - pub const ProposalBondMinimum: Balance = 0; - pub const SpendPeriod: u32 = 0; - pub const TreasuryId: PalletId = PalletId(*b"pc/trsry"); - pub const MaxApprovals: u32 = 100; - pub TreasuryAccount: AccountId = Treasury::account_id(); -} - -impl pallet_treasury::Config for Runtime { - type PalletId = TreasuryId; - type Currency = Balances; - type RejectOrigin = EnsureRoot; - type RuntimeEvent = RuntimeEvent; - type SpendPeriod = SpendPeriod; - type Burn = (); - type BurnDestination = (); - type MaxApprovals = MaxApprovals; - type WeightInfo = (); - type SpendFunds = (); - type SpendOrigin = frame_support::traits::NeverEnsureOrigin; // Same as Polkadot - type AssetKind = NativeOrWithId; - type Beneficiary = AccountId; - type BeneficiaryLookup = IdentityLookup; - type Paymaster = MultiAssetPaymaster; - type BalanceConverter = AssetRateConverter; - type PayoutPeriod = ConstU32<0>; - #[cfg(feature = "runtime-benchmarks")] - type BenchmarkHelper = ArgumentsBenchmarkHelper; - type BlockNumberProvider = System; -} - -#[frame_support::pallet] -pub mod mock_msg_queue { - use super::*; - use frame_support::pallet_prelude::*; - - #[pallet::config] - pub trait Config: frame_system::Config { - type XcmExecutor: ExecuteXcm; - } - - #[pallet::call] - impl Pallet {} - - #[pallet::pallet] - pub struct Pallet(_); - - #[pallet::storage] - #[pallet::getter(fn parachain_id)] - pub(super) type ParachainId = StorageValue<_, ParaId, 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, InstructionError), - /// 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 = Location::new(1, [Parachain(sender.into())]); - let mut id = [0u8; 32]; - id.copy_from_slice(hash.as_ref()); - match T::XcmExecutor::prepare_and_execute( - location, - xcm, - &mut id, - max_weight, - Weight::zero(), - ) { - Outcome::Error(error) => { - (Err(error.clone()), Event::Fail(Some(hash), error)) - } - Outcome::Complete { used } => (Ok(used), 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 { used, error } => { - (Ok(used), Event::Fail(Some(hash), error)) - } - } - } - Err(()) => ( - Err(InstructionError { - error: XcmError::UnhandledXcmVersion, - index: 0, - }), - 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 mut 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::prepare_and_execute( - Parent, - x, - &mut id, - limit, - Weight::zero(), - ); - - Self::deposit_event(Event::ExecutedDownward(id, outcome)); - } - } - } - limit - } - } -} - -// 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 {} - - #[pallet::call] - impl Pallet {} - - #[pallet::pallet] - 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)); - } - } -} - -impl mock_msg_queue::Config for Runtime { - type XcmExecutor = XcmExecutor; -} - -impl mock_version_changer::Config for Runtime {} - -pub type LocalOriginToLocation = - xcm_primitives::SignedToAccountId20; - -parameter_types! { - pub MatcherLocation: Location = Location::here(); -} - -impl pallet_xcm::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type SendXcmOrigin = EnsureXcmOrigin; - type XcmRouter = XcmRouter; - type ExecuteXcmOrigin = EnsureXcmOrigin; - type XcmExecuteFilter = frame_support::traits::Nothing; - type XcmExecutor = XcmExecutor; - // Do not allow teleports - type XcmTeleportFilter = Nothing; - type XcmReserveTransferFilter = Everything; - type Weigher = FixedWeightBounds; - type UniversalLocation = UniversalLocation; - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; - // We use a custom one to test runtime upgrades - type AdvertisedXcmVersion = XcmVersioner; - type Currency = Balances; - type CurrencyMatcher = IsConcrete; - type TrustedLockers = (); - type SovereignAccountOf = (); - type MaxLockers = ConstU32<8>; - type WeightInfo = pallet_xcm::TestWeightInfo; - type MaxRemoteLockConsumers = ConstU32<0>; - type RemoteLockConsumerIdentifier = (); - type AdminOrigin = frame_system::EnsureRoot; - type AuthorizedAliasConsideration = Disabled; -} - -#[derive( - Clone, - Default, - Eq, - Debug, - PartialEq, - Ord, - PartialOrd, - Encode, - Decode, - TypeInfo, - DecodeWithMemTracking, -)] -pub struct AssetMetadata { - pub name: Vec, - pub symbol: Vec, - pub decimals: u8, -} - -pub struct AccountIdToH160; -impl sp_runtime::traits::Convert for AccountIdToH160 { - fn convert(account_id: AccountId) -> H160 { - account_id.into() - } -} - -pub struct EvmForeignAssetIdFilter; -impl frame_support::traits::Contains for EvmForeignAssetIdFilter { - fn contains(_asset_id: &AssetId) -> bool { - true - } -} - -pub type ForeignAssetManagerOrigin = EitherOf< - MapSuccessToXcm>, - MapSuccessToGovernance>, ->; - -moonbeam_runtime_common::impl_evm_runner_precompile_or_eth_xcm!(); - -parameter_types! { - pub ForeignAssetCreationDeposit: u128 = 100 * currency::MOVR; -} - -impl pallet_moonbeam_foreign_assets::Config for Runtime { - type AccountIdToH160 = AccountIdToH160; - type AssetIdFilter = EvmForeignAssetIdFilter; - type EvmRunner = EvmRunnerPrecompileOrEthXcm; - type ConvertLocation = - SiblingParachainConvertsVia; - type ForeignAssetCreatorOrigin = ForeignAssetManagerOrigin; - type ForeignAssetFreezerOrigin = ForeignAssetManagerOrigin; - type ForeignAssetModifierOrigin = ForeignAssetManagerOrigin; - type ForeignAssetUnfreezerOrigin = ForeignAssetManagerOrigin; - type OnForeignAssetCreated = (); - type MaxForeignAssets = ConstU32<256>; - type WeightInfo = (); - type XcmLocationToH160 = LocationToH160; - type ForeignAssetCreationDeposit = ForeignAssetCreationDeposit; - type Balance = Balance; - type Currency = Balances; -} - -// 1 KSM should be enough -parameter_types! { - pub MaxHrmpRelayFee: Asset = (Location::parent(), 1_000_000_000_000u128).into(); -} - -impl pallet_xcm_transactor::Config for Runtime { - type Balance = Balance; - type Transactor = moonriver_runtime::xcm_config::Transactors; - type DerivativeAddressRegistrationOrigin = EnsureRoot; - type SovereignAccountDispatcherOrigin = frame_system::EnsureRoot; - type CurrencyId = CurrencyId; - type AccountIdToLocation = xcm_primitives::AccountIdToLocation; - type CurrencyIdToLocation = CurrencyIdToLocation; - type SelfLocation = SelfLocation; - type Weigher = xcm_builder::FixedWeightBounds; - type UniversalLocation = UniversalLocation; - type XcmSender = XcmRouter; - type BaseXcmWeight = BaseXcmWeight; - type AssetTransactor = AssetTransactors; - type ReserveProvider = xcm_primitives::AbsoluteAndRelativeReserve; - type WeightInfo = (); - type HrmpManipulatorOrigin = EnsureRoot; - type HrmpOpenOrigin = EnsureRoot; - type MaxHrmpFee = xcm_builder::Case; - type FeeTrader = moonbeam_tests_primitives::MemoryFeeTrader; -} - -parameter_types! { - pub RelayLocation: Location = Location::parent(); -} - -impl pallet_xcm_weight_trader::Config for Runtime { - type AccountIdToLocation = xcm_primitives::AccountIdToLocation; - type AddSupportedAssetOrigin = EnsureRoot; - type AssetLocationFilter = Everything; - type AssetTransactor = AssetTransactors; - type Balance = Balance; - type EditSupportedAssetOrigin = EnsureRoot; - type NativeLocation = SelfReserve; - type PauseSupportedAssetOrigin = EnsureRoot; - type RemoveSupportedAssetOrigin = EnsureRoot; - type ResumeSupportedAssetOrigin = EnsureRoot; - type WeightInfo = (); - type WeightToFee = WeightToFee; - type XcmFeesAccount = XcmFeesAccount; - #[cfg(feature = "runtime-benchmarks")] - type NotFilteredLocation = RelayLocation; -} - -parameter_types! { - pub const MinimumPeriod: u64 = 1000; -} -impl pallet_timestamp::Config for Runtime { - type Moment = u64; - type OnTimestampSet = (); - type MinimumPeriod = MinimumPeriod; - type WeightInfo = (); -} - -parameter_types! { - pub BlockGasLimit: U256 = moonriver_runtime::BlockGasLimit::get(); - pub WeightPerGas: Weight = moonriver_runtime::WeightPerGas::get(); - pub const GasLimitPovSizeRatio: u64 = moonriver_runtime::GasLimitPovSizeRatio::get(); - pub GasLimitStorageGrowthRatio: u64 = moonriver_runtime::GasLimitStorageGrowthRatio::get(); -} - -impl pallet_evm::Config for Runtime { - type FeeCalculator = (); - type GasWeightMapping = pallet_evm::FixedGasWeightMapping; - type WeightPerGas = WeightPerGas; - - type CallOrigin = pallet_evm::EnsureAddressRoot; - type WithdrawOrigin = pallet_evm::EnsureAddressNever; - - type AddressMapping = pallet_evm::IdentityAddressMapping; - type Currency = Balances; - type Runner = pallet_evm::runner::stack::Runner; - - type PrecompilesType = (); - type PrecompilesValue = (); - type ChainId = (); - type BlockGasLimit = BlockGasLimit; - type TransactionGasLimit = moonriver_runtime::TransactionGasLimit; - type OnChargeTransaction = (); - type BlockHashMapping = pallet_evm::SubstrateBlockHashMapping; - type FindAuthor = (); - type OnCreate = (); - type GasLimitPovSizeRatio = GasLimitPovSizeRatio; - type GasLimitStorageGrowthRatio = GasLimitStorageGrowthRatio; - type Timestamp = Timestamp; - type WeightInfo = pallet_evm::weights::SubstrateWeight; - type AccountProvider = FrameSystemAccountProvider; - type CreateOriginFilter = (); - type CreateInnerOriginFilter = (); -} - -#[allow(dead_code)] -pub struct NormalFilter; - -impl frame_support::traits::Contains for NormalFilter { - fn contains(c: &RuntimeCall) -> bool { - match c { - _ => true, - } - } -} - -// We need to use the encoding from the relay mock runtime -#[derive(Encode, Decode)] -pub enum RelayCall { - #[codec(index = 5u8)] - // the index should match the position of the module in `construct_runtime!` - Utility(UtilityCall), - #[codec(index = 6u8)] - // the index should match the position of the module in `construct_runtime!` - Hrmp(HrmpCall), -} - -#[derive(Encode, Decode)] -pub enum UtilityCall { - #[codec(index = 1u8)] - AsDerivative(u16), -} - -// HRMP call encoding, needed for xcm transactor pallet -#[derive(Encode, Decode)] -pub enum HrmpCall { - #[codec(index = 0u8)] - InitOpenChannel(ParaId, u32, u32), - #[codec(index = 1u8)] - AcceptOpenChannel(ParaId), - #[codec(index = 2u8)] - CloseChannel(HrmpChannelId), - #[codec(index = 6u8)] - CancelOpenRequest(HrmpChannelId, u32), -} - -#[allow(dead_code)] -pub struct MockHrmpEncoder; - -impl xcm_primitives::HrmpEncodeCall for MockHrmpEncoder { - fn hrmp_encode_call( - call: xcm_primitives::HrmpAvailableCalls, - ) -> Result, xcm::latest::Error> { - match call { - xcm_primitives::HrmpAvailableCalls::InitOpenChannel(a, b, c) => Ok(RelayCall::Hrmp( - HrmpCall::InitOpenChannel(a.clone(), b.clone(), c.clone()), - ) - .encode()), - xcm_primitives::HrmpAvailableCalls::AcceptOpenChannel(a) => { - Ok(RelayCall::Hrmp(HrmpCall::AcceptOpenChannel(a.clone())).encode()) - } - xcm_primitives::HrmpAvailableCalls::CloseChannel(a) => { - Ok(RelayCall::Hrmp(HrmpCall::CloseChannel(a.clone())).encode()) - } - xcm_primitives::HrmpAvailableCalls::CancelOpenRequest(a, b) => { - Ok(RelayCall::Hrmp(HrmpCall::CancelOpenRequest(a.clone(), b.clone())).encode()) - } - } - } -} - -parameter_types! { - pub const PostBlockAndTxnHashes: PostLogContent = PostLogContent::BlockAndTxnHashes; - pub const AllowUnprotectedTxs: bool = false; -} - -impl pallet_ethereum::Config for Runtime { - type StateRoot = - pallet_ethereum::IntermediateStateRoot<::Version>; - type PostLogContent = PostBlockAndTxnHashes; - type ExtraDataLength = ConstU32<30>; - type AllowUnprotectedTxs = AllowUnprotectedTxs; -} - -parameter_types! { - pub ReservedXcmpWeight: Weight = Weight::from_parts(u64::max_value(), 0); -} - -#[derive( - Copy, - Clone, - Eq, - PartialEq, - Ord, - PartialOrd, - Encode, - Decode, - Debug, - MaxEncodedLen, - TypeInfo, - DecodeWithMemTracking, -)] -pub enum ProxyType { - NotAllowed = 0, - Any = 1, -} - -impl pallet_evm_precompile_proxy::EvmProxyCallFilter for ProxyType {} - -impl InstanceFilter for ProxyType { - fn filter(&self, _c: &RuntimeCall) -> bool { - match self { - ProxyType::NotAllowed => false, - ProxyType::Any => true, - } - } - fn is_superset(&self, _o: &Self) -> bool { - false - } -} - -impl Default for ProxyType { - fn default() -> Self { - Self::NotAllowed - } -} - -parameter_types! { - pub const ProxyCost: u64 = 1; -} - -impl pallet_proxy::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type RuntimeCall = RuntimeCall; - type Currency = Balances; - type ProxyType = ProxyType; - type ProxyDepositBase = ProxyCost; - type ProxyDepositFactor = ProxyCost; - type MaxProxies = ConstU32<32>; - type WeightInfo = pallet_proxy::weights::SubstrateWeight; - type MaxPending = ConstU32<32>; - type CallHasher = BlakeTwo256; - type AnnouncementDepositBase = ProxyCost; - type AnnouncementDepositFactor = ProxyCost; - type BlockNumberProvider = System; -} - -pub struct EthereumXcmEnsureProxy; -impl xcm_primitives::EnsureProxy for EthereumXcmEnsureProxy { - fn ensure_ok(delegator: AccountId, delegatee: AccountId) -> Result<(), &'static str> { - // The EVM implicitly contains an Any proxy, so we only allow for "Any" proxies - let def: pallet_proxy::ProxyDefinition = - pallet_proxy::Pallet::::find_proxy( - &delegator, - &delegatee, - Some(ProxyType::Any), - ) - .map_err(|_| "proxy error: expected `ProxyType::Any`")?; - // We only allow to use it for delay zero proxies, as the call will immediately be executed - ensure!(def.delay.is_zero(), "proxy delay is Non-zero`"); - Ok(()) - } -} - -impl pallet_ethereum_xcm::Config for Runtime { - type InvalidEvmTransactionError = pallet_ethereum::InvalidTransactionWrapper; - type ValidatedTransaction = pallet_ethereum::ValidatedTransaction; - type XcmEthereumOrigin = pallet_ethereum_xcm::EnsureXcmEthereumTransaction; - type ReservedXcmpWeight = ReservedXcmpWeight; - type EnsureProxy = EthereumXcmEnsureProxy; - type ControllerOrigin = EnsureRoot; - type ForceOrigin = EnsureRoot; -} - -type Block = frame_system::mocking::MockBlockU32; - -construct_runtime!( - pub enum Runtime { - System: frame_system, - Balances: pallet_balances, - MsgQueue: mock_msg_queue, - XcmVersioner: mock_version_changer, - - PolkadotXcm: pallet_xcm, - CumulusXcm: cumulus_pallet_xcm, - XcmTransactor: pallet_xcm_transactor, - XcmWeightTrader: pallet_xcm_weight_trader, - Treasury: pallet_treasury, - Proxy: pallet_proxy, - - Timestamp: pallet_timestamp, - EVM: pallet_evm, - Ethereum: pallet_ethereum, - EthereumXcm: pallet_ethereum_xcm, - EvmForeignAssets: pallet_moonbeam_foreign_assets, - } -); - -pub(crate) fn para_events() -> Vec { - System::events() - .into_iter() - .map(|r| r.event) - .filter_map(|e| Some(e)) - .collect::>() -} - -use frame_support::traits::{Disabled, OnFinalize, OnInitialize, UncheckedOnRuntimeUpgrade}; -use moonriver_runtime::{currency, xcm_config::LocationToH160}; -use pallet_evm::FrameSystemAccountProvider; - -pub(crate) fn on_runtime_upgrade() { - VersionUncheckedMigrateToV1::::on_runtime_upgrade(); -} - -pub(crate) fn para_roll_to(n: BlockNumber) { - 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/moonriver/tests/xcm_mock/relay_chain.rs b/runtime/moonriver/tests/xcm_mock/relay_chain.rs deleted file mode 100644 index dfc0eabea08..00000000000 --- a/runtime/moonriver/tests/xcm_mock/relay_chain.rs +++ /dev/null @@ -1,452 +0,0 @@ -// Copyright 2021 Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -//! Relay chain runtime mock. - -use frame_support::{ - construct_runtime, parameter_types, - traits::{Everything, Nothing, ProcessMessage, ProcessMessageError}, -}; -use frame_system::pallet_prelude::BlockNumberFor; -use sp_core::H256; -use sp_runtime::{ - traits::{ConstU32, IdentityLookup}, - AccountId32, -}; - -use frame_support::weights::{Weight, WeightMeter}; -use polkadot_parachain::primitives::Id as ParaId; -use polkadot_runtime_parachains::{ - configuration, dmp, hrmp, - inclusion::{AggregateMessageOrigin, UmpQueueId}, - origin, paras, shared, -}; -use sp_runtime::transaction_validity::TransactionPriority; -use sp_runtime::Permill; -use xcm::latest::prelude::*; -use xcm_builder::{ - Account32Hash, AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, - AllowTopLevelPaidExecutionFrom, ChildParachainAsNative, ChildParachainConvertsVia, - ChildSystemParachainAsSuperuser, FixedRateOfFungible, FixedWeightBounds, - FungibleAdapter as XcmCurrencyAdapter, IsConcrete, ProcessXcmMessage, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, - WithComputedOrigin, -}; -use xcm_executor::{Config, XcmExecutor}; -pub type AccountId = AccountId32; -pub type Balance = u128; -pub type BlockNumber = BlockNumberFor; - -parameter_types! { - pub const BlockHashCount: u32 = 250; -} - -impl frame_system::Config for Runtime { - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type RuntimeTask = RuntimeTask; - type Nonce = u64; - type Block = Block; - type Hash = H256; - type Hashing = ::sp_runtime::traits::BlakeTwo256; - type AccountId = AccountId; - type Lookup = IdentityLookup; - type RuntimeEvent = RuntimeEvent; - 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>; - type SingleBlockMigrations = (); - type MultiBlockMigrator = (); - type PreInherents = (); - type PostInherents = (); - type PostTransactions = (); - type ExtensionsWeightInfo = (); -} - -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 RuntimeEvent = RuntimeEvent; - type DustRemoval = (); - type ExistentialDeposit = ExistentialDeposit; - type AccountStore = System; - type WeightInfo = (); - type MaxReserves = MaxReserves; - type ReserveIdentifier = [u8; 8]; - type RuntimeHoldReason = (); - type FreezeIdentifier = (); - type MaxFreezes = (); - type RuntimeFreezeReason = (); - type DoneSlashHandler = (); -} - -impl pallet_utility::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type RuntimeCall = RuntimeCall; - type WeightInfo = (); - type PalletsOrigin = OriginCaller; -} - -impl shared::Config for Runtime { - type DisabledValidators = (); -} - -impl configuration::Config for Runtime { - type WeightInfo = configuration::TestWeightInfo; -} - -parameter_types! { - pub KsmLocation: Location = Here.into(); - pub const KusamaNetwork: NetworkId = NetworkId::Kusama; - pub const AnyNetwork: Option = None; - pub UniversalLocation: InteriorLocation = Here; -} - -pub type SovereignAccountOf = ( - ChildParachainConvertsVia, - AccountId32Aliases, - // Not enabled in the relay per se, but we enable it to test - // the transact_through_signed extrinsic - Account32Hash, -); - -pub type LocalAssetTransactor = - XcmCurrencyAdapter, SovereignAccountOf, AccountId, ()>; - -type LocalOriginConverter = ( - SovereignSignedViaLocation, - ChildParachainAsNative, - SignedAccountId32AsNative, - ChildSystemParachainAsSuperuser, -); - -parameter_types! { - pub const BaseXcmWeight: Weight = Weight::from_parts(1000u64, 1000u64); - pub KsmPerSecond: (AssetId, u128, u128) = (AssetId(KsmLocation::get()), 1, 1); - pub const MaxInstructions: u32 = 100; - pub const MaxAssetsIntoHolding: u32 = 64; - pub MatcherLocation: Location = Location::here(); -} - -pub type XcmRouter = super::RelayChainXcmRouter; - -pub type XcmBarrier = ( - // Weight that is paid for may be consumed. - TakeWeightCredit, - // Expected responses are OK. - AllowKnownQueryResponses, - WithComputedOrigin< - ( - // If the message is one that immediately attemps to pay for execution, then allow it. - AllowTopLevelPaidExecutionFrom, - // Subscriptions for version tracking are OK. - AllowSubscriptionsFrom, - ), - UniversalLocation, - ConstU32<8>, - >, -); - -parameter_types! { - pub Kusama: AssetFilter = Wild(AllOf { fun: WildFungible, id: AssetId(KsmLocation::get()) }); - pub Statemine: Location = Parachain(1000).into(); - pub KusamaForStatemine: (AssetFilter, Location) = (Kusama::get(), Statemine::get()); -} - -pub type TrustedTeleporters = xcm_builder::Case; - -pub struct XcmConfig; -impl Config for XcmConfig { - type RuntimeCall = RuntimeCall; - type XcmSender = XcmRouter; - type AssetTransactor = LocalAssetTransactor; - type OriginConverter = LocalOriginConverter; - type IsReserve = (); - type IsTeleporter = TrustedTeleporters; - type UniversalLocation = UniversalLocation; - type Barrier = XcmBarrier; - type Weigher = FixedWeightBounds; - type Trader = FixedRateOfFungible; - type ResponseHandler = XcmPallet; - type AssetTrap = XcmPallet; - type AssetClaims = XcmPallet; - type SubscriptionService = XcmPallet; - type CallDispatcher = RuntimeCall; - type AssetLocker = (); - type AssetExchanger = (); - type PalletInstancesInfo = (); - type MaxAssetsIntoHolding = MaxAssetsIntoHolding; - type FeeManager = (); - type MessageExporter = (); - type UniversalAliases = Nothing; - type SafeCallFilter = Everything; - type Aliasers = Nothing; - type TransactionalProcessor = (); - type HrmpNewChannelOpenRequestHandler = (); - type HrmpChannelAcceptedHandler = (); - type HrmpChannelClosingHandler = (); - type XcmRecorder = XcmPallet; - type XcmEventEmitter = XcmPallet; -} - -pub type LocalOriginToLocation = SignedToAccountId32; - -impl pallet_xcm::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - 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 UniversalLocation = UniversalLocation; - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; - type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; - type Currency = Balances; - type CurrencyMatcher = (); - type TrustedLockers = (); - type SovereignAccountOf = (); - type MaxLockers = ConstU32<8>; - type WeightInfo = pallet_xcm::TestWeightInfo; - type MaxRemoteLockConsumers = ConstU32<0>; - type RemoteLockConsumerIdentifier = (); - type AdminOrigin = frame_system::EnsureRoot; - type AuthorizedAliasConsideration = Disabled; -} - -parameter_types! { - pub const FirstMessageFactorPercent: u64 = 100; -} - -parameter_types! { - pub const ParasUnsignedPriority: TransactionPriority = TransactionPriority::max_value(); -} - -/// A very dumb implementation of `EstimateNextSessionRotation`. At the moment of writing, this -/// is more to satisfy type requirements rather than to test anything. -pub struct TestNextSessionRotation; - -impl frame_support::traits::EstimateNextSessionRotation for TestNextSessionRotation { - fn average_session_length() -> u32 { - 10 - } - - fn estimate_current_session_progress(_now: u32) -> (Option, Weight) { - (None, Weight::zero()) - } - - fn estimate_next_session_rotation(_now: u32) -> (Option, Weight) { - (None, Weight::zero()) - } -} - -impl paras::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = paras::TestWeightInfo; - type UnsignedPriority = ParasUnsignedPriority; - type NextSessionRotation = TestNextSessionRotation; - type QueueFootprinter = (); - type OnNewHead = (); - type AssignCoretime = (); - type Fungible = (); - type CooldownRemovalMultiplier = (); - type AuthorizeCurrentCodeOrigin = frame_system::EnsureRoot; -} - -impl dmp::Config for Runtime {} - -parameter_types! { - pub const DefaultChannelSizeAndCapacityWithSystem: (u32, u32) = (4, 1); -} - -impl hrmp::Config for Runtime { - type RuntimeOrigin = RuntimeOrigin; - type RuntimeEvent = RuntimeEvent; - type Currency = Balances; - type WeightInfo = TestHrmpWeightInfo; - type ChannelManager = frame_system::EnsureRoot; - type DefaultChannelSizeAndCapacityWithSystem = DefaultChannelSizeAndCapacityWithSystem; - type VersionWrapper = XcmPallet; -} - -impl frame_system::offchain::CreateTransactionBase for Runtime -where - RuntimeCall: From, -{ - type Extrinsic = UncheckedExtrinsic; - type RuntimeCall = RuntimeCall; -} - -impl origin::Config for Runtime {} - -type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; -type Block = frame_system::mocking::MockBlockU32; - -impl frame_system::offchain::CreateInherent for Runtime -where - RuntimeCall: From, -{ - fn create_inherent(call: RuntimeCall) -> UncheckedExtrinsic { - UncheckedExtrinsic::new_bare(call) - } - - fn create_bare(call: RuntimeCall) -> UncheckedExtrinsic { - UncheckedExtrinsic::new_bare(call) - } -} - -parameter_types! { - pub MessageQueueServiceWeight: Weight = Weight::from_parts(1_000_000_000, 1_000_000); - pub const MessageQueueHeapSize: u32 = 65_536; - pub const MessageQueueMaxStale: u32 = 16; -} - -pub struct MessageProcessor; -impl ProcessMessage for MessageProcessor { - type Origin = AggregateMessageOrigin; - - fn process_message( - message: &[u8], - origin: Self::Origin, - meter: &mut WeightMeter, - id: &mut [u8; 32], - ) -> Result { - let para = match origin { - AggregateMessageOrigin::Ump(UmpQueueId::Para(para)) => para, - }; - ProcessXcmMessage::, RuntimeCall>::process_message( - message, - Junction::Parachain(para.into()), - meter, - id, - ) - } -} - -impl pallet_message_queue::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Size = u32; - type HeapSize = MessageQueueHeapSize; - type MaxStale = MessageQueueMaxStale; - type ServiceWeight = MessageQueueServiceWeight; - type MessageProcessor = MessageProcessor; - type QueueChangeHandler = (); - type WeightInfo = (); - type QueuePausedQuery = (); - type IdleMaxServiceWeight = MessageQueueServiceWeight; -} - -construct_runtime!( - pub enum Runtime { - System: frame_system, - Balances: pallet_balances, - ParasOrigin: origin, - MessageQueue: pallet_message_queue, - XcmPallet: pallet_xcm, - Utility: pallet_utility, - Hrmp: hrmp, - Dmp: dmp, - Paras: paras, - Configuration: configuration, - } -); - -pub(crate) fn relay_events() -> Vec { - System::events() - .into_iter() - .map(|r| r.event) - .filter_map(|e| Some(e)) - .collect::>() -} - -use frame_support::traits::{Disabled, OnFinalize, OnInitialize}; -pub(crate) fn relay_roll_to(n: BlockNumber) { - 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()); - } -} - -/// A weight info that is only suitable for testing. -pub struct TestHrmpWeightInfo; - -impl hrmp::WeightInfo for TestHrmpWeightInfo { - fn hrmp_accept_open_channel() -> Weight { - Weight::from_parts(1, 0) - } - fn force_clean_hrmp(_: u32, _: u32) -> Weight { - Weight::from_parts(1, 0) - } - fn force_process_hrmp_close(_: u32) -> Weight { - Weight::from_parts(1, 0) - } - fn force_process_hrmp_open(_: u32) -> Weight { - Weight::from_parts(1, 0) - } - fn hrmp_cancel_open_request(_: u32) -> Weight { - Weight::from_parts(1, 0) - } - fn hrmp_close_channel() -> Weight { - Weight::from_parts(1, 0) - } - fn hrmp_init_open_channel() -> Weight { - Weight::from_parts(1, 0) - } - fn clean_open_channel_requests(_: u32) -> Weight { - Weight::from_parts(1, 0) - } - fn force_open_hrmp_channel(_: u32) -> Weight { - Weight::from_parts(1, 0) - } - fn establish_system_channel() -> Weight { - Weight::from_parts(1, 0) - } - - fn poke_channel_deposits() -> Weight { - Weight::from_parts(1, 0) - } - - fn establish_channel_with_system() -> Weight { - Weight::from_parts(1, 0) - } -} diff --git a/runtime/moonriver/tests/xcm_mock/statemine_like.rs b/runtime/moonriver/tests/xcm_mock/statemine_like.rs deleted file mode 100644 index 56276c6da0e..00000000000 --- a/runtime/moonriver/tests/xcm_mock/statemine_like.rs +++ /dev/null @@ -1,616 +0,0 @@ -// Copyright 2021 Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -//! Relay chain runtime mock. - -use frame_support::traits::Disabled; -use frame_support::{ - construct_runtime, parameter_types, - traits::{AsEnsureOriginWithArg, Contains, ContainsPair, Everything, Get, Nothing}, - weights::Weight, -}; -use frame_system::{EnsureRoot, EnsureSigned}; - -use sp_core::H256; -use sp_runtime::{ - traits::{ConstU32, Hash, IdentityLookup}, - AccountId32, -}; - -use polkadot_core_primitives::BlockNumber as RelayBlockNumber; - -use polkadot_parachain::primitives::Id as ParaId; -use polkadot_parachain::primitives::Sibling; -use sp_std::convert::TryFrom; -use xcm::latest::prelude::*; -use xcm::VersionedXcm; -use xcm_builder::{ - AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, - AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, - ConvertedConcreteId, EnsureXcmOrigin, FixedRateOfFungible, FixedWeightBounds, FungibleAdapter, - FungiblesAdapter, IsConcrete, NoChecking, ParentAsSuperuser, ParentIsPreset, - RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, -}; -use xcm_executor::{traits::JustTry, Config, XcmExecutor}; -use xcm_simulator::{ - DmpMessageHandlerT as DmpMessageHandler, XcmpMessageFormat, - XcmpMessageHandlerT as XcmpMessageHandler, -}; -pub type AccountId = AccountId32; -pub type Balance = u128; -pub type AssetId = u128; -pub type ReserveId = u128; - -parameter_types! { - pub const BlockHashCount: u32 = 250; -} - -impl frame_system::Config for Runtime { - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type RuntimeTask = RuntimeTask; - type Nonce = u64; - type Block = Block; - type Hash = H256; - type Hashing = ::sp_runtime::traits::BlakeTwo256; - type AccountId = AccountId; - type Lookup = IdentityLookup; - type RuntimeEvent = RuntimeEvent; - 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 = frame_support::traits::ConstU32<16>; - type SingleBlockMigrations = (); - type MultiBlockMigrator = (); - type PreInherents = (); - type PostInherents = (); - type PostTransactions = (); - type ExtensionsWeightInfo = (); -} - -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 RuntimeEvent = RuntimeEvent; - type DustRemoval = (); - type ExistentialDeposit = ExistentialDeposit; - type AccountStore = System; - type WeightInfo = (); - type MaxReserves = MaxReserves; - type ReserveIdentifier = [u8; 8]; - type RuntimeHoldReason = (); - type FreezeIdentifier = (); - type MaxFreezes = (); - type RuntimeFreezeReason = (); - type DoneSlashHandler = (); -} - -// Required for runtime benchmarks -pallet_assets::runtime_benchmarks_enabled! { - pub struct BenchmarkHelper; - impl pallet_assets::BenchmarkHelper for BenchmarkHelper - where - AssetIdParameter: From, - ReserveIdParameter: From, - { - fn create_asset_id_parameter(id: u32) -> AssetIdParameter { - (id as u128).into() - } - fn create_reserve_id_parameter(id: u32) -> ReserveIdParameter { - (id as u128).into() - } - } -} - -parameter_types! { - pub const AssetDeposit: Balance = 0; // 1 UNIT deposit to create asset - pub const ApprovalDeposit: Balance = 0; - pub const AssetsStringLimit: u32 = 50; - /// Key = 32 bytes, Value = 36 bytes (32+1+1+1+1) - // https://github.com/paritytech/substrate/blob/069917b/frame/assets/src/lib.rs#L257L271 - pub const MetadataDepositBase: Balance = 0; - pub const MetadataDepositPerByte: Balance = 0; - pub const ExecutiveBody: BodyId = BodyId::Executive; - pub const AssetAccountDeposit: Balance = 0; -} - -impl pallet_assets::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Balance = Balance; - type AssetId = AssetId; - type Currency = Balances; - type ForceOrigin = EnsureRoot; - type AssetDeposit = AssetDeposit; - type MetadataDepositBase = MetadataDepositBase; - type MetadataDepositPerByte = MetadataDepositPerByte; - type ApprovalDeposit = ApprovalDeposit; - type StringLimit = AssetsStringLimit; - type Freezer = (); - type Extra = (); - type AssetAccountDeposit = AssetAccountDeposit; - type WeightInfo = (); - type RemoveItemsLimit = ConstU32<656>; - type AssetIdParameter = AssetId; - type ReserveData = ReserveId; - type CreateOrigin = AsEnsureOriginWithArg>; - type CallbackHandle = (); - type Holder = (); - pallet_assets::runtime_benchmarks_enabled! { - type BenchmarkHelper = BenchmarkHelper; - } -} - -parameter_types! { - pub const KsmLocation: Location = Location::parent(); - pub const RelayNetwork: NetworkId = NetworkId::Kusama; - pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); - pub UniversalLocation: InteriorLocation = - [GlobalConsensus(RelayNetwork::get()), Parachain(MsgQueue::parachain_id().into())].into(); - pub Local: Location = Here.into(); - pub CheckingAccount: AccountId = PolkadotXcm::check_account(); - pub KsmPerSecond: (xcm::latest::prelude::AssetId, u128, u128) = - (AssetId(KsmLocation::get()), 1, 1); -} - -/// Type for specifying how a `Location` can be converted into an `AccountId`. This is used -/// when determining ownership of accounts for asset transacting and when attempting to use XCM -/// `Transact` in order to determine the dispatch Origin. -pub type LocationToAccountId = ( - // The parent (Relay-chain) origin converts to the default `AccountId`. - ParentIsPreset, - // Sibling parachain origins convert to AccountId via the `ParaId::into`. - SiblingParachainConvertsVia, - // Straight up local `AccountId32` origins just alias directly to `AccountId`. - AccountId32Aliases, -); - -/// Means for transacting the native currency on this chain. -pub type CurrencyTransactor = FungibleAdapter< - // Use this currency: - Balances, - // Use this currency when it is a fungible asset matching the given location or name: - IsConcrete, - // Convert an XCM Location into a local account id: - LocationToAccountId, - // Our chain's account ID type (we can't get away without mentioning it explicitly): - AccountId, - // We don't track any teleports of `Balances`. - (), ->; - -/// Means for transacting assets besides the native currency on this chain. -pub type FungiblesTransactor = FungiblesAdapter< - // Use this fungibles implementation: - Assets, - // Use this currency when it is a fungible asset matching the given location or name: - ConvertedConcreteId< - AssetId, - Balance, - AsPrefixedGeneralIndex, - JustTry, - >, - // Convert an XCM Location into a local account id: - LocationToAccountId, - // Our chain's account ID type (we can't get away without mentioning it explicitly): - AccountId, - // We only want to allow teleports of known assets. We use non-zero issuance as an indication - // that this asset is known. - NoChecking, - // The account to use for tracking teleports. - CheckingAccount, ->; -/// Means for transacting assets on this chain. -pub type AssetTransactors = (CurrencyTransactor, FungiblesTransactor); - -/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, -/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can -/// biases the kind of local `Origin` it will become. -pub type XcmOriginToTransactDispatchOrigin = ( - // Sovereign account converter; this attempts to derive an `AccountId` from the origin location - // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for - // foreign chains who want to have a local sovereign account on this chain which they control. - SovereignSignedViaLocation, - // Native converter for Relay-chain (Parent) location; will convert to a `Relay` origin when - // recognised. - RelayChainAsNative, - // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when - // recognised. - SiblingParachainAsNative, - // Superuser converter for the Relay-chain (Parent) location. This will allow it to issue a - // transaction from the Root origin. - ParentAsSuperuser, - // Native signed account converter; this just converts an `AccountId32` origin into a normal - // `RuntimeOrigin::signed` origin of the same 32-byte value. - SignedAccountId32AsNative, - // Xcm origins can be represented natively under the Xcm pallet's Xcm origin. - pallet_xcm::XcmPassthrough, -); - -parameter_types! { - // One XCM operation is 1_000_000_000 weight - almost certainly a conservative estimate. - pub UnitWeightCost: Weight = Weight::from_parts(100u64, 100u64); - pub const MaxInstructions: u32 = 100; -} - -pub struct ParentOrParentsExecutivePlurality; -impl Contains for ParentOrParentsExecutivePlurality { - fn contains(location: &Location) -> bool { - matches!( - location.unpack(), - (1, []) - | ( - 1, - [Plurality { - id: BodyId::Executive, - .. - }] - ) - ) - } -} - -pub struct ParentOrSiblings; -impl Contains for ParentOrSiblings { - fn contains(location: &Location) -> bool { - matches!(location.unpack(), (1, []) | (1, [_])) - } -} - -pub type Barrier = ( - TakeWeightCredit, - AllowTopLevelPaidExecutionFrom, - // Parent and its exec plurality get free execution - AllowUnpaidExecutionFrom, - // Expected responses are OK. - AllowKnownQueryResponses, - // Subscriptions for version tracking are OK. - AllowSubscriptionsFrom, -); - -parameter_types! { - pub MatcherLocation: Location = Location::here(); - pub const MaxAssetsIntoHolding: u32 = 64; - pub const RelayTokenLocation: Location = Location::parent(); -} - -// Copied from: -// -// https://github.com/paritytech/polkadot-sdk/blob/f4eb41773611008040c9d4d8a8e6b7323eccfca1/cumulus -// /parachains/common/src/xcm_config.rs#L118 -// -// The difference with the original "ConcreteAssetFromSystem" (which is used by AssetHub), -// is that in our tests we only need to check if the asset matches the relay one. -pub struct ConcreteAssetFromRelay(sp_std::marker::PhantomData); -impl> ContainsPair - for ConcreteAssetFromRelay -{ - fn contains(asset: &Asset, origin: &Location) -> bool { - let is_relay = match origin.unpack() { - // The Relay Chain - (1, []) => true, - // Others - _ => false, - }; - asset.id.0 == AssetLocation::get() && is_relay - } -} - -pub type TrustedTeleporters = (ConcreteAssetFromRelay,); - -pub struct XcmConfig; -impl Config for XcmConfig { - type RuntimeCall = RuntimeCall; - type XcmSender = XcmRouter; - type AssetTransactor = AssetTransactors; - type OriginConverter = XcmOriginToTransactDispatchOrigin; - type IsReserve = xcm_primitives::MultiNativeAsset; - type IsTeleporter = TrustedTeleporters; - type UniversalLocation = UniversalLocation; - type Barrier = Barrier; - type Weigher = FixedWeightBounds; - type Trader = FixedRateOfFungible; - type ResponseHandler = PolkadotXcm; - type AssetTrap = PolkadotXcm; - type AssetClaims = PolkadotXcm; - type SubscriptionService = PolkadotXcm; - type CallDispatcher = RuntimeCall; - type AssetLocker = (); - type AssetExchanger = (); - type PalletInstancesInfo = (); - type MaxAssetsIntoHolding = MaxAssetsIntoHolding; - type FeeManager = (); - type MessageExporter = (); - type UniversalAliases = Nothing; - type SafeCallFilter = Everything; - type Aliasers = Nothing; - type TransactionalProcessor = (); - type HrmpNewChannelOpenRequestHandler = (); - type HrmpChannelAcceptedHandler = (); - type HrmpChannelClosingHandler = (); - type XcmRecorder = PolkadotXcm; - type XcmEventEmitter = PolkadotXcm; -} - -/// No local origins on this chain are allowed to dispatch XCM sends/executions. -pub type LocalOriginToLocation = SignedToAccountId32; - -pub type XcmRouter = super::ParachainXcmRouter; - -impl pallet_xcm::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type SendXcmOrigin = EnsureXcmOrigin; - type XcmRouter = XcmRouter; - type ExecuteXcmOrigin = EnsureXcmOrigin; - type XcmExecuteFilter = Nothing; - type XcmExecutor = XcmExecutor; - type XcmTeleportFilter = Everything; - type XcmReserveTransferFilter = Everything; - type Weigher = FixedWeightBounds; - type UniversalLocation = UniversalLocation; - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; - type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; - type Currency = Balances; - type CurrencyMatcher = IsConcrete; - type TrustedLockers = (); - type SovereignAccountOf = (); - type MaxLockers = ConstU32<8>; - type WeightInfo = pallet_xcm::TestWeightInfo; - type MaxRemoteLockConsumers = ConstU32<0>; - type RemoteLockConsumerIdentifier = (); - type AdminOrigin = frame_system::EnsureRoot; - type AuthorizedAliasConsideration = Disabled; -} - -impl cumulus_pallet_xcm::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type XcmExecutor = XcmExecutor; -} - -#[frame_support::pallet] -pub mod mock_msg_queue { - use super::*; - use frame_support::pallet_prelude::*; - - #[pallet::config] - pub trait Config: frame_system::Config { - type XcmExecutor: ExecuteXcm; - } - - #[pallet::call] - impl Pallet {} - - #[pallet::pallet] - pub struct Pallet(_); - - #[pallet::storage] - #[pallet::getter(fn parachain_id)] - pub(super) type ParachainId = StorageValue<_, ParaId, 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, InstructionError), - /// 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 = Location::new(1, [Parachain(sender.into())]); - let mut id = [0u8; 32]; - id.copy_from_slice(hash.as_ref()); - match T::XcmExecutor::prepare_and_execute( - location, - xcm, - &mut id, - max_weight, - Weight::zero(), - ) { - Outcome::Error(error) => { - (Err(error.clone()), Event::Fail(Some(hash), error)) - } - Outcome::Complete { used } => (Ok(used), 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 { used, error } => { - (Ok(used), Event::Fail(Some(hash), error)) - } - } - } - Err(()) => ( - Err(InstructionError { - error: XcmError::UnhandledXcmVersion, - index: 0, - }), - 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 mut 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::prepare_and_execute( - Parent, - x, - &mut id, - limit, - Weight::zero(), - ); - - Self::deposit_event(Event::ExecutedDownward(id, outcome)); - } - } - } - limit - } - } -} -impl mock_msg_queue::Config for Runtime { - type XcmExecutor = XcmExecutor; -} - -// Pallet to cover test cases for change https://github.com/paritytech/cumulus/pull/831 -#[frame_support::pallet] -pub mod mock_statemine_prefix { - use super::*; - use frame_support::pallet_prelude::*; - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::call] - impl Pallet {} - - #[pallet::pallet] - #[pallet::without_storage_info] - pub struct Pallet(_); - - #[pallet::storage] - #[pallet::getter(fn current_prefix)] - pub(super) type CurrentPrefix = StorageValue<_, Location, ValueQuery>; - - impl Get for Pallet { - fn get() -> Location { - Self::current_prefix() - } - } - - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event { - // Changed Prefix - PrefixChanged(Location), - } - - impl Pallet { - pub fn set_prefix(prefix: Location) { - CurrentPrefix::::put(&prefix); - Self::deposit_event(Event::PrefixChanged(prefix)); - } - } -} - -impl mock_statemine_prefix::Config for Runtime {} - -type Block = frame_system::mocking::MockBlockU32; -construct_runtime!( - pub enum Runtime { - System: frame_system, - Balances: pallet_balances, - PolkadotXcm: pallet_xcm, - CumulusXcm: cumulus_pallet_xcm, - MsgQueue: mock_msg_queue, - Assets: pallet_assets, - PrefixChanger: mock_statemine_prefix, - - } -); diff --git a/runtime/moonriver/tests/xcm_tests.rs b/runtime/moonriver/tests/xcm_tests.rs deleted file mode 100644 index a49361e7fa5..00000000000 --- a/runtime/moonriver/tests/xcm_tests.rs +++ /dev/null @@ -1,5564 +0,0 @@ -// Copyright 2019-2025 PureStake Inc. -// This file is part of Moonbeam. - -// Moonbeam 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. - -// Moonbeam 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 Moonbeam. If not, see . - -//! Moonriver Runtime Xcm Tests - -mod xcm_mock; -use frame_support::{ - assert_ok, - traits::{PalletInfo, PalletInfoAccess}, - weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight}, - BoundedVec, -}; -use moonriver_runtime::xcm_config::AssetType; -use sp_core::ConstU32; -use sp_core::U256; -use sp_runtime::traits::Convert; -use xcm::{ - latest::prelude::{ - AccountId32, AccountKey20, All, Asset, AssetId, Assets as XcmAssets, BuyExecution, - ClearOrigin, DepositAsset, Fungibility, GeneralIndex, Junction, Junctions, Limited, - Location, OriginKind, PalletInstance, Parachain, QueryResponse, Reanchorable, Response, - WeightLimit, Wild, WithdrawAsset, Xcm, - }, - IntoVersion, VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm, WrapVersion, -}; -use xcm_executor::traits::{ConvertLocation, TransferType}; -use xcm_mock::parachain::{self, EvmForeignAssets, PolkadotXcm, Treasury}; -use xcm_mock::relay_chain; -use xcm_mock::*; -use xcm_simulator::TestExt; -mod common; -use cumulus_primitives_core::relay_chain::HrmpChannelId; -use pallet_xcm_transactor::{ - Currency, CurrencyPayment, HrmpInitParams, HrmpOperation, TransactWeights, -}; -use xcm_primitives::{ - split_location_into_chain_part_and_beneficiary, UtilityEncodeCall, DEFAULT_PROOF_SIZE, -}; - -fn add_supported_asset(asset_type: parachain::AssetType, units_per_second: u128) -> Result<(), ()> { - let parachain::AssetType::Xcm(location_v3) = asset_type; - let VersionedLocation::V5(location_v5) = VersionedLocation::V3(location_v3) - .into_version(xcm::latest::VERSION) - .map_err(|_| ())? - else { - return Err(()); - }; - use frame_support::weights::WeightToFee as _; - let native_amount_per_second: u128 = - ::WeightToFee::weight_to_fee( - &Weight::from_parts( - frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND, - 0, - ), - ) - .try_into() - .map_err(|_| ())?; - let precision_factor = 10u128.pow(pallet_xcm_weight_trader::RELATIVE_PRICE_DECIMALS); - let relative_price: u128 = if units_per_second > 0u128 { - native_amount_per_second - .saturating_mul(precision_factor) - .saturating_div(units_per_second) - } else { - 0u128 - }; - pallet_xcm_weight_trader::SupportedAssets::::insert( - location_v5, - (true, relative_price), - ); - Ok(()) -} - -/// Helper function to set fee per second for an asset location (for compatibility with old tests). -/// Converts fee_per_second to relative_price and adds/edits the asset in the weight-trader. -fn set_fee_per_second_for_location(location: Location, fee_per_second: u128) -> Result<(), ()> { - use moonbeam_tests_primitives::MemoryFeeTrader; - use xcm_primitives::XcmFeeTrader; - - // Configure fees for XcmTransactor via the in-memory fee trader only, so that - // the initial funding XCM transfers stay free and only transactor calls pay fees. - let precision_factor = 10u128.pow(moonbeam_tests_primitives::RELATIVE_PRICE_DECIMALS); - let native_amount_per_second = - frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND as u128; - let relative_price = native_amount_per_second - .saturating_mul(precision_factor) - .checked_div(fee_per_second) - .ok_or(())?; - - ::set_asset_price(location, relative_price).map_err(|_| ()) -} - -fn currency_to_asset(currency_id: parachain::CurrencyId, amount: u128) -> Asset { - Asset { - id: AssetId( - ::CurrencyIdToLocation::convert( - currency_id, - ) - .unwrap(), - ), - fun: Fungibility::Fungible(amount), - } -} - -// Send a relay asset (like DOT) to a parachain A -#[test] -fn receive_relay_asset_from_relay() { - MockNet::reset(); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - // Register relay asset in parachain A - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 0u128)); - }); - - // Actually send relay asset to parachain - let dest: Location = AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - Relay::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([] /* Here */, 123).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - // Verify that parachain received the asset - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(123)) - ); - }); -} - -// Send relay asset (like DOT) back from Parachain A to relaychain -#[test] -fn send_relay_asset_to_relay() { - MockNet::reset(); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - // Register relay asset in paraA - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - // free execution - assert_ok!(add_supported_asset(source_location.clone(), 0u128)); - }); - - let dest: Location = Junction::AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - - // First send relay chain asset to Parachain like in previous test - Relay::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([] /* Here */, 123).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - ParaA::execute_with(|| { - // Free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(123)) - ); - }); - - // Lets gather the balance before sending back money - let mut balance_before_sending = 0; - Relay::execute_with(|| { - balance_before_sending = RelayBalances::free_balance(&RELAYALICE); - }); - - // We now send back some money to the relay - let dest_chain = Location::parent(); - let beneficiary = Location { - parents: 0, - interior: [AccountId32 { - network: None, - id: RELAYALICE.into(), - }] - .into(), - }; - - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_id), 123); - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: beneficiary.clone(), - }]); - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(dest_chain)), - Box::new(VersionedAssets::from(vec![asset])), - Box::new(TransferType::DestinationReserve), - Box::new(fees_id), - Box::new(TransferType::DestinationReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) - )); - }); - - // The balances in paraAlice should have been substracted - ParaA::execute_with(|| { - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(0)) - ); - }); - - // Balances in the relay should have been received - Relay::execute_with(|| { - assert!(RelayBalances::free_balance(&RELAYALICE) > balance_before_sending); - }); -} - -#[test] -fn send_relay_asset_to_para_b() { - MockNet::reset(); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - // Register asset in paraA. Free execution - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.clone().try_into().expect("too long"), - asset_metadata.name.clone().try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 0u128)); - }); - - // Register asset in paraB. Free execution - ParaB::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.clone().try_into().expect("too long"), - asset_metadata.name.clone().try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 0u128)); - }); - - // First send relay chain asset to Parachain A like in previous test - let dest: Location = Junction::AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - Relay::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([] /* Here */, 123).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - ParaA::execute_with(|| { - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(123)) - ); - }); - - // Now send relay asset from para A to para B - let dest = Location { - parents: 1, - interior: [ - Parachain(2), - AccountKey20 { - network: None, - key: PARAALICE.into(), - }, - ] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_id), 100); - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: beneficiary.clone(), - }]); - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from(vec![asset])), - Box::new(TransferType::RemoteReserve(Location::parent().into())), - Box::new(fees_id), - Box::new(TransferType::RemoteReserve(Location::parent().into())), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) - )); - }); - - // Para A balances should have been substracted - ParaA::execute_with(|| { - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(23)) - ); - }); - - // Para B balances should have been credited - ParaB::execute_with(|| { - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(100)) - ); - }); -} - -#[test] -fn send_para_a_asset_to_para_b() { - MockNet::reset(); - - // This represents the asset in paraA - let para_a_balances = Location::new(1, [Parachain(1), PalletInstance(1u8)]); - let source_location: AssetType = para_a_balances - .try_into() - .expect("Location convertion to AssetType should succeed"); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"ParaAToken".to_vec(), - symbol: b"ParaA".to_vec(), - decimals: 18, - }; - - // Register asset in paraB. Free execution - ParaB::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 0u128)); - }); - - // Send para A asset from para A to para B - let dest = Location { - parents: 1, - interior: [ - Parachain(2), - AccountKey20 { - network: None, - key: PARAALICE.into(), - }, - ] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::SelfReserve, 100); - assert_ok!(PolkadotXcm::transfer_assets( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedLocation::from(beneficiary)), - Box::new(VersionedAssets::from(vec![asset])), - 0, - WeightLimit::Limited(Weight::from_parts(800000u64, DEFAULT_PROOF_SIZE)) - )); - }); - - // Native token is substracted in paraA - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - ParaBalances::free_balance(&PARAALICE.into()), - INITIAL_BALANCE - 100 - ); - }); - - // Asset is minted in paraB - ParaB::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(100)) - ); - }); -} - -#[test] -fn send_para_a_asset_from_para_b_to_para_c() { - MockNet::reset(); - - // Represents para A asset - let para_a_balances = Location::new(1, [Parachain(1), PalletInstance(1u8)]); - let source_location: AssetType = para_a_balances - .try_into() - .expect("Location convertion to AssetType should succeed"); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"ParaAToken".to_vec(), - symbol: b"ParaA".to_vec(), - decimals: 18, - }; - - // Register para A asset in parachain B. Free execution - ParaB::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.clone().try_into().expect("too long"), - asset_metadata.name.clone().try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 0u128)); - }); - - // Register para A asset in parachain C. Free execution - ParaC::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.clone().try_into().expect("too long"), - asset_metadata.name.clone().try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 0u128)); - }); - - // Send para A asset to para B - let dest = Location { - parents: 1, - interior: [ - Parachain(2), - AccountKey20 { - network: None, - key: PARAALICE.into(), - }, - ] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::SelfReserve, 100); - assert_ok!(PolkadotXcm::transfer_assets( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedLocation::from(beneficiary)), - Box::new(VersionedAssets::from(vec![asset])), - 0, - WeightLimit::Limited(Weight::from_parts(80u64, DEFAULT_PROOF_SIZE)) - )); - }); - - // Para A balances have been substracted - ParaA::execute_with(|| { - assert_eq!( - ParaBalances::free_balance(&PARAALICE.into()), - INITIAL_BALANCE - 100 - ); - }); - - // Para B balances have been credited - ParaB::execute_with(|| { - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(100)) - ); - }); - - // Send para A asset from para B to para C - let dest = Location { - parents: 1, - interior: [ - Parachain(3), - AccountKey20 { - network: None, - key: PARAALICE.into(), - }, - ] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - ParaB::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_id), 100); - assert_ok!(PolkadotXcm::transfer_assets( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedLocation::from(beneficiary)), - Box::new(VersionedAssets::from(vec![asset])), - 0, - WeightLimit::Limited(Weight::from_parts(80u64, DEFAULT_PROOF_SIZE)) - )); - }); - - // The message passed through parachainA so we needed to pay since its the native token - ParaC::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(95)) - ); - }); -} - -#[test] -fn send_para_a_asset_to_para_b_and_back_to_para_a() { - MockNet::reset(); - - // Para A asset - let para_a_balances = Location::new(1, [Parachain(1), PalletInstance(1u8)]); - let source_location: AssetType = para_a_balances - .try_into() - .expect("Location convertion to AssetType should succeed"); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"ParaAToken".to_vec(), - symbol: b"ParaA".to_vec(), - decimals: 18, - }; - - // Register para A asset in para B - ParaB::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 0u128)); - }); - - // Send para A asset to para B - let dest = Location { - parents: 1, - interior: [ - Parachain(2), - AccountKey20 { - network: None, - key: PARAALICE.into(), - }, - ] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::SelfReserve, 100); - assert_ok!(PolkadotXcm::transfer_assets( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedLocation::from(beneficiary)), - Box::new(VersionedAssets::from(vec![asset])), - 0, - WeightLimit::Limited(Weight::from_parts(80u64, DEFAULT_PROOF_SIZE)) - )); - }); - - // Balances have been subtracted - ParaA::execute_with(|| { - assert_eq!( - ParaBalances::free_balance(&PARAALICE.into()), - INITIAL_BALANCE - 100 - ); - }); - - // Para B balances have been credited - ParaB::execute_with(|| { - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(100)) - ); - }); - - // Send back para A asset to para A - let dest = Location { - parents: 1, - interior: [ - Parachain(1), - AccountKey20 { - network: None, - key: PARAALICE.into(), - }, - ] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - ParaB::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_id), 100); - assert_ok!(PolkadotXcm::transfer_assets( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedLocation::from(beneficiary)), - Box::new(VersionedAssets::from(vec![asset])), - 0, - WeightLimit::Limited(Weight::from_parts(80u64, DEFAULT_PROOF_SIZE)) - )); - }); - - // Para A asset has been credited - ParaA::execute_with(|| { - // Weight used is 4 - assert_eq!( - ParaBalances::free_balance(&PARAALICE.into()), - INITIAL_BALANCE - 4 - ); - }); -} - -#[test] -fn send_para_a_asset_to_para_b_and_back_to_para_a_with_new_reanchoring() { - MockNet::reset(); - - let para_a_balances = Location::new(1, [Parachain(1), PalletInstance(1u8)]); - let source_location: AssetType = para_a_balances - .try_into() - .expect("Location conversion to AssetType should succeed"); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"ParaAToken".to_vec(), - symbol: b"ParaA".to_vec(), - decimals: 18, - }; - - ParaB::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 0u128)); - }); - - let dest = Location { - parents: 1, - interior: [ - Parachain(2), - AccountKey20 { - network: None, - key: PARAALICE.into(), - }, - ] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::SelfReserve, 100); - assert_ok!(PolkadotXcm::transfer_assets( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedLocation::from(beneficiary)), - Box::new(VersionedAssets::from(vec![asset])), - 0, - WeightLimit::Limited(Weight::from_parts(80u64, DEFAULT_PROOF_SIZE)) - )); - }); - - ParaA::execute_with(|| { - // Free execution, full amount received - assert_eq!( - ParaBalances::free_balance(&PARAALICE.into()), - INITIAL_BALANCE - 100 - ); - }); - - ParaB::execute_with(|| { - // Free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(100)) - ); - }); - - // This time we will force the new reanchoring by manually sending the - // Message through polkadotXCM pallet - - let dest = Location { - parents: 1, - interior: [Parachain(1)].into(), - }; - - let reanchored_para_a_balances = Location::new(0, [PalletInstance(1u8)]); - - let message = xcm::VersionedXcm::<()>::V5(Xcm(vec![ - WithdrawAsset((reanchored_para_a_balances.clone(), 100).into()), - ClearOrigin, - BuyExecution { - fees: (reanchored_para_a_balances, 100).into(), - weight_limit: Limited(80.into()), - }, - DepositAsset { - assets: All.into(), - beneficiary: Location::new( - 0, - [AccountKey20 { - network: None, - key: PARAALICE, - }], - ), - }, - ])); - ParaB::execute_with(|| { - // Send a message to the sovereign account in ParaA to withdraw - // and deposit asset - assert_ok!(ParachainPalletXcm::send( - parachain::RuntimeOrigin::root(), - Box::new(dest.into()), - Box::new(message), - )); - }); - - ParaB::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(100)) - ); - }); - - // This time we will force the new reanchoring by manually sending the - // Message through polkadotXCM pallet - - let dest = Location { - parents: 1, - interior: [Parachain(1)].into(), - }; - - let reanchored_para_a_balances = Location::new(0, [PalletInstance(1u8)]); - - let message = xcm::VersionedXcm::<()>::V5(Xcm(vec![ - WithdrawAsset((reanchored_para_a_balances.clone(), 100).into()), - ClearOrigin, - BuyExecution { - fees: (reanchored_para_a_balances, 100).into(), - weight_limit: Limited(80.into()), - }, - DepositAsset { - assets: All.into(), - beneficiary: Location::new( - 0, - [AccountKey20 { - network: None, - key: PARAALICE, - }], - ), - }, - ])); - ParaB::execute_with(|| { - // Send a message to the sovereign account in ParaA to withdraw - // and deposit asset - assert_ok!(ParachainPalletXcm::send( - parachain::RuntimeOrigin::root(), - Box::new(dest.into()), - Box::new(message), - )); - }); - - ParaA::execute_with(|| { - // Weight used is 4 - assert_eq!( - ParaBalances::free_balance(&PARAALICE.into()), - INITIAL_BALANCE - 4 - ); - }); -} - -#[test] -fn receive_relay_asset_with_trader() { - MockNet::reset(); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - // This time we are gonna put a rather high number of units per second - // we know later we will divide by 1e12 - // Lets put 1e6 as units per second - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset( - source_location.clone(), - 2500000000000u128 - )); - }); - - let dest: Location = Junction::AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - // We are sending 100 tokens from relay. - // Amount spent in fees is Units per second * weight / 1_000_000_000_000 (weight per second) - // weight is 4 since we are executing 4 instructions with a unitweightcost of 1. - // Units per second should be 2_500_000_000_000_000 - // Therefore with no refund, we should receive 10 tokens less - // Native trader fails for this, and we use the asset trader - Relay::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([] /* Here */, 100).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - ParaA::execute_with(|| { - // non-free execution, not full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(90)) - ); - // Fee should have been received by treasury - assert_eq!( - EvmForeignAssets::balance(source_id, Treasury::account_id()), - Ok(U256::from(10)) - ); - }); -} - -#[test] -fn send_para_a_asset_to_para_b_with_trader() { - MockNet::reset(); - - let para_a_balances = Location::new(1, [Parachain(1), PalletInstance(1u8)]); - let source_location: AssetType = para_a_balances - .try_into() - .expect("Location convertion to AssetType should succeed"); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"ParaAToken".to_vec(), - symbol: b"ParaA".to_vec(), - decimals: 18, - }; - - ParaB::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset( - source_location.clone(), - 2500000000000u128 - )); - }); - - let dest = Location { - parents: 1, - interior: [ - Parachain(2), - AccountKey20 { - network: None, - key: PARAALICE.into(), - }, - ] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - // In destination chain, we only need 4 weight - // We put 10 weight, 6 of which should be refunded and 4 of which should go to treasury - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::SelfReserve, 100); - assert_ok!(PolkadotXcm::transfer_assets( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedLocation::from(beneficiary)), - Box::new(VersionedAssets::from(vec![asset])), - 0, - WeightLimit::Limited(Weight::from_parts(10u64, DEFAULT_PROOF_SIZE)) - )); - }); - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - ParaBalances::free_balance(&PARAALICE.into()), - INITIAL_BALANCE - 100 - ); - }); - - // We are sending 100 tokens from para A. - // Amount spent in fees is Units per second * weight / 1_000_000_000_000 (weight per second) - // weight is 4 since we are executing 4 instructions with a unitweightcost of 1. - // Units per second should be 2_500_000_000_000_000 - // Since we set 10 weight in destination chain, 25 will be charged upfront - // 15 of those will be refunded, while 10 will go to treasury as the true weight used - // will be 4 - ParaB::execute_with(|| { - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(90)) - ); - // Fee should have been received by treasury - assert_eq!( - EvmForeignAssets::balance(source_id, Treasury::account_id()), - Ok(U256::from(10)) - ); - }); -} - -#[test] -fn send_para_a_asset_to_para_b_with_trader_and_fee() { - MockNet::reset(); - - let para_a_balances = Location::new(1, [Parachain(1), PalletInstance(1u8)]); - let source_location: AssetType = para_a_balances - .try_into() - .expect("Location convertion to AssetType should succeed"); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"ParaAToken".to_vec(), - symbol: b"ParaA".to_vec(), - decimals: 18, - }; - - ParaB::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - // With these units per second, 80K weight convrets to 1 asset unit - assert_ok!(add_supported_asset(source_location.clone(), 12500000u128)); - }); - - let dest = Location { - parents: 1, - interior: [ - Parachain(2), - AccountKey20 { - network: None, - key: PARAALICE.into(), - }, - ] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - // we use transfer_with_fee - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::SelfReserve, 100); - let asset_fee = currency_to_asset(parachain::CurrencyId::SelfReserve, 1); - assert_ok!(PolkadotXcm::transfer_assets( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedLocation::from(beneficiary)), - Box::new(VersionedAssets::from(vec![asset_fee, asset])), - 0, - WeightLimit::Limited(Weight::from_parts(800000u64, DEFAULT_PROOF_SIZE)) - )); - }); - ParaA::execute_with(|| { - // 100 tokens transferred plus 1 taken from fees - assert_eq!( - ParaBalances::free_balance(&PARAALICE.into()), - INITIAL_BALANCE - 100 - 1 - ); - }); - - ParaB::execute_with(|| { - // free execution, full amount received because trully the xcm instruction does not cost - // what it is specified - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(101)) - ); - }); -} - -#[test] -fn error_when_not_paying_enough() { - MockNet::reset(); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - let dest: Location = Junction::AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - // This time we are gonna put a rather high number of units per second - // we know later we will divide by 1e12 - // Lets put 1e6 as units per second - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset( - source_location.clone(), - 2500000000000u128 - )); - }); - - // We are sending 100 tokens from relay. - // If we set the dest weight to be 1e7, we know the buy_execution will spend 1e7*1e6/1e12 = 10 - // Therefore with no refund, we should receive 10 tokens less - Relay::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([] /* Here */, 5).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - ParaA::execute_with(|| { - // amount not received as it is not paying enough - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(0)) - ); - }); -} - -#[test] -fn transact_through_derivative_multilocation() { - MockNet::reset(); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 1u128)); - - // Root can set transact info - assert_ok!(XcmTransactor::set_transact_info( - parachain::RuntimeOrigin::root(), - Box::new(xcm::VersionedLocation::from(Location::parent())), - // Relay charges 1000 for every instruction, and we have 3, so 3000 - 3000.into(), - 20000000000.into(), - None - )); - // Set fee per second using weight-trader (replaces old set_fee_per_second) - set_fee_per_second_for_location(Location::parent(), WEIGHT_REF_TIME_PER_SECOND as u128) - .expect("must succeed"); - }); - - // Let's construct the call to know how much weight it is going to require - - let dest: Location = AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - Relay::execute_with(|| { - // 4000000000 transact + 3000 correspond to 4000003000 tokens. 100 more for the transfer call - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([] /* Here */, 4000003100u128).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(4000003100u64)) - ); - }); - - // Register address - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::register( - parachain::RuntimeOrigin::root(), - PARAALICE.into(), - 0, - )); - }); - - // Send to registered address - - let registered_address = derivative_account_id(para_a_account(), 0); - let dest = Location { - parents: 1, - interior: [AccountId32 { - network: None, - id: registered_address.clone().into(), - }] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_id), 100); - // free execution, full amount received - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: beneficiary.clone(), - }]); - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from(vec![asset])), - Box::new(TransferType::DestinationReserve), - Box::new(fees_id), - Box::new(TransferType::DestinationReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) - )); - }); - - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(4000003000u64)) - ); - }); - - // What we will do now is transfer this relay tokens from the derived account to the sovereign - // again - Relay::execute_with(|| { - // free execution,x full amount received - assert!(RelayBalances::free_balance(¶_a_account()) == 4000003000); - }); - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = ::PalletInfo::index::< - relay_chain::Balances, - >() - .unwrap() as u8; - - encoded.push(index); - - // Then call bytes - let mut call_bytes = pallet_balances::Call::::transfer_allow_death { - dest: para_a_account(), - value: 100u32.into(), - } - .encode(); - encoded.append(&mut call_bytes); - - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::transact_through_derivative( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - moonriver_runtime::xcm_config::Transactors::Relay, - 0, - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - Location::parent() - ))), - fee_amount: None - }, - encoded, - // 4000000000 + 3000 we should have taken out 4000003000 tokens from the caller - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: None - }, - false - )); - }); - - Relay::execute_with(|| { - // free execution,x full amount received - assert!(RelayBalances::free_balance(¶_a_account()) == 100); - - assert!(RelayBalances::free_balance(®istered_address) == 0); - }); -} - -#[test] -fn transact_through_derivative_with_custom_fee_weight() { - MockNet::reset(); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 1u128)); - }); - - // Let's construct the call to know how much weight it is going to require - - let dest: Location = AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - Relay::execute_with(|| { - // 4000000000 transact + 3000 correspond to 4000003000 tokens. 100 more for the transfer call - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([] /* Here */, 4000003100u128).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(4000003100u64)) - ); - }); - - // Register address - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::register( - parachain::RuntimeOrigin::root(), - PARAALICE.into(), - 0, - )); - }); - - // Send to registered address - - let registered_address = derivative_account_id(para_a_account(), 0); - let dest = Location { - parents: 1, - interior: [AccountId32 { - network: None, - id: registered_address.clone().into(), - }] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_id), 100); - // free execution, full amount received - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: beneficiary.clone(), - }]); - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from(vec![asset])), - Box::new(TransferType::DestinationReserve), - Box::new(fees_id), - Box::new(TransferType::DestinationReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) - )); - }); - - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(4000003000u64)) - ); - }); - - // What we will do now is transfer this relay tokens from the derived account to the sovereign - // again - Relay::execute_with(|| { - // free execution,x full amount received - assert!(RelayBalances::free_balance(¶_a_account()) == 4000003000); - }); - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = ::PalletInfo::index::< - relay_chain::Balances, - >() - .unwrap() as u8; - - encoded.push(index); - - // Then call bytes - let mut call_bytes = pallet_balances::Call::::transfer_allow_death { - dest: para_a_account(), - value: 100u32.into(), - } - .encode(); - encoded.append(&mut call_bytes); - - let overall_weight = 4000003000u64; - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::transact_through_derivative( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - moonriver_runtime::xcm_config::Transactors::Relay, - 0, - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - Location::parent() - ))), - // 1-1 fee weight mapping - fee_amount: Some(overall_weight as u128) - }, - // 4000000000 + 3000 we should have taken out 4000003000 tokens from the caller - encoded, - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: Some(Limited(overall_weight.into())) - }, - false - )); - let event_found: Option = parachain::para_events() - .iter() - .find_map(|event| match event.clone() { - parachain::RuntimeEvent::PolkadotXcm(pallet_xcm::Event::AssetsTrapped { - .. - }) => Some(event.clone()), - _ => None, - }); - // Assert that the events do not contain the assets being trapped - assert!(event_found.is_none()); - }); - - Relay::execute_with(|| { - // free execution,x full amount received - assert!(RelayBalances::free_balance(¶_a_account()) == 100); - - assert!(RelayBalances::free_balance(®istered_address) == 0); - }); -} - -#[test] -fn transact_through_derivative_with_custom_fee_weight_refund() { - MockNet::reset(); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 1u128)); - }); - - // Let's construct the call to know how much weight it is going to require - - let dest: Location = AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - Relay::execute_with(|| { - // 4000000000 transact + 9000 correspond to 4000009000 tokens. 100 more for the transfer call - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([] /* Here */, 4000009100u128).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(4000009100u64)) - ); - }); - - // Register address - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::register( - parachain::RuntimeOrigin::root(), - PARAALICE.into(), - 0, - )); - }); - - // Send to registered address - - let registered_address = derivative_account_id(para_a_account(), 0); - let dest = Location { - parents: 1, - interior: [AccountId32 { - network: None, - id: registered_address.clone().into(), - }] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_id), 100); - // free execution, full amount received - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: beneficiary.clone(), - }]); - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from(vec![asset])), - Box::new(TransferType::DestinationReserve), - Box::new(fees_id), - Box::new(TransferType::DestinationReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) - )); - }); - - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(4000009000u64)) - ); - }); - - // What we will do now is transfer this relay tokens from the derived account to the sovereign - // again - Relay::execute_with(|| { - // free execution,x full amount received - assert!(RelayBalances::free_balance(¶_a_account()) == 4000009000); - }); - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = ::PalletInfo::index::< - relay_chain::Balances, - >() - .unwrap() as u8; - - encoded.push(index); - - // Then call bytes - let mut call_bytes = pallet_balances::Call::::transfer_allow_death { - dest: para_a_account(), - value: 100u32.into(), - } - .encode(); - encoded.append(&mut call_bytes); - - let overall_weight = 4000009000u64; - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::transact_through_derivative( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - moonriver_runtime::xcm_config::Transactors::Relay, - 0, - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - Location::parent() - ))), - // 1-1 fee weight mapping - fee_amount: Some(overall_weight as u128) - }, - encoded, - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: Some(Limited(overall_weight.into())) - }, - true - )); - let event_found: Option = parachain::para_events() - .iter() - .find_map(|event| match event.clone() { - parachain::RuntimeEvent::PolkadotXcm(pallet_xcm::Event::AssetsTrapped { - .. - }) => Some(event.clone()), - _ => None, - }); - // Assert that the events do not contain the assets being trapped - assert!(event_found.is_none()); - }); - - Relay::execute_with(|| { - // free execution,x full amount received - // 4000009000 refunded + 100 transferred = 4000009100 - assert_eq!(RelayBalances::free_balance(¶_a_account()), 4000009100); - assert_eq!(RelayBalances::free_balance(®istered_address), 0); - }); -} - -#[test] -fn transact_through_sovereign() { - MockNet::reset(); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 1u128)); - - // Root can set transact info - assert_ok!(XcmTransactor::set_transact_info( - parachain::RuntimeOrigin::root(), - Box::new(xcm::VersionedLocation::from(Location::parent())), - // Relay charges 1000 for every instruction, and we have 3, so 3000 - 3000.into(), - 20000000000.into(), - None - )); - // Set fee per second using weight-trader (replaces old set_fee_per_second) - set_fee_per_second_for_location(Location::parent(), WEIGHT_REF_TIME_PER_SECOND as u128) - .expect("must succeed"); - }); - - let dest: Location = AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - Relay::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([] /* Here */, 4000003100u128).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(4000003100u64)) - ); - }); - - // Register address - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::register( - parachain::RuntimeOrigin::root(), - PARAALICE.into(), - 0, - )); - }); - - // Send to registered address - let registered_address = derivative_account_id(para_a_account(), 0); - let dest = Location { - parents: 1, - interior: [AccountId32 { - network: None, - id: registered_address.clone().into(), - }] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_id), 100); - // free execution, full amount received - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: beneficiary.clone(), - }]); - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from(vec![asset])), - Box::new(TransferType::DestinationReserve), - Box::new(fees_id), - Box::new(TransferType::DestinationReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) - )); - }); - - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(4000003000u64)) - ); - }); - - // What we will do now is transfer this relay tokens from the derived account to the sovereign - // again - Relay::execute_with(|| { - // free execution,x full amount received - assert!(RelayBalances::free_balance(¶_a_account()) == 4000003000); - 0 - }); - - // We send the xcm transact operation to parent - let dest = Location { - parents: 1, - interior: /* Here */ [].into(), - }; - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = ::PalletInfo::index::< - relay_chain::Balances, - >() - .unwrap() as u8; - - encoded.push(index); - - // Then call bytes - let mut call_bytes = pallet_balances::Call::::transfer_allow_death { - dest: para_a_account(), - value: 100u32.into(), - } - .encode(); - encoded.append(&mut call_bytes); - - // Root can directly pass the execution byes to the sovereign - ParaA::execute_with(|| { - let utility_bytes = ::encode_call( - moonriver_runtime::xcm_config::Transactors::Relay, - xcm_primitives::UtilityAvailableCalls::AsDerivative(0, encoded), - ); - - assert_ok!(XcmTransactor::transact_through_sovereign( - parachain::RuntimeOrigin::root(), - Box::new(xcm::VersionedLocation::from(dest)), - Some(PARAALICE.into()), - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - Location::parent() - ))), - fee_amount: None - }, - utility_bytes, - OriginKind::SovereignAccount, - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: None - }, - false - )); - }); - - Relay::execute_with(|| { - // free execution,x full amount received - assert!(RelayBalances::free_balance(¶_a_account()) == 100); - - assert!(RelayBalances::free_balance(®istered_address) == 0); - }); -} - -#[test] -fn transact_through_sovereign_fee_payer_none() { - MockNet::reset(); - - ParaA::execute_with(|| { - // Root can set transact info - assert_ok!(XcmTransactor::set_transact_info( - parachain::RuntimeOrigin::root(), - Box::new(xcm::VersionedLocation::from(Location::parent())), - // Relay charges 1000 for every instruction, and we have 3, so 3000 - 3000.into(), - 20000000000.into(), - None - )); - // Set fee per second using weight-trader (replaces old set_fee_per_second) - set_fee_per_second_for_location(Location::parent(), WEIGHT_REF_TIME_PER_SECOND as u128) - .expect("must succeed"); - }); - - let derivative_address = derivative_account_id(para_a_account(), 0); - - Relay::execute_with(|| { - // Transfer 100 tokens to derivative_address on the relay - assert_ok!(RelayBalances::transfer_keep_alive( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - derivative_address.clone(), - 100u128 - )); - - // Transfer the XCM execution fee amount to ParaA's sovereign account - assert_ok!(RelayBalances::transfer_keep_alive( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - para_a_account(), - 4000003000u128 - )); - }); - - // Check balances before the transact call - Relay::execute_with(|| { - assert_eq!(RelayBalances::free_balance(¶_a_account()), 4000003000); - assert_eq!(RelayBalances::free_balance(&derivative_address), 100); - assert_eq!(RelayBalances::free_balance(&RELAYBOB), 0); - }); - - // Encode the call. Balances transfer of 100 relay tokens to RELAYBOB - let mut encoded: Vec = Vec::new(); - let index = ::PalletInfo::index::< - relay_chain::Balances, - >() - .unwrap() as u8; - - encoded.push(index); - - let mut call_bytes = pallet_balances::Call::::transfer_allow_death { - dest: RELAYBOB, - value: 100u32.into(), - } - .encode(); - encoded.append(&mut call_bytes); - - // We send the xcm transact operation to parent - let dest = Location { - parents: 1, - interior: /* Here */ [].into(), - }; - - // Root can directly pass the execution byes to the sovereign - ParaA::execute_with(|| { - // The final call will be an AsDerivative using index 0 - let utility_bytes = ::encode_call( - moonriver_runtime::xcm_config::Transactors::Relay, - xcm_primitives::UtilityAvailableCalls::AsDerivative(0, encoded), - ); - - assert_ok!(XcmTransactor::transact_through_sovereign( - parachain::RuntimeOrigin::root(), - Box::new(xcm::VersionedLocation::from(dest)), - // No fee_payer here. The sovereign account will pay the fees on destination. - None, - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - Location::parent() - ))), - fee_amount: None - }, - utility_bytes, - OriginKind::SovereignAccount, - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: None - }, - false - )); - }); - - // Check balances after the transact call are correct - Relay::execute_with(|| { - assert_eq!(RelayBalances::free_balance(¶_a_account()), 0); - assert_eq!(RelayBalances::free_balance(&derivative_address), 0); - assert_eq!(RelayBalances::free_balance(&RELAYBOB), 100); - }); -} - -#[test] -fn transact_through_sovereign_with_custom_fee_weight() { - MockNet::reset(); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 1u128)); - }); - - let dest: Location = AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - Relay::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([] /* Here */, 4000003100u128).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(4000003100u64)) - ); - }); - - // Register address - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::register( - parachain::RuntimeOrigin::root(), - PARAALICE.into(), - 0, - )); - }); - - // Send to registered address - let registered_address = derivative_account_id(para_a_account(), 0); - let dest = Location { - parents: 1, - interior: [AccountId32 { - network: None, - id: registered_address.clone().into(), - }] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_id), 100); - // free execution, full amount received - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: beneficiary.clone(), - }]); - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from(vec![asset])), - Box::new(TransferType::DestinationReserve), - Box::new(fees_id), - Box::new(TransferType::DestinationReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) - )); - }); - - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(4000003000u64)) - ); - }); - - // What we will do now is transfer this relay tokens from the derived account to the sovereign - // again - Relay::execute_with(|| { - // free execution,x full amount received - assert!(RelayBalances::free_balance(¶_a_account()) == 4000003000); - 0 - }); - - // We send the xcm transact operation to parent - let dest = Location { - parents: 1, - interior: /* Here */ [].into(), - }; - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = ::PalletInfo::index::< - relay_chain::Balances, - >() - .unwrap() as u8; - - encoded.push(index); - - // Then call bytes - let mut call_bytes = pallet_balances::Call::::transfer_allow_death { - dest: para_a_account(), - value: 100u32.into(), - } - .encode(); - encoded.append(&mut call_bytes); - - let total_weight = 4000003000u64; - // Root can directly pass the execution byes to the sovereign - ParaA::execute_with(|| { - let utility_bytes = ::encode_call( - moonriver_runtime::xcm_config::Transactors::Relay, - xcm_primitives::UtilityAvailableCalls::AsDerivative(0, encoded), - ); - - assert_ok!(XcmTransactor::transact_through_sovereign( - parachain::RuntimeOrigin::root(), - Box::new(xcm::VersionedLocation::from(dest)), - Some(PARAALICE.into()), - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - Location::parent() - ))), - // 1-1 fee-weight mapping - fee_amount: Some(total_weight as u128) - }, - utility_bytes, - OriginKind::SovereignAccount, - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: Some(Limited(total_weight.into())) - }, - false - )); - }); - - Relay::execute_with(|| { - // free execution,x full amount received - assert!(RelayBalances::free_balance(¶_a_account()) == 100); - - assert!(RelayBalances::free_balance(®istered_address) == 0); - }); -} - -#[test] -fn transact_through_sovereign_with_custom_fee_weight_refund() { - MockNet::reset(); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 1u128)); - }); - - let dest: Location = AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - Relay::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([] /* Here */, 4000009100u128).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(4000009100u64)) - ); - }); - - // Register address - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::register( - parachain::RuntimeOrigin::root(), - PARAALICE.into(), - 0, - )); - }); - - // Send to registered address - let registered_address = derivative_account_id(para_a_account(), 0); - let dest = Location { - parents: 1, - interior: [AccountId32 { - network: None, - id: registered_address.clone().into(), - }] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_id), 100); - // free execution, full amount received - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: beneficiary.clone(), - }]); - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from(vec![asset])), - Box::new(TransferType::DestinationReserve), - Box::new(fees_id), - Box::new(TransferType::DestinationReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) - )); - }); - - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(4000009000u64)) - ); - }); - - // What we will do now is transfer this relay tokens from the derived account to the sovereign - // again - Relay::execute_with(|| { - // free execution,x full amount received - assert!(RelayBalances::free_balance(¶_a_account()) == 4000009000); - 0 - }); - - // We send the xcm transact operation to parent - let dest = Location { - parents: 1, - interior: /* Here */ [].into(), - }; - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = ::PalletInfo::index::< - relay_chain::Balances, - >() - .unwrap() as u8; - - encoded.push(index); - - // Then call bytes - let mut call_bytes = pallet_balances::Call::::transfer_allow_death { - dest: para_a_account(), - value: 100u32.into(), - } - .encode(); - encoded.append(&mut call_bytes); - - let total_weight = 4000009000u64; - // Root can directly pass the execution byes to the sovereign - ParaA::execute_with(|| { - let utility_bytes = ::encode_call( - moonriver_runtime::xcm_config::Transactors::Relay, - xcm_primitives::UtilityAvailableCalls::AsDerivative(0, encoded), - ); - - assert_ok!(XcmTransactor::transact_through_sovereign( - parachain::RuntimeOrigin::root(), - Box::new(xcm::VersionedLocation::from(dest)), - Some(PARAALICE.into()), - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - Location::parent() - ))), - // 1-1 fee-weight mapping - fee_amount: Some(total_weight as u128) - }, - utility_bytes, - OriginKind::SovereignAccount, - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: Some(Limited(total_weight.into())) - }, - true - )); - }); - - Relay::execute_with(|| { - // free execution, full amount received - // 4000009000 refunded + 100 transferred = 4000009100 - assert_eq!(RelayBalances::free_balance(¶_a_account()), 4000009100); - - assert_eq!(RelayBalances::free_balance(®istered_address), 0); - }); -} - -#[test] -fn test_automatic_versioning_on_runtime_upgrade_with_relay() { - MockNet::reset(); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - // register relay asset in parachain A and set XCM version to 1 - ParaA::execute_with(|| { - parachain::XcmVersioner::set_version(1); - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 0u128)); - }); - - let response = Response::Version(2); - let querier: Location = ([]/* Here */).into(); - - // 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: Weight::zero(), - querier: Some(querier), - }]); - // The router is mocked, and we cannot use WrapVersion in ChildParachainRouter. So we will force - // it directly here - // Actually send relay asset to parachain - let dest: Location = AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - - Relay::execute_with(|| { - // This sets the default version, for not known destinations - assert_ok!(RelayChainPalletXcm::force_default_xcm_version( - relay_chain::RuntimeOrigin::root(), - Some(3) - )); - - // Wrap version, which sets VersionedStorage - // This is necessary because the mock router does not use wrap_version, but - // this is not necessary in prod - assert_ok!(::wrap_version( - &Parachain(1).into(), - mock_message - )); - - // Transfer assets. Since it is an unknown destination, it will query for version - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([] /* Here */, 123).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - - // 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::RuntimeEvent = - pallet_xcm::Event::SupportedVersionChanged { - location: Location { - parents: 0, - interior: [Parachain(1)].into(), - }, - version: 1, - } - .into(); - - Relay::execute_with(|| { - // Assert that the events vector contains the version change - assert!(relay_chain::relay_events().contains(&expected_supported_version)); - }); - - // 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().iter().any(|e| matches!( - e, - parachain::RuntimeEvent::PolkadotXcm(pallet_xcm::Event::VersionChangeNotified { - result: 2, - .. - }) - ))); - }); - - // This event should have been seen in the relay - let expected_supported_version_2: relay_chain::RuntimeEvent = - pallet_xcm::Event::SupportedVersionChanged { - location: Location { - parents: 0, - interior: [Parachain(1)].into(), - }, - version: 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(); - - let para_a_balances = Location::new(1, [Parachain(1), PalletInstance(1u8)]); - let source_location: AssetType = para_a_balances - .try_into() - .expect("Location convertion to AssetType should succeed"); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"ParaAToken".to_vec(), - symbol: b"ParaA".to_vec(), - decimals: 18, - }; - let response = Response::Version(2); - let querier: Location = [] /* Here */ - .into(); - - // 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: Weight::zero(), - querier: Some(querier), - }]); - - ParaA::execute_with(|| { - // advertised version - parachain::XcmVersioner::set_version(2); - }); - - ParaB::execute_with(|| { - // Let's try with v0 - parachain::XcmVersioner::set_version(0); - - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 0u128)); - }); - - ParaA::execute_with(|| { - // This sets the default version, for not known destinations - assert_ok!(ParachainPalletXcm::force_default_xcm_version( - parachain::RuntimeOrigin::root(), - Some(3) - )); - // Wrap version, which sets VersionedStorage - assert_ok!(::wrap_version( - &Location::new(1, [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::RuntimeEvent = - pallet_xcm::Event::SupportedVersionChanged { - location: Location { - parents: 1, - interior: [Parachain(2)].into(), - }, - version: 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 = Location { - parents: 1, - interior: [ - Parachain(2), - AccountKey20 { - network: None, - key: PARAALICE.into(), - }, - ] - .into(), - }; - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::SelfReserve, 100); - // free execution, full amount received - assert_ok!(PolkadotXcm::transfer_assets( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedLocation::from(beneficiary)), - Box::new(VersionedAssets::from(vec![asset])), - 0, - WeightLimit::Limited(Weight::from_parts(80u64, DEFAULT_PROOF_SIZE)) - )); - // free execution, full amount received - assert_eq!( - ParaBalances::free_balance(&PARAALICE.into()), - INITIAL_BALANCE - 100 - ); - }); - - ParaB::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(100)) - ); - }); - - // 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().iter().any(|e| matches!( - e, - parachain::RuntimeEvent::PolkadotXcm(pallet_xcm::Event::VersionChangeNotified { - result: 2, - .. - }) - ))); - }); - - // This event should have been seen in para A - let expected_supported_version_2: parachain::RuntimeEvent = - pallet_xcm::Event::SupportedVersionChanged { - location: Location { - parents: 1, - interior: [Parachain(2)].into(), - }, - version: 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)); - }); -} - -#[test] -fn receive_asset_with_no_sufficients_is_possible_for_non_existent_account() { - MockNet::reset(); - - let fresh_account = PARABOB; - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - // register relay asset in parachain A - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 0u128)); - }); - - // Actually send relay asset to parachain - let dest: Location = AccountKey20 { - network: None, - key: fresh_account, - } - .into(); - Relay::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([] /* Here */, 123).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - // parachain should have received assets - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, fresh_account.into()), - Ok(U256::from(123)) - ); - }); -} - -#[test] -fn receive_assets_with_sufficients_true_allows_non_funded_account_to_receive_assets() { - MockNet::reset(); - - let fresh_account = [2u8; 20]; - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - // register relay asset in parachain A - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 0u128)); - }); - - // Actually send relay asset to parachain - let dest: Location = AccountKey20 { - network: None, - key: fresh_account, - } - .into(); - Relay::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([] /* Here */, 123).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - // parachain should have received assets - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - EvmForeignAssets::balance(source_id, fresh_account.into()), - Ok(U256::from(123)) - ); - }); -} - -#[test] -fn evm_account_receiving_assets_should_handle_sufficients_ref_count() { - MockNet::reset(); - - let mut sufficient_account = [0u8; 20]; - sufficient_account[0..20].copy_from_slice(&evm_account()[..]); - - let evm_account_id = parachain::AccountId::from(sufficient_account); - - // Evm account is self sufficient - ParaA::execute_with(|| { - assert_eq!(parachain::System::account(evm_account_id).sufficients, 1); - }); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - // register relay asset in parachain A - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 0u128)); - }); - - // Actually send relay asset to parachain - let dest: Location = AccountKey20 { - network: None, - key: sufficient_account, - } - .into(); - Relay::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([] /* Here */, 123).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - // Evm account sufficient ref count increased by 1. - ParaA::execute_with(|| { - // TODO: since the suicided logic was introduced an smart contract account - // is not deleted completely until it's data is deleted. Data deletion - // will be implemented in a future release - // assert_eq!(parachain::System::account(evm_account_id).sufficients, 2); - }); - - ParaA::execute_with(|| { - // Remove the account from the evm context. - parachain::EVM::remove_account(&evm_account()); - // Evm account sufficient ref count decreased by 1. - // TODO: since the suicided logic was introduced an smart contract account - // is not deleted completely until it's data is deleted. Data deletion - // will be implemented in a future release - // assert_eq!(parachain::System::account(evm_account_id).sufficients, 1); - }); -} - -#[test] -fn empty_account_should_not_be_reset() { - MockNet::reset(); - - // Test account has nonce 1 on genesis. - let sufficient_account = PARABOB; - - let evm_account_id = parachain::AccountId::from(sufficient_account); - - let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_id: parachain::AssetId = source_location.clone().into(); - let asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - // register relay asset in parachain A - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 0u128)); - }); - - // Send native token to evm_account - ParaA::execute_with(|| { - assert_ok!(ParaBalances::transfer_allow_death( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - evm_account_id, - 100 - )); - }); - - // Actually send relay asset to parachain - let dest: Location = AccountKey20 { - network: None, - key: sufficient_account, - } - .into(); - Relay::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([] /* Here */, 123).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - ParaA::execute_with(|| { - // Empty the assets from the account. - // As this makes the account go below the `min_balance`, the account is considered dead - // at eyes of pallet-assets, and the consumer reference is decreased by 1 and is now Zero. - // Transfer the assets from evm_account to PARAALICE - assert_ok!(EvmForeignAssets::transfer( - source_id, - evm_account_id, - PARAALICE.into(), - U256::from(123) - )); - // Verify account asset balance is Zero. - assert_eq!( - parachain::EvmForeignAssets::balance(source_id, evm_account_id.into()), - Ok(U256::from(0)) - ); - // Because we no longer have consumer references, we can set the balance to Zero. - // This would reset the account if our ED were to be > than Zero. - assert_ok!(ParaBalances::force_set_balance( - parachain::RuntimeOrigin::root(), - evm_account_id, - 0, - )); - // Verify account native balance is Zero. - assert_eq!(ParaBalances::free_balance(&evm_account_id), 0); - // Remove the account from the evm context. - // This decreases the sufficients reference by 1 and now is Zero. - parachain::EVM::remove_account(&evm_account()); - // Verify reference count. - let account = parachain::System::account(evm_account_id); - assert_eq!(account.sufficients, 0); - assert_eq!(account.consumers, 0); - assert_eq!(account.providers, 1); - // We expect the account to be alive in a Zero ED context. - assert_eq!(parachain::System::account_nonce(evm_account_id), 1); - }); -} - -#[test] -fn test_statemine_like() { - MockNet::reset(); - - let dest_para = Location::new(1, [Parachain(1)]); - - let sov = xcm_builder::SiblingParachainConvertsVia::< - polkadot_parachain::primitives::Sibling, - statemine_like::AccountId, - >::convert_location(&dest_para) - .unwrap(); - - let statemine_asset_a_balances = Location::new( - 1, - [ - Parachain(1000), - PalletInstance(5), - xcm::latest::prelude::GeneralIndex(0u128), - ], - ); - let source_location: AssetType = statemine_asset_a_balances - .try_into() - .expect("Location convertion to AssetType should succeed"); - let source_id: parachain::AssetId = source_location.clone().into(); - - let asset_metadata = parachain::AssetMetadata { - name: b"StatemineToken".to_vec(), - symbol: b"StatemineToken".to_vec(), - decimals: 12, - }; - - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = source_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - asset_metadata.decimals, - asset_metadata.symbol.try_into().expect("too long"), - asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(source_location.clone(), 0u128)); - }); - - Statemine::execute_with(|| { - // Set new prefix - statemine_like::PrefixChanger::set_prefix( - PalletInstance(::index() as u8).into(), - ); - assert_ok!(StatemineAssets::create( - statemine_like::RuntimeOrigin::signed(RELAYALICE), - 0, - RELAYALICE, - 1 - )); - - assert_ok!(StatemineAssets::mint( - statemine_like::RuntimeOrigin::signed(RELAYALICE), - 0, - RELAYALICE, - 300000000000000 - )); - - // This is needed, since the asset is created as non-sufficient - assert_ok!(StatemineBalances::transfer_allow_death( - statemine_like::RuntimeOrigin::signed(RELAYALICE), - sov, - 100000000000000 - )); - - // Actually send relay asset to parachain - let dest: Location = AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - - // Send with new prefix - let asset_location = Location::new( - 0, - [ - xcm::latest::prelude::PalletInstance( - ::index() as u8, - ), - xcm::latest::prelude::GeneralIndex(0), - ], - ); - let fees_id: VersionedAssetId = AssetId(asset_location.clone()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: dest.clone(), - }]); - assert_ok!( - StatemineChainPalletXcm::transfer_assets_using_type_and_then( - statemine_like::RuntimeOrigin::signed(RELAYALICE), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new((asset_location, 123).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - }); - - ParaA::execute_with(|| { - assert_eq!( - EvmForeignAssets::balance(source_id, PARAALICE.into()), - Ok(U256::from(123)) - ); - }); -} - -#[test] -fn send_statemine_asset_from_para_a_to_statemine_with_relay_fee() { - MockNet::reset(); - - // Relay asset - let relay_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_relay_id: parachain::AssetId = relay_location.clone().into(); - - let relay_asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - // Statemine asset - let statemine_asset = Location::new( - 1, - [ - Parachain(1000u32), - PalletInstance(5u8), - GeneralIndex(10u128), - ], - ); - let statemine_location_asset: AssetType = statemine_asset - .clone() - .try_into() - .expect("Location convertion to AssetType should succeed"); - let source_statemine_asset_id: parachain::AssetId = statemine_location_asset.clone().into(); - - let asset_metadata_statemine_asset = parachain::AssetMetadata { - name: b"USDC".to_vec(), - symbol: b"USDC".to_vec(), - decimals: 12, - }; - - let dest_para = Location::new(1, [Parachain(1)]); - - let sov = xcm_builder::SiblingParachainConvertsVia::< - polkadot_parachain::primitives::Sibling, - statemine_like::AccountId, - >::convert_location(&dest_para) - .unwrap(); - - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(relay_location_v3) = relay_location.clone(); - let relay_location_latest: Location = xcm::VersionedLocation::V3(relay_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - let relay_id: parachain::AssetId = relay_location.clone().into(); - assert_ok!(EvmForeignAssets::register_foreign_asset( - relay_id, - relay_location_latest, - relay_asset_metadata.decimals, - relay_asset_metadata.symbol.try_into().expect("too long"), - relay_asset_metadata.name.try_into().expect("too long"), - )); - assert_ok!(add_supported_asset(relay_location.clone(), 0u128)); - - let parachain::AssetType::Xcm(statemine_location_v3) = statemine_location_asset.clone(); - let statemine_location_latest: Location = xcm::VersionedLocation::V3(statemine_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - let statemine_id: parachain::AssetId = statemine_location_asset.clone().into(); - assert_ok!(EvmForeignAssets::register_foreign_asset( - statemine_id, - statemine_location_latest, - asset_metadata_statemine_asset.decimals, - asset_metadata_statemine_asset - .symbol - .try_into() - .expect("too long"), - asset_metadata_statemine_asset - .name - .try_into() - .expect("too long"), - )); - assert_ok!(add_supported_asset(statemine_location_asset.clone(), 0u128)); - }); - - let parachain_beneficiary_from_relay: Location = Junction::AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - - // Send relay chain asset to Alice in Parachain A - Relay::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::here()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_from_relay.clone(), - }]); - assert_ok!(RelayChainPalletXcm::transfer_assets_using_type_and_then( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1).into()), - Box::new(([] /* Here */, 200).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - )); - }); - - Statemine::execute_with(|| { - // Set new prefix - statemine_like::PrefixChanger::set_prefix( - PalletInstance(::index() as u8).into(), - ); - - assert_ok!(StatemineAssets::create( - statemine_like::RuntimeOrigin::signed(RELAYALICE), - 10, - RELAYALICE, - 1 - )); - - assert_ok!(StatemineAssets::mint( - statemine_like::RuntimeOrigin::signed(RELAYALICE), - 10, - RELAYALICE, - 300000000000000 - )); - - // Send some native statemine tokens to sovereign for fees. - // We can't pay fees with USDC as the asset is minted as non-sufficient. - assert_ok!(StatemineBalances::transfer_allow_death( - statemine_like::RuntimeOrigin::signed(RELAYALICE), - sov, - 100000000000000 - )); - - // Send statemine USDC asset to Alice in Parachain A - let parachain_beneficiary_from_statemine: Location = AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - - // Send with new prefix - let asset_location = Location::new( - 0, - [ - xcm::latest::prelude::PalletInstance( - ::index() as u8, - ), - GeneralIndex(10), - ], - ); - let fees_id: VersionedAssetId = AssetId(asset_location.clone()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_from_statemine.clone(), - }]); - assert_ok!( - StatemineChainPalletXcm::transfer_assets_using_type_and_then( - statemine_like::RuntimeOrigin::signed(RELAYALICE), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new((asset_location, 125).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - }); - - let statemine_beneficiary = Location { - parents: 1, - interior: [ - Parachain(1000), - AccountId32 { - network: None, - id: RELAYBOB.into(), - }, - ] - .into(), - }; - - ParaA::execute_with(|| { - // Alice has received 125 USDC - assert_eq!( - EvmForeignAssets::balance(source_statemine_asset_id, PARAALICE.into()), - Ok(U256::from(125)) - ); - - // Alice has received 200 Relay assets - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(200)) - ); - }); - - Statemine::execute_with(|| { - // Check that BOB's balance is empty before the transfer - assert_eq!(StatemineAssets::account_balances(RELAYBOB), vec![]); - }); - let (chain_part, beneficiary) = - split_location_into_chain_part_and_beneficiary(statemine_beneficiary).unwrap(); - - // Transfer USDC from Parachain A to Statemine using Relay asset as fee - ParaA::execute_with(|| { - let asset_1 = currency_to_asset( - parachain::CurrencyId::ForeignAsset(source_statemine_asset_id), - 100, - ); - let asset_2 = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_relay_id), 100); - let fees_id: VersionedAssetId = asset_2.id.clone().into(); - let assets_to_send = vec![asset_1, asset_2]; - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: beneficiary.clone(), - }]); - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from(assets_to_send)), - Box::new(TransferType::DestinationReserve), - Box::new(fees_id), - Box::new(TransferType::DestinationReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(80_000_000u64, 100_000u64)) - )); - }); - - ParaA::execute_with(|| { - // Alice has 100 USDC less - assert_eq!( - EvmForeignAssets::balance(source_statemine_asset_id, PARAALICE.into()), - Ok(U256::from(25)) - ); - - // Alice has 100 relay asset less - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(100)) - ); - }); - - Statemine::execute_with(|| { - // Check that BOB received 100 USDC on statemine - assert_eq!(StatemineAssets::account_balances(RELAYBOB), vec![(10, 100)]); - }); -} - -#[test] -fn send_dot_from_moonbeam_to_statemine_via_xtokens_transfer() { - MockNet::reset(); - - // Relay asset - let relay_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_relay_id: parachain::AssetId = relay_location.clone().into(); - - let relay_asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - let dest_para = Location::new(1, [Parachain(1)]); - - let sov = xcm_builder::SiblingParachainConvertsVia::< - polkadot_parachain::primitives::Sibling, - statemine_like::AccountId, - >::convert_location(&dest_para) - .unwrap(); - - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = relay_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - let source_id: parachain::AssetId = relay_location.clone().into(); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - relay_asset_metadata.decimals, - relay_asset_metadata.symbol.try_into().expect("too long"), - relay_asset_metadata.name.try_into().expect("too long"), - )); - XcmWeightTrader::set_asset_price(Location::parent(), 0u128); - }); - - let parachain_beneficiary_absolute: Location = Junction::AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - - let statemine_beneficiary_absolute: Location = Junction::AccountId32 { - network: None, - id: RELAYALICE.into(), - } - .into(); - - // First we send relay chain asset to Alice in AssetHub (via teleport) - Relay::execute_with(|| { - assert_ok!(RelayChainPalletXcm::limited_teleport_assets( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1000).into()), - Box::new( - VersionedLocation::from(statemine_beneficiary_absolute) - .clone() - .into() - ), - Box::new(([], 200).into()), - 0, - WeightLimit::Unlimited - )); - }); - - // Send DOTs from AssetHub to ParaA (Moonbeam) - Statemine::execute_with(|| { - // Check Alice received 200 tokens on AssetHub - assert_eq!( - StatemineBalances::free_balance(RELAYALICE), - INITIAL_BALANCE + 200 - ); - - assert_ok!(StatemineBalances::transfer_allow_death( - statemine_like::RuntimeOrigin::signed(RELAYALICE), - sov, - 110000000000000 - )); - - // Now send those tokens to ParaA - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_absolute.clone(), - }]); - assert_ok!( - StatemineChainPalletXcm::transfer_assets_using_type_and_then( - statemine_like::RuntimeOrigin::signed(RELAYALICE), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new((Location::parent(), 200).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - }); - - ParaA::execute_with(|| { - // Alice should have received the DOTs - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(200)) - ); - }); - - let dest = Location::new( - 1, - [ - Parachain(1000), - AccountId32 { - network: None, - id: RELAYBOB.into(), - }, - ], - ); - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - // Finally we test that we are able to send back the DOTs to AssetHub from the ParaA - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_relay_id), 100); - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: beneficiary.clone(), - }]); - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from(vec![asset])), - Box::new(TransferType::DestinationReserve), - Box::new(fees_id), - Box::new(TransferType::DestinationReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) - )); - - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(100)) - ); - }); - - Statemine::execute_with(|| { - // Check that Bob received the tokens back in AssetHub - assert_eq!( - StatemineBalances::free_balance(RELAYBOB), - INITIAL_BALANCE + 100 - ); - }); - - // Send back tokens from AH to ParaA from Bob's account - Statemine::execute_with(|| { - // Now send those tokens to ParaA - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_absolute.clone(), - }]); - assert_ok!( - StatemineChainPalletXcm::transfer_assets_using_type_and_then( - statemine_like::RuntimeOrigin::signed(RELAYBOB), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new((Location::parent(), 100).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - - // 100 DOTs were deducted from Bob's account - assert_eq!(StatemineBalances::free_balance(RELAYBOB), INITIAL_BALANCE); - }); - - ParaA::execute_with(|| { - // Alice should have received 100 DOTs - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(200)) - ); - }); -} - -#[test] -fn send_dot_from_moonbeam_to_statemine_via_xtokens_transfer_with_fee() { - MockNet::reset(); - - // Relay asset - let relay_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_relay_id: parachain::AssetId = relay_location.clone().into(); - - let relay_asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - let dest_para = Location::new(1, [Parachain(1)]); - - let sov = xcm_builder::SiblingParachainConvertsVia::< - polkadot_parachain::primitives::Sibling, - statemine_like::AccountId, - >::convert_location(&dest_para) - .unwrap(); - - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = relay_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - let source_id: parachain::AssetId = relay_location.clone().into(); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - relay_asset_metadata.decimals, - relay_asset_metadata.symbol.try_into().expect("too long"), - relay_asset_metadata.name.try_into().expect("too long"), - )); - XcmWeightTrader::set_asset_price(Location::parent(), 0u128); - }); - - let parachain_beneficiary_absolute: Location = Junction::AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - - let statemine_beneficiary_absolute: Location = Junction::AccountId32 { - network: None, - id: RELAYALICE.into(), - } - .into(); - - // First we send relay chain asset to Alice in AssetHub (via teleport) - Relay::execute_with(|| { - assert_ok!(RelayChainPalletXcm::limited_teleport_assets( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1000).into()), - Box::new( - VersionedLocation::from(statemine_beneficiary_absolute) - .clone() - .into() - ), - Box::new(([], 200).into()), - 0, - WeightLimit::Unlimited - )); - }); - - // Send DOTs from AssetHub to ParaA (Moonbeam) - Statemine::execute_with(|| { - // Check Alice received 200 tokens on AssetHub - assert_eq!( - StatemineBalances::free_balance(RELAYALICE), - INITIAL_BALANCE + 200 - ); - - assert_ok!(StatemineBalances::transfer_allow_death( - statemine_like::RuntimeOrigin::signed(RELAYALICE), - sov, - 110000000000000 - )); - - // Now send those tokens to ParaA - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_absolute.clone(), - }]); - assert_ok!( - StatemineChainPalletXcm::transfer_assets_using_type_and_then( - statemine_like::RuntimeOrigin::signed(RELAYALICE), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new((Location::parent(), 200).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - }); - - ParaA::execute_with(|| { - // Alice should have received the DOTs - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(200)) - ); - }); - - let dest = Location::new( - 1, - [ - Parachain(1000), - AccountId32 { - network: None, - id: RELAYBOB.into(), - }, - ], - ); - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - // Finally we test that we are able to send back the DOTs to AssetHub from the ParaA - ParaA::execute_with(|| { - let asset = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_relay_id), 100); - let asset_fee = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_relay_id), 10); - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: beneficiary.clone(), - }]); - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from(vec![asset_fee, asset])), - Box::new(TransferType::DestinationReserve), - Box::new(fees_id), - Box::new(TransferType::DestinationReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) - )); - - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(90)) - ); - }); - - Statemine::execute_with(|| { - // Free execution: check that Bob received the tokens back in AssetHub - assert_eq!( - StatemineBalances::free_balance(RELAYBOB), - INITIAL_BALANCE + 110 - ); - }); - - // Send back tokens from AH to ParaA from Bob's account - Statemine::execute_with(|| { - // Now send those tokens to ParaA - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_absolute.clone(), - }]); - assert_ok!( - StatemineChainPalletXcm::transfer_assets_using_type_and_then( - statemine_like::RuntimeOrigin::signed(RELAYBOB), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new((Location::parent(), 100).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - - // 100 DOTs were deducted from Bob's account - assert_eq!( - StatemineBalances::free_balance(RELAYBOB), - INITIAL_BALANCE + 10 - ); - }); - - ParaA::execute_with(|| { - // Alice should have received 100 DOTs - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(190)) - ); - }); -} - -#[test] -fn send_dot_from_moonbeam_to_statemine_via_xtokens_transfer_multiasset() { - MockNet::reset(); - - // Relay asset - let relay_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_relay_id: parachain::AssetId = relay_location.clone().into(); - - let relay_asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - let dest_para = Location::new(1, [Parachain(1)]); - - let sov = xcm_builder::SiblingParachainConvertsVia::< - polkadot_parachain::primitives::Sibling, - statemine_like::AccountId, - >::convert_location(&dest_para) - .unwrap(); - - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = relay_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - let source_id: parachain::AssetId = relay_location.clone().into(); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - relay_asset_metadata.decimals, - relay_asset_metadata.symbol.try_into().expect("too long"), - relay_asset_metadata.name.try_into().expect("too long"), - )); - XcmWeightTrader::set_asset_price(Location::parent(), 0u128); - }); - - let parachain_beneficiary_absolute: Location = Junction::AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - - let statemine_beneficiary_absolute: Location = Junction::AccountId32 { - network: None, - id: RELAYALICE.into(), - } - .into(); - - // First we send relay chain asset to Alice in AssetHub (via teleport) - Relay::execute_with(|| { - assert_ok!(RelayChainPalletXcm::limited_teleport_assets( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1000).into()), - Box::new( - VersionedLocation::from(statemine_beneficiary_absolute) - .clone() - .into() - ), - Box::new(([], 200).into()), - 0, - WeightLimit::Unlimited - )); - }); - - // Send DOTs from AssetHub to ParaA (Moonbeam) - Statemine::execute_with(|| { - // Check Alice received 200 tokens on AssetHub - assert_eq!( - StatemineBalances::free_balance(RELAYALICE), - INITIAL_BALANCE + 200 - ); - - assert_ok!(StatemineBalances::transfer_allow_death( - statemine_like::RuntimeOrigin::signed(RELAYALICE), - sov, - 110000000000000 - )); - - // Now send those tokens to ParaA - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_absolute.clone(), - }]); - assert_ok!( - StatemineChainPalletXcm::transfer_assets_using_type_and_then( - statemine_like::RuntimeOrigin::signed(RELAYALICE), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new((Location::parent(), 200).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - }); - - ParaA::execute_with(|| { - // Alice should have received the DOTs - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(200)) - ); - }); - - let dest = Location::new( - 1, - [ - Parachain(1000), - AccountId32 { - network: None, - id: RELAYBOB.into(), - }, - ], - ); - - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - // Finally we test that we are able to send back the DOTs to AssetHub from the ParaA - ParaA::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: beneficiary.clone(), - }]); - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from( - vec![(Location::parent(), 100).into()] - )), - Box::new(TransferType::DestinationReserve), - Box::new(fees_id), - Box::new(TransferType::DestinationReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(40000u64, DEFAULT_PROOF_SIZE)) - )); - - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(100)) - ); - }); - - Statemine::execute_with(|| { - // Check that Bob received the tokens back in AssetHub - assert_eq!( - StatemineBalances::free_balance(RELAYBOB), - INITIAL_BALANCE + 100 - ); - }); - - // Send back tokens from AH to ParaA from Bob's account - Statemine::execute_with(|| { - // Now send those tokens to ParaA - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_absolute.clone(), - }]); - assert_ok!( - StatemineChainPalletXcm::transfer_assets_using_type_and_then( - statemine_like::RuntimeOrigin::signed(RELAYBOB), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new((Location::parent(), 100).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - - // 100 DOTs were deducted from Bob's account - assert_eq!(StatemineBalances::free_balance(RELAYBOB), INITIAL_BALANCE); - }); - - ParaA::execute_with(|| { - // Alice should have received 100 DOTs - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(200)) - ); - }); -} - -#[test] -fn send_dot_from_moonbeam_to_statemine_via_xtokens_transfer_multicurrencies() { - MockNet::reset(); - - // Relay asset - let relay_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_relay_id: parachain::AssetId = relay_location.clone().into(); - - let relay_asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - // Statemine asset - let statemine_asset = Location::new( - 1, - [ - Parachain(1000u32), - PalletInstance(5u8), - GeneralIndex(10u128), - ], - ); - let statemine_location_asset: AssetType = statemine_asset - .clone() - .try_into() - .expect("Location convertion to AssetType should succeed"); - let source_statemine_asset_id: parachain::AssetId = statemine_location_asset.clone().into(); - - let asset_metadata_statemine_asset = parachain::AssetMetadata { - name: b"USDC".to_vec(), - symbol: b"USDC".to_vec(), - decimals: 12, - }; - - let dest_para = Location::new(1, [Parachain(1)]); - - let sov = xcm_builder::SiblingParachainConvertsVia::< - polkadot_parachain::primitives::Sibling, - statemine_like::AccountId, - >::convert_location(&dest_para) - .unwrap(); - - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = relay_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - let source_id: parachain::AssetId = relay_location.clone().into(); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - relay_asset_metadata.decimals, - relay_asset_metadata.symbol.try_into().expect("too long"), - relay_asset_metadata.name.try_into().expect("too long"), - )); - XcmWeightTrader::set_asset_price(Location::parent(), 0u128); - - let parachain::AssetType::Xcm(statemine_location_v3) = statemine_location_asset.clone(); - let statemine_location_latest: Location = xcm::VersionedLocation::V3(statemine_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - let statemine_id: parachain::AssetId = statemine_location_asset.clone().into(); - assert_ok!(EvmForeignAssets::register_foreign_asset( - statemine_id, - statemine_location_latest, - asset_metadata_statemine_asset.decimals, - asset_metadata_statemine_asset - .symbol - .try_into() - .expect("too long"), - asset_metadata_statemine_asset - .name - .try_into() - .expect("too long"), - )); - XcmWeightTrader::set_asset_price(statemine_asset.clone(), 0u128); - }); - - let parachain_beneficiary_absolute: Location = Junction::AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - - let statemine_beneficiary_absolute: Location = Junction::AccountId32 { - network: None, - id: RELAYALICE.into(), - } - .into(); - - // First we send relay chain asset to Alice in AssetHub (via teleport) - Relay::execute_with(|| { - assert_ok!(RelayChainPalletXcm::limited_teleport_assets( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1000).into()), - Box::new( - VersionedLocation::from(statemine_beneficiary_absolute) - .clone() - .into() - ), - Box::new(([], 200).into()), - 0, - WeightLimit::Unlimited - )); - }); - - // Send DOTs and USDC from AssetHub to ParaA (Moonbeam) - Statemine::execute_with(|| { - // Check Alice received 200 tokens on AssetHub - assert_eq!( - StatemineBalances::free_balance(RELAYALICE), - INITIAL_BALANCE + 200 - ); - - assert_ok!(StatemineBalances::transfer_allow_death( - statemine_like::RuntimeOrigin::signed(RELAYALICE), - sov, - 110000000000000 - )); - - statemine_like::PrefixChanger::set_prefix( - PalletInstance(::index() as u8).into(), - ); - - assert_ok!(StatemineAssets::create( - statemine_like::RuntimeOrigin::signed(RELAYALICE), - 10, - RELAYALICE, - 1 - )); - - assert_ok!(StatemineAssets::mint( - statemine_like::RuntimeOrigin::signed(RELAYALICE), - 10, - RELAYALICE, - 300000000000000 - )); - - // Now send relay tokens to ParaA - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_absolute.clone(), - }]); - assert_ok!( - StatemineChainPalletXcm::transfer_assets_using_type_and_then( - statemine_like::RuntimeOrigin::signed(RELAYALICE), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new((Location::parent(), 200).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - - // Send USDC - let asset_location = Location::new( - 0, - [ - xcm::latest::prelude::PalletInstance( - ::index() as u8, - ), - GeneralIndex(10), - ], - ); - let fees_id: VersionedAssetId = AssetId(asset_location.clone()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_absolute.clone(), - }]); - assert_ok!( - StatemineChainPalletXcm::transfer_assets_using_type_and_then( - statemine_like::RuntimeOrigin::signed(RELAYALICE), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new((asset_location, 125).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - }); - - ParaA::execute_with(|| { - // Alice should have received the DOTs - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(200)) - ); - - // Alice has received 125 USDC - assert_eq!( - EvmForeignAssets::balance(source_statemine_asset_id, PARAALICE.into()), - Ok(U256::from(125)) - ); - }); - - let dest = Location::new( - 1, - [ - Parachain(1000), - AccountId32 { - network: None, - id: RELAYBOB.into(), - }, - ], - ); - - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - - // Finally we test that we are able to send back the DOTs to AssetHub from the ParaA - ParaA::execute_with(|| { - let asset_1 = currency_to_asset( - parachain::CurrencyId::ForeignAsset(source_statemine_asset_id), - 100, - ); - let asset_2 = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_relay_id), 100); - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: beneficiary.clone(), - }]); - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from(vec![asset_1, asset_2])), - Box::new(TransferType::DestinationReserve), - Box::new(fees_id), - Box::new(TransferType::DestinationReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(80_000_000u64, 100_000u64)) - )); - - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(100)) - ); - }); - - Statemine::execute_with(|| { - // Check that Bob received relay tokens back in AssetHub - // (100 - MinXcmFee) - assert_eq!( - StatemineBalances::free_balance(RELAYBOB), - INITIAL_BALANCE + 100 - ); - - // Check that BOB received 100 USDC on AssetHub - assert_eq!(StatemineAssets::account_balances(RELAYBOB), vec![(10, 100)]); - }); - - // Send back tokens from AH to ParaA from Bob's account - Statemine::execute_with(|| { - let bob_previous_balance = StatemineBalances::free_balance(RELAYBOB); - - // Now send those tokens to ParaA - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_absolute.clone(), - }]); - assert_ok!( - StatemineChainPalletXcm::transfer_assets_using_type_and_then( - statemine_like::RuntimeOrigin::signed(RELAYBOB), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new((Location::parent(), 100).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - - // 100 DOTs were deducted from Bob's account - assert_eq!( - StatemineBalances::free_balance(RELAYBOB), - bob_previous_balance - 100 - ); - }); - - ParaA::execute_with(|| { - // Alice should have received 100 DOTs - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(200)) - ); - }); -} - -#[test] -fn send_dot_from_moonbeam_to_statemine_via_xtokens_transfer_multiassets() { - MockNet::reset(); - - // Relay asset - let relay_location = parachain::AssetType::Xcm(xcm::v3::Location::parent()); - let source_relay_id: parachain::AssetId = relay_location.clone().into(); - - let relay_asset_metadata = parachain::AssetMetadata { - name: b"RelayToken".to_vec(), - symbol: b"Relay".to_vec(), - decimals: 12, - }; - - // Statemine asset - let statemine_asset = Location::new( - 1, - [ - Parachain(1000u32), - PalletInstance(5u8), - GeneralIndex(10u128), - ], - ); - let statemine_location_asset: AssetType = statemine_asset - .clone() - .try_into() - .expect("Location convertion to AssetType should succeed"); - let source_statemine_asset_id: parachain::AssetId = statemine_location_asset.clone().into(); - - let asset_metadata_statemine_asset = parachain::AssetMetadata { - name: b"USDC".to_vec(), - symbol: b"USDC".to_vec(), - decimals: 12, - }; - - let dest_para = Location::new(1, [Parachain(1)]); - - let sov = xcm_builder::SiblingParachainConvertsVia::< - polkadot_parachain::primitives::Sibling, - statemine_like::AccountId, - >::convert_location(&dest_para) - .unwrap(); - - ParaA::execute_with(|| { - let parachain::AssetType::Xcm(source_location_v3) = relay_location.clone(); - let source_location_latest: Location = xcm::VersionedLocation::V3(source_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - let source_id: parachain::AssetId = relay_location.clone().into(); - assert_ok!(EvmForeignAssets::register_foreign_asset( - source_id, - source_location_latest, - relay_asset_metadata.decimals, - relay_asset_metadata.symbol.try_into().expect("too long"), - relay_asset_metadata.name.try_into().expect("too long"), - )); - XcmWeightTrader::set_asset_price(Location::parent(), 0u128); - - let parachain::AssetType::Xcm(statemine_location_v3) = statemine_location_asset.clone(); - let statemine_location_latest: Location = xcm::VersionedLocation::V3(statemine_location_v3) - .try_into() - .expect("v3 to latest location conversion failed"); - let statemine_id: parachain::AssetId = statemine_location_asset.clone().into(); - assert_ok!(EvmForeignAssets::register_foreign_asset( - statemine_id, - statemine_location_latest, - asset_metadata_statemine_asset.decimals, - asset_metadata_statemine_asset - .symbol - .try_into() - .expect("too long"), - asset_metadata_statemine_asset - .name - .try_into() - .expect("too long"), - )); - XcmWeightTrader::set_asset_price(statemine_asset.clone(), 0u128); - }); - - let parachain_beneficiary_absolute: Location = Junction::AccountKey20 { - network: None, - key: PARAALICE, - } - .into(); - - let statemine_beneficiary_absolute: Location = Junction::AccountId32 { - network: None, - id: RELAYALICE.into(), - } - .into(); - - // First we send relay chain asset to Alice in AssetHub (via teleport) - Relay::execute_with(|| { - assert_ok!(RelayChainPalletXcm::limited_teleport_assets( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - Box::new(Parachain(1000).into()), - Box::new( - VersionedLocation::from(statemine_beneficiary_absolute) - .clone() - .into() - ), - Box::new(([], 200).into()), - 0, - WeightLimit::Unlimited - )); - }); - - // Send DOTs and USDC from AssetHub to ParaA (Moonbeam) - Statemine::execute_with(|| { - // Check Alice received 200 tokens on AssetHub - assert_eq!( - StatemineBalances::free_balance(RELAYALICE), - INITIAL_BALANCE + 200 - ); - - assert_ok!(StatemineBalances::transfer_allow_death( - statemine_like::RuntimeOrigin::signed(RELAYALICE), - sov, - 110000000000000 - )); - - statemine_like::PrefixChanger::set_prefix( - PalletInstance(::index() as u8).into(), - ); - - assert_ok!(StatemineAssets::create( - statemine_like::RuntimeOrigin::signed(RELAYALICE), - 10, - RELAYALICE, - 1 - )); - - assert_ok!(StatemineAssets::mint( - statemine_like::RuntimeOrigin::signed(RELAYALICE), - 10, - RELAYALICE, - 300000000000000 - )); - - // Now send relay tokens to ParaA - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_absolute.clone(), - }]); - assert_ok!( - StatemineChainPalletXcm::transfer_assets_using_type_and_then( - statemine_like::RuntimeOrigin::signed(RELAYALICE), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new((Location::parent(), 200).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - - // Send USDC - let asset_location = Location::new( - 0, - [ - xcm::latest::prelude::PalletInstance( - ::index() as u8, - ), - GeneralIndex(10), - ], - ); - let fees_id: VersionedAssetId = AssetId(asset_location.clone()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_absolute.clone(), - }]); - assert_ok!( - StatemineChainPalletXcm::transfer_assets_using_type_and_then( - statemine_like::RuntimeOrigin::signed(RELAYALICE), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new((asset_location, 125).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - }); - - ParaA::execute_with(|| { - // Alice should have received the DOTs - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(200)) - ); - - // Alice has received 125 USDC - assert_eq!( - EvmForeignAssets::balance(source_statemine_asset_id, PARAALICE.into()), - Ok(U256::from(125)) - ); - }); - - let dest = Location::new( - 1, - [ - Parachain(1000), - AccountId32 { - network: None, - id: RELAYBOB.into(), - }, - ], - ); - - let statemine_asset_to_send = Asset { - id: AssetId(statemine_asset), - fun: Fungibility::Fungible(100), - }; - - let relay_asset_to_send = Asset { - id: AssetId(Location::parent()), - fun: Fungibility::Fungible(100), - }; - - let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap(); - let assets_to_send: XcmAssets = - XcmAssets::from(vec![statemine_asset_to_send, relay_asset_to_send.clone()]); - // For some reason the order of the assets is inverted when creating the array above. - // We need to use relay asset for fees, so we pick index 0. - assert_eq!(assets_to_send.get(0).unwrap(), &relay_asset_to_send); - - // Finally we test that we are able to send back the DOTs to AssetHub from the ParaA - ParaA::execute_with(|| { - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: beneficiary.clone(), - }]); - assert_ok!(PolkadotXcm::transfer_assets_using_type_and_then( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(VersionedLocation::from(chain_part)), - Box::new(VersionedAssets::from(assets_to_send)), - Box::new(TransferType::DestinationReserve), - Box::new(fees_id), - Box::new(TransferType::DestinationReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Limited(Weight::from_parts(80_000_000u64, 100_000u64)) - )); - - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(100)) - ); - }); - - Statemine::execute_with(|| { - // Check that Bob received relay tokens back in AssetHub - // (100 - MinXcmFee) - assert_eq!( - StatemineBalances::free_balance(RELAYBOB), - INITIAL_BALANCE + 100 - ); - - // Check that BOB received 100 USDC on AssetHub - assert_eq!(StatemineAssets::account_balances(RELAYBOB), vec![(10, 100)]); - }); - - // Send back tokens from AH to ParaA from Bob's account - Statemine::execute_with(|| { - let bob_previous_balance = StatemineBalances::free_balance(RELAYBOB); - - // Now send those tokens to ParaA - let fees_id: VersionedAssetId = AssetId(Location::parent()).into(); - let xcm_on_dest = Xcm::<()>(vec![DepositAsset { - assets: Wild(All), - beneficiary: parachain_beneficiary_absolute.clone(), - }]); - assert_ok!( - StatemineChainPalletXcm::transfer_assets_using_type_and_then( - statemine_like::RuntimeOrigin::signed(RELAYBOB), - Box::new(Location::new(1, [Parachain(1)]).into()), - Box::new((Location::parent(), 100).into()), - Box::new(TransferType::LocalReserve), - Box::new(fees_id), - Box::new(TransferType::LocalReserve), - Box::new(VersionedXcm::V5(xcm_on_dest)), - WeightLimit::Unlimited - ) - ); - - // 100 DOTs were deducted from Bob's account - assert_eq!( - StatemineBalances::free_balance(RELAYBOB), - bob_previous_balance - 100 - ); - }); - - ParaA::execute_with(|| { - // Alice should have received 100 DOTs - assert_eq!( - EvmForeignAssets::balance(source_relay_id, PARAALICE.into()), - Ok(U256::from(200)) - ); - }); -} - -#[test] -fn transact_through_signed_multilocation() { - MockNet::reset(); - let mut ancestry = Location::parent(); - - ParaA::execute_with(|| { - // Root can set transact info - assert_ok!(XcmTransactor::set_transact_info( - parachain::RuntimeOrigin::root(), - Box::new(xcm::VersionedLocation::from(Location::parent())), - // Relay charges 1000 for every instruction, and we have 3, so 3000 - 3000.into(), - 20000000000.into(), - // 4 instructions in transact through signed - Some(4000.into()) - )); - // Set fee per second using weight-trader (replaces old set_fee_per_second) - set_fee_per_second_for_location(Location::parent(), WEIGHT_REF_TIME_PER_SECOND as u128) - .expect("must succeed"); - ancestry = parachain::UniversalLocation::get().into(); - }); - - // Let's construct the Junction that we will append with DescendOrigin - let signed_origin: Junctions = [AccountKey20 { - network: None, - key: PARAALICE, - }] - .into(); - - let mut descend_origin_multilocation = parachain::SelfLocation::get(); - descend_origin_multilocation - .append_with(signed_origin) - .unwrap(); - - // To convert it to what the relay will see instead of us - descend_origin_multilocation - .reanchor(&Location::parent(), &ancestry.interior) - .unwrap(); - - let derived = xcm_builder::Account32Hash::< - relay_chain::KusamaNetwork, - relay_chain::AccountId, - >::convert_location(&descend_origin_multilocation) - .unwrap(); - - Relay::execute_with(|| { - // free execution, full amount received - assert_ok!(RelayBalances::transfer_allow_death( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - derived.clone(), - 4000004100u128, - )); - // derived account has all funds - assert!(RelayBalances::free_balance(&derived) == 4000004100); - // sovereign account has 0 funds - assert!(RelayBalances::free_balance(¶_a_account()) == 0); - }); - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = ::PalletInfo::index::< - relay_chain::Balances, - >() - .unwrap() as u8; - - encoded.push(index); - - // Then call bytes - let mut call_bytes = pallet_balances::Call::::transfer_allow_death { - // 100 to sovereign - dest: para_a_account(), - value: 100u32.into(), - } - .encode(); - encoded.append(&mut call_bytes); - - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::transact_through_signed( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(xcm::VersionedLocation::from(Location::parent())), - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - Location::parent() - ))), - fee_amount: None - }, - encoded, - // 4000000000 for transfer + 4000 for XCM - // 1-1 to fee - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: None - }, - false - )); - }); - - Relay::execute_with(|| { - assert!(RelayBalances::free_balance(¶_a_account()) == 100); - - assert!(RelayBalances::free_balance(&derived) == 0); - }); -} - -#[test] -fn transact_through_signed_multilocation_custom_fee_and_weight() { - MockNet::reset(); - let mut ancestry = Location::parent(); - - ParaA::execute_with(|| { - ancestry = parachain::UniversalLocation::get().into(); - }); - - // Let's construct the Junction that we will append with DescendOrigin - let signed_origin: Junctions = [AccountKey20 { - network: None, - key: PARAALICE, - }] - .into(); - - let mut descend_origin_multilocation = parachain::SelfLocation::get(); - descend_origin_multilocation - .append_with(signed_origin) - .unwrap(); - - // To convert it to what the relay will see instead of us - descend_origin_multilocation - .reanchor(&Location::parent(), &ancestry.interior) - .unwrap(); - - let derived = xcm_builder::Account32Hash::< - relay_chain::KusamaNetwork, - relay_chain::AccountId, - >::convert_location(&descend_origin_multilocation) - .unwrap(); - - Relay::execute_with(|| { - // free execution, full amount received - assert_ok!(RelayBalances::transfer_allow_death( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - derived.clone(), - 4000004100u128, - )); - // derived account has all funds - assert!(RelayBalances::free_balance(&derived) == 4000004100); - // sovereign account has 0 funds - assert!(RelayBalances::free_balance(¶_a_account()) == 0); - }); - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = ::PalletInfo::index::< - relay_chain::Balances, - >() - .unwrap() as u8; - - encoded.push(index); - - // Then call bytes - let mut call_bytes = pallet_balances::Call::::transfer_allow_death { - // 100 to sovereign - dest: para_a_account(), - value: 100u32.into(), - } - .encode(); - encoded.append(&mut call_bytes); - - let total_weight = 4000004000u64; - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::transact_through_signed( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(xcm::VersionedLocation::from(Location::parent())), - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - Location::parent() - ))), - fee_amount: Some(total_weight as u128) - }, - encoded, - // 4000000000 for transfer + 4000 for XCM - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: Some(Limited(total_weight.into())) - }, - false - )); - }); - - Relay::execute_with(|| { - assert!(RelayBalances::free_balance(¶_a_account()) == 100); - - assert!(RelayBalances::free_balance(&derived) == 0); - }); -} - -#[test] -fn transact_through_signed_multilocation_custom_fee_and_weight_refund() { - MockNet::reset(); - let mut ancestry = Location::parent(); - - ParaA::execute_with(|| { - ancestry = parachain::UniversalLocation::get().into(); - }); - - // Let's construct the Junction that we will append with DescendOrigin - let signed_origin: Junctions = [AccountKey20 { - network: None, - key: PARAALICE, - }] - .into(); - - let mut descend_origin_multilocation = parachain::SelfLocation::get(); - descend_origin_multilocation - .append_with(signed_origin) - .unwrap(); - - // To convert it to what the relay will see instead of us - descend_origin_multilocation - .reanchor(&Location::parent(), &ancestry.interior) - .unwrap(); - - let derived = xcm_builder::Account32Hash::< - relay_chain::KusamaNetwork, - relay_chain::AccountId, - >::convert_location(&descend_origin_multilocation) - .unwrap(); - - Relay::execute_with(|| { - // free execution, full amount received - assert_ok!(RelayBalances::transfer_allow_death( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - derived.clone(), - 4000009100u128, - )); - // derived account has all funds - assert!(RelayBalances::free_balance(&derived) == 4000009100); - // sovereign account has 0 funds - assert!(RelayBalances::free_balance(¶_a_account()) == 0); - }); - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = ::PalletInfo::index::< - relay_chain::Balances, - >() - .unwrap() as u8; - - encoded.push(index); - - // Then call bytes - let mut call_bytes = pallet_balances::Call::::transfer_allow_death { - // 100 to sovereign - dest: para_a_account(), - value: 100u32.into(), - } - .encode(); - encoded.append(&mut call_bytes); - - let total_weight = 4000009000u64; - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::transact_through_signed( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(xcm::VersionedLocation::from(Location::parent())), - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - Location::parent() - ))), - fee_amount: Some(total_weight as u128) - }, - encoded, - // 4000000000 for transfer + 9000 for XCM - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: Some(Limited(total_weight.into())) - }, - true - )); - }); - - Relay::execute_with(|| { - // 100 transferred - assert_eq!(RelayBalances::free_balance(¶_a_account()), 100); - - // 4000009000 refunded - assert_eq!(RelayBalances::free_balance(&derived), 4000009000); - }); -} - -#[test] -fn transact_through_signed_multilocation_para_to_para() { - MockNet::reset(); - let mut ancestry = Location::parent(); - - let para_b_location = Location::new(1, [Parachain(2)]); - - let para_b_balances = Location::new(1, [Parachain(2), PalletInstance(1u8)]); - - ParaA::execute_with(|| { - // Root can set transact info - assert_ok!(XcmTransactor::set_transact_info( - parachain::RuntimeOrigin::root(), - // ParaB - Box::new(xcm::VersionedLocation::from(para_b_location.clone())), - // Para charges 1000 for every instruction, and we have 3, so 3 - 3.into(), - 20000000000.into(), - // 4 instructions in transact through signed - Some(4.into()) - )); - // Root can set transact info - // Set fee per second using weight-trader (replaces old set_fee_per_second) - set_fee_per_second_for_location( - para_b_balances.clone(), - parachain::ParaTokensPerSecond::get(), - ) - .expect("must succeed"); - ancestry = parachain::UniversalLocation::get().into(); - }); - - // Let's construct the Junction that we will append with DescendOrigin - let signed_origin: Junctions = [AccountKey20 { - network: None, - key: PARAALICE, - }] - .into(); - - let mut descend_origin_location = parachain::SelfLocation::get(); - descend_origin_location.append_with(signed_origin).unwrap(); - - // To convert it to what the paraB will see instead of us - descend_origin_location - .reanchor(¶_b_location, &ancestry.interior) - .unwrap(); - - let derived = xcm_builder::HashedDescription::< - parachain::AccountId, - xcm_builder::DescribeFamily, - >::convert_location(&descend_origin_location) - .unwrap(); - - ParaB::execute_with(|| { - // free execution, full amount received - assert_ok!(ParaBalances::transfer_allow_death( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - derived.clone(), - 4000000104u128, - )); - // derived account has all funds - assert!(ParaBalances::free_balance(&derived) == 4000000104); - // sovereign account has 0 funds - assert!(ParaBalances::free_balance(¶_a_account_20()) == 0); - }); - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = - ::PalletInfo::index::() - .unwrap() as u8; - - encoded.push(index); - - // Then call bytes - let mut call_bytes = pallet_balances::Call::::transfer_allow_death { - // 100 to sovereign - dest: para_a_account_20(), - value: 100u32.into(), - } - .encode(); - encoded.append(&mut call_bytes); - - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::transact_through_signed( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(xcm::VersionedLocation::from(para_b_location)), - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - para_b_balances - ))), - fee_amount: None - }, - encoded, - // 4000000000 for transfer + 4000 for XCM - // 1-1 to fee - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: None - }, - false - )); - }); - - ParaB::execute_with(|| { - assert!(ParaBalances::free_balance(&derived) == 0); - - assert!(ParaBalances::free_balance(¶_a_account_20()) == 100); - }); -} - -#[test] -fn transact_through_signed_multilocation_para_to_para_refund() { - MockNet::reset(); - let mut ancestry = Location::parent(); - - let para_b_location = Location::new(1, [Parachain(2)]); - - let para_b_balances = Location::new(1, [Parachain(2), PalletInstance(1u8)]); - - ParaA::execute_with(|| { - // Set fee per second using weight-trader (replaces old set_fee_per_second) - set_fee_per_second_for_location( - para_b_balances.clone(), - parachain::ParaTokensPerSecond::get(), - ) - .expect("must succeed"); - ancestry = parachain::UniversalLocation::get().into(); - }); - - // Let's construct the Junction that we will append with DescendOrigin - let signed_origin: Junctions = [AccountKey20 { - network: None, - key: PARAALICE, - }] - .into(); - - let mut descend_origin_location = parachain::SelfLocation::get(); - descend_origin_location.append_with(signed_origin).unwrap(); - - // To convert it to what the paraB will see instead of us - descend_origin_location - .reanchor(¶_b_location, &ancestry.interior) - .unwrap(); - - let derived = xcm_builder::HashedDescription::< - parachain::AccountId, - xcm_builder::DescribeFamily, - >::convert_location(&descend_origin_location) - .unwrap(); - - ParaB::execute_with(|| { - // free execution, full amount received - assert_ok!(ParaBalances::transfer_allow_death( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - derived.clone(), - 4000009100u128, - )); - // derived account has all funds - assert!(ParaBalances::free_balance(&derived) == 4000009100); - // sovereign account has 0 funds - assert!(ParaBalances::free_balance(¶_a_account_20()) == 0); - }); - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = - ::PalletInfo::index::() - .unwrap() as u8; - - encoded.push(index); - - // Then call bytes - let mut call_bytes = pallet_balances::Call::::transfer_allow_death { - // 100 to sovereign - dest: para_a_account_20(), - value: 100u32.into(), - } - .encode(); - encoded.append(&mut call_bytes); - - let overall_weight = 4000009000u64; - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::transact_through_signed( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(xcm::VersionedLocation::from(para_b_location)), - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - para_b_balances - ))), - fee_amount: Some(overall_weight as u128) - }, - encoded, - // 4000000000 for transfer + 9000 for XCM - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: Some(Limited(overall_weight.into())) - }, - true - )); - }); - - ParaB::execute_with(|| { - // Check the derived account was refunded - assert_eq!(ParaBalances::free_balance(&derived), 3826174993); - - // Check the transfer was executed - assert_eq!(ParaBalances::free_balance(¶_a_account_20()), 100); - }); -} - -#[test] -fn transact_through_signed_multilocation_para_to_para_ethereum() { - MockNet::reset(); - let mut ancestry = Location::parent(); - - let para_b_location = Location::new(1, [Parachain(2)]); - - let para_b_balances = Location::new(1, [Parachain(2), PalletInstance(1u8)]); - - ParaA::execute_with(|| { - // Root can set transact info - assert_ok!(XcmTransactor::set_transact_info( - parachain::RuntimeOrigin::root(), - // ParaB - Box::new(xcm::VersionedLocation::from(para_b_location.clone())), - // Para charges 1000 for every instruction, and we have 3, so 3 - 3.into(), - 20000000000.into(), - // 4 instructions in transact through signed - Some(4.into()) - )); - // Root can set transact info - // Set fee per second using weight-trader (replaces old set_fee_per_second) - set_fee_per_second_for_location( - para_b_balances.clone(), - parachain::ParaTokensPerSecond::get(), - ) - .expect("must succeed"); - ancestry = parachain::UniversalLocation::get().into(); - }); - - // Let's construct the Junction that we will append with DescendOrigin - let signed_origin: Junctions = [AccountKey20 { - network: None, - key: PARAALICE, - }] - .into(); - - let mut descend_origin_location = parachain::SelfLocation::get(); - descend_origin_location.append_with(signed_origin).unwrap(); - - // To convert it to what the paraB will see instead of us - descend_origin_location - .reanchor(¶_b_location, &ancestry.interior) - .unwrap(); - - let derived = xcm_builder::HashedDescription::< - parachain::AccountId, - xcm_builder::DescribeFamily, - >::convert_location(&descend_origin_location) - .unwrap(); - - let mut parachain_b_alice_balances_before = 0; - ParaB::execute_with(|| { - assert_ok!(ParaBalances::transfer_allow_death( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - derived.clone(), - 4000000104u128, - )); - // derived account has all funds - assert!(ParaBalances::free_balance(&derived) == 4000000104); - // sovereign account has 0 funds - assert!(ParaBalances::free_balance(¶_a_account_20()) == 0); - - parachain_b_alice_balances_before = ParaBalances::free_balance(&PARAALICE.into()) - }); - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = - ::PalletInfo::index::() - .unwrap() as u8; - - encoded.push(index); - - use sp_core::U256; - // Let's do a EVM transfer - let eth_tx = - xcm_primitives::EthereumXcmTransaction::V1(xcm_primitives::EthereumXcmTransactionV1 { - gas_limit: U256::from(21000), - fee_payment: xcm_primitives::EthereumXcmFee::Auto, - action: pallet_ethereum::TransactionAction::Call(PARAALICE.into()), - value: U256::from(100), - input: BoundedVec::< - u8, - ConstU32<{ xcm_primitives::MAX_ETHEREUM_XCM_INPUT_SIZE }> - >::try_from(vec![]).unwrap(), - access_list: None, - }); - - // Then call bytes - let mut call_bytes = pallet_ethereum_xcm::Call::::transact { - xcm_transaction: eth_tx, - } - .encode(); - encoded.append(&mut call_bytes); - - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::transact_through_signed( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(xcm::VersionedLocation::from(para_b_location)), - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - para_b_balances - ))), - fee_amount: None - }, - encoded, - // 4000000000 for transfer + 4000 for XCM - // 1-1 to fee - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: None - }, - false - )); - }); - - ParaB::execute_with(|| { - // Make sure the EVM transfer went through - assert!( - ParaBalances::free_balance(&PARAALICE.into()) - == parachain_b_alice_balances_before + 100 - ); - }); -} - -#[test] -fn transact_through_signed_multilocation_para_to_para_ethereum_no_proxy_fails() { - MockNet::reset(); - let mut ancestry = Location::parent(); - - let para_b_location = Location::new(1, [Parachain(2)]); - - let para_b_balances = Location::new(1, [Parachain(2), PalletInstance(1u8)]); - - ParaA::execute_with(|| { - // Root can set transact info - assert_ok!(XcmTransactor::set_transact_info( - parachain::RuntimeOrigin::root(), - // ParaB - Box::new(xcm::VersionedLocation::from(para_b_location.clone())), - // Para charges 1000 for every instruction, and we have 3, so 3 - 3.into(), - 20000000000.into(), - // 4 instructions in transact through signed - Some(4.into()) - )); - // Root can set transact info - // Set fee per second using weight-trader (replaces old set_fee_per_second) - set_fee_per_second_for_location( - para_b_balances.clone(), - parachain::ParaTokensPerSecond::get(), - ) - .expect("must succeed"); - ancestry = parachain::UniversalLocation::get().into(); - }); - - // Let's construct the Junction that we will append with DescendOrigin - let signed_origin: Junctions = [AccountKey20 { - network: None, - key: PARAALICE, - }] - .into(); - - let mut descend_origin_location = parachain::SelfLocation::get(); - descend_origin_location.append_with(signed_origin).unwrap(); - - // To convert it to what the paraB will see instead of us - descend_origin_location - .reanchor(¶_b_location, &ancestry.interior) - .unwrap(); - - let derived = xcm_builder::HashedDescription::< - parachain::AccountId, - xcm_builder::DescribeFamily, - >::convert_location(&descend_origin_location) - .unwrap(); - - let mut parachain_b_alice_balances_before = 0; - ParaB::execute_with(|| { - assert_ok!(ParaBalances::transfer_allow_death( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - derived.clone(), - 4000000104u128, - )); - // derived account has all funds - assert!(ParaBalances::free_balance(&derived) == 4000000104); - // sovereign account has 0 funds - assert!(ParaBalances::free_balance(¶_a_account_20()) == 0); - - parachain_b_alice_balances_before = ParaBalances::free_balance(&PARAALICE.into()) - }); - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = - ::PalletInfo::index::() - .unwrap() as u8; - - encoded.push(index); - - use sp_core::U256; - // Let's do a EVM transfer - let eth_tx = - xcm_primitives::EthereumXcmTransaction::V1(xcm_primitives::EthereumXcmTransactionV1 { - gas_limit: U256::from(21000), - fee_payment: xcm_primitives::EthereumXcmFee::Auto, - action: pallet_ethereum::TransactionAction::Call(PARAALICE.into()), - value: U256::from(100), - input: BoundedVec::< - u8, - ConstU32<{ xcm_primitives::MAX_ETHEREUM_XCM_INPUT_SIZE }> - >::try_from(vec![]).unwrap(), - access_list: None, - }); - - // Then call bytes - let mut call_bytes = pallet_ethereum_xcm::Call::::transact_through_proxy { - transact_as: PARAALICE.into(), - xcm_transaction: eth_tx, - } - .encode(); - encoded.append(&mut call_bytes); - - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::transact_through_signed( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(xcm::VersionedLocation::from(para_b_location)), - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - para_b_balances - ))), - fee_amount: None - }, - encoded, - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: None - }, - false - )); - }); - - ParaB::execute_with(|| { - // Make sure the EVM transfer wasn't executed - assert!(ParaBalances::free_balance(&PARAALICE.into()) == parachain_b_alice_balances_before); - }); -} - -#[test] -fn transact_through_signed_multilocation_para_to_para_ethereum_proxy_succeeds() { - MockNet::reset(); - let mut ancestry = Location::parent(); - - let para_b_location = Location::new(1, [Parachain(2)]); - - let para_b_balances = Location::new(1, [Parachain(2), PalletInstance(1u8)]); - - ParaA::execute_with(|| { - // Root can set transact info - assert_ok!(XcmTransactor::set_transact_info( - parachain::RuntimeOrigin::root(), - // ParaB - Box::new(xcm::VersionedLocation::from(para_b_location.clone())), - // Para charges 1000 for every instruction, and we have 3, so 3 - 3.into(), - 20000000000.into(), - // 4 instructions in transact through signed - Some(4.into()) - )); - // Root can set transact info - // Set fee per second using weight-trader (replaces old set_fee_per_second) - set_fee_per_second_for_location( - para_b_balances.clone(), - parachain::ParaTokensPerSecond::get(), - ) - .expect("must succeed"); - ancestry = parachain::UniversalLocation::get().into(); - }); - - // Let's construct the Junction that we will append with DescendOrigin - let signed_origin: Junctions = [AccountKey20 { - network: None, - key: PARAALICE, - }] - .into(); - - let mut descend_origin_location = parachain::SelfLocation::get(); - descend_origin_location.append_with(signed_origin).unwrap(); - - // To convert it to what the paraB will see instead of us - descend_origin_location - .reanchor(¶_b_location, &ancestry.interior) - .unwrap(); - - let derived = xcm_builder::HashedDescription::< - parachain::AccountId, - xcm_builder::DescribeFamily, - >::convert_location(&descend_origin_location) - .unwrap(); - - let transfer_recipient = evm_account(); - let mut transfer_recipient_balance_before = 0; - ParaB::execute_with(|| { - assert_ok!(ParaBalances::transfer_allow_death( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - derived.clone(), - 4000000104u128, - )); - // derived account has all funds - assert!(ParaBalances::free_balance(&derived) == 4000000104); - // sovereign account has 0 funds - assert!(ParaBalances::free_balance(¶_a_account_20()) == 0); - - transfer_recipient_balance_before = ParaBalances::free_balance(&transfer_recipient.into()); - - // Add proxy ALICE -> derived - let _ = parachain::Proxy::add_proxy_delegate( - &PARAALICE.into(), - derived, - parachain::ProxyType::Any, - 0, - ); - }); - - // Encode the call. Balances transact to para_a_account - // First index - let mut encoded: Vec = Vec::new(); - let index = - ::PalletInfo::index::() - .unwrap() as u8; - - encoded.push(index); - - use sp_core::U256; - // Let's do a EVM transfer - let eth_tx = - xcm_primitives::EthereumXcmTransaction::V2(xcm_primitives::EthereumXcmTransactionV2 { - gas_limit: U256::from(21000), - action: pallet_ethereum::TransactionAction::Call(transfer_recipient.into()), - value: U256::from(100), - input: BoundedVec::< - u8, - ConstU32<{ xcm_primitives::MAX_ETHEREUM_XCM_INPUT_SIZE }> - >::try_from(vec![]).unwrap(), - access_list: None, - }); - - // Then call bytes - let mut call_bytes = pallet_ethereum_xcm::Call::::transact_through_proxy { - transact_as: PARAALICE.into(), - xcm_transaction: eth_tx, - } - .encode(); - encoded.append(&mut call_bytes); - - ParaA::execute_with(|| { - assert_ok!(XcmTransactor::transact_through_signed( - parachain::RuntimeOrigin::signed(PARAALICE.into()), - Box::new(xcm::VersionedLocation::from(para_b_location)), - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - para_b_balances - ))), - fee_amount: None - }, - encoded, - TransactWeights { - transact_required_weight_at_most: 4000000000.into(), - overall_weight: None - }, - false - )); - }); - - ParaB::execute_with(|| { - // Make sure the EVM transfer was executed - assert!( - ParaBalances::free_balance(&transfer_recipient.into()) - == transfer_recipient_balance_before + 100 - ); - }); -} - -#[test] -fn hrmp_init_accept_through_root() { - MockNet::reset(); - - Relay::execute_with(|| { - assert_ok!(RelayBalances::transfer_allow_death( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - para_a_account(), - 1000u128 - )); - assert_ok!(RelayBalances::transfer_allow_death( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - para_b_account(), - 1000u128 - )); - }); - - ParaA::execute_with(|| { - let total_fee = 1_000u128; - let total_weight: u64 = 1_000_000_000; - let tx_weight: u64 = 500_000_000; - // Root can send hrmp init channel - assert_ok!(XcmTransactor::hrmp_manage( - parachain::RuntimeOrigin::root(), - HrmpOperation::InitOpen(HrmpInitParams { - para_id: 2u32.into(), - proposed_max_capacity: 1, - proposed_max_message_size: 1 - }), - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - Location::parent() - ))), - fee_amount: Some(total_fee) - }, - TransactWeights { - transact_required_weight_at_most: tx_weight.into(), - overall_weight: Some(Limited(total_weight.into())) - } - )); - }); - Relay::execute_with(|| { - let expected_event: relay_chain::RuntimeEvent = - polkadot_runtime_parachains::hrmp::Event::OpenChannelRequested { - sender: 1u32.into(), - recipient: 2u32.into(), - proposed_max_capacity: 1u32, - proposed_max_message_size: 1u32, - } - .into(); - assert!(relay_chain::relay_events().contains(&expected_event)); - }); - ParaB::execute_with(|| { - let total_fee = 1_000u128; - let total_weight: u64 = 1_000_000_000; - let tx_weight: u64 = 500_000_000; - // Root can send hrmp accept channel - assert_ok!(XcmTransactor::hrmp_manage( - parachain::RuntimeOrigin::root(), - HrmpOperation::Accept { - para_id: 1u32.into() - }, - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - Location::parent() - ))), - fee_amount: Some(total_fee) - }, - TransactWeights { - transact_required_weight_at_most: tx_weight.into(), - overall_weight: Some(Limited(total_weight.into())) - } - )); - }); - - Relay::execute_with(|| { - let expected_event: relay_chain::RuntimeEvent = - polkadot_runtime_parachains::hrmp::Event::OpenChannelAccepted { - sender: 1u32.into(), - recipient: 2u32.into(), - } - .into(); - assert!(relay_chain::relay_events().contains(&expected_event)); - }); -} - -#[test] -fn hrmp_close_works() { - MockNet::reset(); - - Relay::execute_with(|| { - assert_ok!(RelayBalances::transfer_allow_death( - relay_chain::RuntimeOrigin::signed(RELAYALICE), - para_a_account(), - 1000u128 - )); - assert_ok!(Hrmp::force_open_hrmp_channel( - relay_chain::RuntimeOrigin::root(), - 1u32.into(), - 2u32.into(), - 1u32, - 1u32 - )); - assert_ok!(Hrmp::force_process_hrmp_open( - relay_chain::RuntimeOrigin::root(), - 1u32 - )); - }); - - ParaA::execute_with(|| { - let total_fee = 1_000u128; - let total_weight: u64 = 1_000_000_000; - let tx_weight: u64 = 500_000_000; - // Root can send hrmp close - assert_ok!(XcmTransactor::hrmp_manage( - parachain::RuntimeOrigin::root(), - HrmpOperation::Close(HrmpChannelId { - sender: 1u32.into(), - recipient: 2u32.into() - }), - CurrencyPayment { - currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from( - Location::parent() - ))), - fee_amount: Some(total_fee) - }, - TransactWeights { - transact_required_weight_at_most: tx_weight.into(), - overall_weight: Some(Limited(total_weight.into())) - } - )); - }); - Relay::execute_with(|| { - let expected_event: relay_chain::RuntimeEvent = - polkadot_runtime_parachains::hrmp::Event::ChannelClosed { - by_parachain: 1u32.into(), - channel_id: HrmpChannelId { - sender: 1u32.into(), - recipient: 2u32.into(), - }, - } - .into(); - assert!(relay_chain::relay_events().contains(&expected_event)); - }); -} - -use crate::xcm_mock::parachain::XcmWeightTrader; -use parity_scale_codec::{Decode, Encode}; -use sp_io::hashing::blake2_256; - -// Helper to derive accountIds -pub fn derivative_account_id(who: sp_runtime::AccountId32, index: u16) -> sp_runtime::AccountId32 { - let entropy = (b"modlpy/utilisuba", who, index).using_encoded(blake2_256); - sp_runtime::AccountId32::decode(&mut &entropy[..]).expect("valid account id") -} diff --git a/scripts/verify-licenses.sh b/scripts/verify-licenses.sh index 4c9ce33094e..b2c825d4382 100755 --- a/scripts/verify-licenses.sh +++ b/scripts/verify-licenses.sh @@ -29,6 +29,7 @@ LICENSES=( "CC0-1.0" "GPL-3.0-only" "GPL-3.0-or-later WITH Classpath-exception-2.0" + "LGPL-3.0-or-later" "ISC" "MIT OR Unlicense" "MIT"