├── .github └── workflows │ └── mdbook.yml ├── .gitignore ├── README.md ├── book.toml ├── examples ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── rustfmt.toml └── src │ ├── 0_first_look │ └── mod.rs │ ├── 10_locks │ └── mod.rs │ ├── 1_transfers │ ├── mod.rs │ ├── reserve.rs │ └── teleport.rs │ ├── 2_fees │ └── mod.rs │ ├── 3_transact │ └── mod.rs │ ├── 4_origins │ └── mod.rs │ ├── 5_holding_modifiers │ └── mod.rs │ ├── 6_trap_and_claim │ └── mod.rs │ ├── 7_expects │ └── mod.rs │ ├── 8_queries │ └── mod.rs │ ├── 9_version_subscription │ └── mod.rs │ ├── lib.rs │ └── simple_test_net │ ├── asset_hub.rs │ ├── mock_msg_queue.rs │ ├── mod.rs │ ├── parachain.rs │ └── relay_chain.rs └── src ├── SUMMARY.md ├── executor_config ├── README.md └── images │ └── executor.png ├── fundamentals ├── README.md ├── images │ ├── MultiLocation_Example.png │ └── MultiLocation_simple_example.png ├── multiasset.md ├── multilocation │ ├── README.md │ ├── example.md │ └── junction.md ├── weight_and_fees.md └── xcvm.md ├── journey ├── README.md ├── channels-and-bridges.md ├── expects.md ├── fees │ └── README.md ├── holding-modifiers.md ├── locks │ ├── images │ │ ├── Example1.png │ │ └── Example2.png │ └── locks.md ├── origins.md ├── queries.md ├── register-modifiers.md ├── transact.md ├── transfers │ ├── README.md │ ├── images │ │ ├── asset_teleportation.png │ │ ├── reserve_asset_transfer.png │ │ └── source_is_reserve.png │ ├── reserve.md │ └── teleports.md ├── trap-and-claim.md └── version.md ├── overview ├── README.md ├── architecture.md ├── format.md ├── interoperability.md └── xcvm.md ├── quickstart ├── README.md ├── first-look.md └── xcm-simulator.md ├── reference ├── glossary.md └── xcvm-registers.md ├── testing └── README.md └── xcm.md /.github/workflows/mdbook.yml: -------------------------------------------------------------------------------- 1 | # Sample workflow for building and deploying a mdBook site to GitHub Pages 2 | # 3 | # To get started with mdBook see: https://rust-lang.github.io/mdBook/index.html 4 | # 5 | name: Deploy mdBook site to Pages 6 | 7 | on: 8 | # Runs on pushes targeting the default branch 9 | push: 10 | branches: ["main"] 11 | 12 | # Allows you to run this workflow manually from the Actions tab 13 | workflow_dispatch: 14 | 15 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 16 | permissions: 17 | contents: read 18 | pages: write 19 | id-token: write 20 | 21 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 22 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 23 | concurrency: 24 | group: "pages" 25 | cancel-in-progress: false 26 | 27 | jobs: 28 | # Build job 29 | build: 30 | runs-on: ubuntu-latest 31 | env: 32 | MDBOOK_VERSION: 0.4.21 33 | steps: 34 | - uses: actions/checkout@v3 35 | - name: Install mdBook 36 | run: | 37 | curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf -y | sh 38 | rustup update 39 | cargo install --version ${MDBOOK_VERSION} mdbook 40 | - name: Setup Pages 41 | id: pages 42 | uses: actions/configure-pages@v3 43 | - name: Build with mdBook 44 | run: mdbook build 45 | - name: Upload artifact 46 | uses: actions/upload-pages-artifact@v1 47 | with: 48 | path: ./book 49 | 50 | # Deployment job 51 | deploy: 52 | environment: 53 | name: github-pages 54 | url: ${{ steps.deployment.outputs.page_url }} 55 | runs-on: ubuntu-latest 56 | needs: build 57 | steps: 58 | - name: Deploy to GitHub Pages 59 | id: deployment 60 | uses: actions/deploy-pages@v2 61 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | book 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # XCM Docs - DEPRECATED 2 | 3 | The documentation for XCM has moved to [polkadot-sdk](https://github.com/paritytech/polkadot-sdk/) rust docs. 4 | You can find an online rendered version [here](https://paritytech.github.io/polkadot-sdk/master/xcm_docs/index.html). 5 | -------------------------------------------------------------------------------- /book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["Francisco Aguirre"] 3 | language = "en" 4 | multilingual = false 5 | src = "src" 6 | title = "XCM Documentation" 7 | -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | 13 | # Added by cargo 14 | 15 | /target 16 | /Cargo.lock 17 | -------------------------------------------------------------------------------- /examples/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xcm-examples" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["Xcm Team"] 6 | 7 | 8 | [dependencies] 9 | bounded-collections = { version = "0.1.5", default-features = false } 10 | smallvec = "1.10.0" 11 | codec = { package = "parity-scale-codec", version = "3.5.0", features = ["derive", "max-encoded-len"] } 12 | scale-info = { version = "2.7.0", default-features = false, features = ["derive"] } 13 | serde_json = { version = "1.0.96" } 14 | hex = { version = "0.4" } 15 | hex-literal = { version = "0.3.1" } 16 | libsecp256k1 = { version = "0.7" } 17 | 18 | #Polkadot 19 | polkadot-parachain = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.43" } 20 | xcm = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.43" } 21 | xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.43" } 22 | xcm-builder = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.43" } 23 | pallet-xcm = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.43" } 24 | pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } 25 | pallet-assets = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } 26 | pallet-uniques = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } 27 | pallet-nfts = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } 28 | pallet-message-queue = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } 29 | polkadot-primitives = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.43" } 30 | polkadot-runtime-parachains = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.43" } 31 | kusama-runtime = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.43" } 32 | kusama-runtime-constants = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.43" } 33 | xcm-simulator = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.43" } 34 | # substrate 35 | frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } 36 | frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } 37 | sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } 38 | sp-application-crypto = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } 39 | sp-block-builder = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } 40 | sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } 41 | sp-inherents = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } 42 | sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } 43 | sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } 44 | sp-session = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } 45 | sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } 46 | sp-version = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } 47 | sp-keyring = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } 48 | 49 | cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.43" } 50 | cumulus-primitives-utility = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.43" } 51 | cumulus-primitives-timestamp = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.43" } 52 | cumulus-pallet-parachain-system = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.43" } 53 | cumulus-pallet-dmp-queue = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.43" } 54 | cumulus-pallet-xcmp-queue = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.43" } 55 | cumulus-pallet-xcm = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.43" } 56 | parachain-info = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.43" } 57 | cumulus-primitives-parachain-inherent = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.43" } 58 | cumulus-test-relay-sproof-builder = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.43" } 59 | statemine-runtime = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.43" } 60 | 61 | [dev-dependencies] 62 | env_logger = "0.9.0" 63 | log = "0.4.17" 64 | sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } 65 | sp-trie = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } 66 | 67 | 68 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # xcm-examples 2 | This repository contains the xcm examples for the xcm docs. 3 | The examples are set up using the [XCM-simulator](https://github.com/paritytech/polkadot/tree/master/xcm/xcm-simulator). 4 | The testnet can be found in `examples/src/simple_test_net`. 5 | 6 | #### How to run 7 | To run the examples, do the following: 8 | 1. Clone the repository: 9 | `git clone https://github.com/paritytech/xcm-docs.git` 10 | 11 | 2. cd to the examples folder: 12 | `cd examples/` 13 | 14 | 3. Run all the tests: 15 | `cargo test` 16 | or a single test: 17 | `cargo test -p xcm-examples trap_and_claim_assets -- --nocapture` 18 | 19 | #### events printing 20 | You can print out the events on a parachain or the relay chain using the `print_para_events` or `print_relay_events` functions. The functions are used in a parachain or relay chain `TestExternalities`: 21 | 22 | ```rust 23 | ParaA::execute_with(|| { 24 | print_para_events(); 25 | }); 26 | ``` 27 | 28 | #### Tests 29 | - `first_look` 30 | - `transfers/teleport_fungible` 31 | - `transfers/reserve_backed_transfer_para_to_para` 32 | - `transfers/reserve_backed_transfer_relay_to_para` 33 | - `transfers/reserve_backed_transfer_para_to_relay` 34 | - `transact/transact_set_balance` 35 | - `transact/transact_mint_nft` 36 | - `origins/descend_origin` 37 | - `holding_modifiers/burn_assets` 38 | - `holding_modifiers/exchange_asset_maximal_true` 39 | - `holding_modifiers/exchange_asset_maximal_false` 40 | - `trap_and_claim/trap_and_claim_assets` 41 | - `expects/expect_asset` 42 | - `expects/expect_origin` 43 | - `expects/expect_pallet` 44 | - `expects/expect_error` 45 | - `expects/expect_transact_status` 46 | - `queries/query_holding` 47 | - `queries/query_pallet` 48 | - `queries/report_error` 49 | - `queries/report_transact_status` 50 | - `version_subscription/subscribe_and_unsubscribe_version` 51 | - `locks/remote_locking_on_relay` 52 | - `locks/locking_overlap` 53 | -------------------------------------------------------------------------------- /examples/rustfmt.toml: -------------------------------------------------------------------------------- 1 | # Basic 2 | hard_tabs = true 3 | max_width = 100 4 | use_small_heuristics = "Max" 5 | # Imports 6 | imports_granularity = "Crate" 7 | reorder_imports = true 8 | # Consistency 9 | newline_style = "Unix" 10 | # Misc 11 | chain_width = 80 12 | spaces_around_ranges = false 13 | binop_separator = "Back" 14 | reorder_impl_items = false 15 | match_arm_leading_pipes = "Preserve" 16 | match_arm_blocks = false 17 | match_block_trailing_comma = true 18 | trailing_comma = "Vertical" 19 | trailing_semicolon = false 20 | use_field_init_shorthand = true 21 | edition = "2021" -------------------------------------------------------------------------------- /examples/src/0_first_look/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | // use crate::test_net::kusama_test_net::*; 4 | use crate::simple_test_net::*; 5 | use frame_support::assert_ok; 6 | pub use xcm::latest::prelude::*; 7 | use xcm_simulator::TestExt; 8 | 9 | #[test] 10 | fn para_a_simple_transfer() { 11 | MockNet::reset(); 12 | 13 | ParaA::execute_with(|| { 14 | // Amount to transfer. 15 | let amount: u128 = 10 * CENTS; 16 | // Check that the balance of Alice is equal to the `INITIAL_BALANCE`. 17 | assert_eq!(ParachainBalances::free_balance(&ALICE), INITIAL_BALANCE); 18 | 19 | let fee = parachain::estimate_message_fee(3); 20 | 21 | // The XCM used to transfer funds from Alice to Bob. 22 | let message = Xcm(vec![ 23 | WithdrawAsset(vec![(Here, amount).into(), (Parent, fee).into()].into()), 24 | BuyExecution { fees: (Parent, fee).into(), weight_limit: WeightLimit::Unlimited }, 25 | DepositAsset { 26 | assets: All.into(), 27 | beneficiary: MultiLocation { 28 | parents: 0, 29 | interior: Junction::AccountId32 { network: None, id: BOB.clone().into() } 30 | .into(), 31 | } 32 | .into(), 33 | }, 34 | ]); 35 | 36 | // Execution of the XCM Instructions in the local consensus system. 37 | // The RuntimeOrigin is Alice, so Alice's account will be used for the WithdrawAsset. 38 | assert_ok!(ParachainPalletXcm::execute( 39 | parachain::RuntimeOrigin::signed(ALICE), 40 | Box::new(xcm::VersionedXcm::from(message.clone())), 41 | (100_000_000_000, 100_000_000_000).into() 42 | )); 43 | 44 | // Check if the funds are subtracted from the account of Alice and added to the account of Bob. 45 | assert_eq!(ParachainBalances::free_balance(ALICE), INITIAL_BALANCE - amount); 46 | assert_eq!(parachain::Assets::balance(0, ALICE), INITIAL_BALANCE - fee); 47 | assert_eq!(ParachainBalances::free_balance(BOB), INITIAL_BALANCE + amount); 48 | }); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /examples/src/10_locks/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use crate::simple_test_net::*; 4 | use frame_support::assert_ok; 5 | use pallet_balances::{BalanceLock, Reasons}; 6 | use xcm::latest::prelude::*; 7 | use xcm_simulator::TestExt; 8 | 9 | /// Scenario: 10 | /// ALICE from parachain A locks 5 cents of relay chain native assets of its Sovereign account on the relay chain and assigns Parachain B as unlocker. 11 | /// Parachain A then asks Parachain B to unlock the funds partly. Parachain B responds by sending an UnlockAssets instruction to the relay chain. 12 | #[ignore] // TODO: Fix issue upstream 13 | #[test] 14 | fn remote_locking_on_relay() { 15 | MockNet::reset(); 16 | 17 | let fee = relay_chain::estimate_message_fee(4); // Accounts for the `DescendOrigin` instruction added by `send_xcm` 18 | 19 | ParaA::execute_with(|| { 20 | let message = Xcm(vec![ 21 | WithdrawAsset((Here, fee).into()), 22 | BuyExecution { fees: (Here, fee).into(), weight_limit: WeightLimit::Unlimited }, 23 | LockAsset { asset: (Here, 5 * CENTS).into(), unlocker: (Parachain(2)).into() }, 24 | ]); 25 | let interior = AccountId32 { id: ALICE.into(), network: None }; 26 | assert_ok!(ParachainPalletXcm::send_xcm(interior, Parent, message.clone())); 27 | }); 28 | 29 | Relay::execute_with(|| { 30 | assert_eq!( 31 | relay_chain::Balances::locks(¶chain_account_sovereign_account_id(1, ALICE)), 32 | vec![BalanceLock { id: *b"py/xcmlk", amount: 5 * CENTS, reasons: Reasons::All }] 33 | ); 34 | }); 35 | 36 | ParaB::execute_with(|| { 37 | assert_eq!( 38 | parachain::MsgQueue::received_dmp(), 39 | vec![Xcm(vec![NoteUnlockable { 40 | owner: (Parent, Parachain(1), AccountId32 { id: ALICE.into(), network: None }) 41 | .into(), 42 | asset: (Parent, 5 * CENTS).into() 43 | }])] 44 | ); 45 | }); 46 | 47 | ParaA::execute_with(|| { 48 | let message = Xcm(vec![ 49 | WithdrawAsset((Parent, fee).into()), 50 | BuyExecution { fees: (Parent, fee).into(), weight_limit: WeightLimit::Unlimited }, 51 | RequestUnlock { asset: (Parent, 3 * CENTS).into(), locker: Parent.into() }, 52 | ]); 53 | let interior = AccountId32 { id: ALICE.into(), network: None }; 54 | assert_ok!(ParachainPalletXcm::send_xcm( 55 | interior, 56 | (Parent, Parachain(2)), 57 | message.clone() 58 | )); 59 | }); 60 | 61 | Relay::execute_with(|| { 62 | assert_eq!( 63 | relay_chain::Balances::locks(¶chain_account_sovereign_account_id(1, ALICE)), 64 | vec![BalanceLock { id: *b"py/xcmlk", amount: 2 * CENTS, reasons: Reasons::All }] 65 | ); 66 | }); 67 | } 68 | 69 | /// Scenario: 70 | /// Parachain A sets two locks with Parachain B and Parachain C as unlockers on the relay chain. 71 | /// Parachain A then requests Parachain B to partly unlock. 72 | /// Note: The locks overlap. 73 | /// Steps: 74 | /// 1) Set locks on the relay chain. 75 | /// Unlockers: B, C; Funds registered in pallet-xcm: 10, 5. 76 | /// Lock set in pallet-balances: 10. 77 | /// 2) Parachain B and C receive `NoteUnlockable` instruction. 78 | /// 3) Parachain A sends an `RequestUnlock` instruction to Parachain B for 8 Cents. 79 | /// 4) Parachain B Unlocks a part of the funds by sending a `UnlockAsset` instruction to the relay chain. 80 | /// Unlockers: B, C; Funds registered in pallet-xcm: 2, 5. 81 | /// Lock set in pallet-balances: 5. 82 | /// 83 | #[ignore] // TODO: Fix issue upstream 84 | #[test] 85 | fn locking_overlap() { 86 | MockNet::reset(); 87 | 88 | let fee = relay_chain::estimate_message_fee(4); 89 | 90 | // 1) 91 | ParaA::execute_with(|| { 92 | let message = Xcm(vec![ 93 | WithdrawAsset((Here, fee).into()), 94 | BuyExecution { fees: (Here, fee).into(), weight_limit: WeightLimit::Unlimited }, 95 | LockAsset { asset: (Here, 10 * CENTS).into(), unlocker: (Parachain(2)).into() }, 96 | LockAsset { asset: (Here, 5 * CENTS).into(), unlocker: (Parachain(3)).into() }, 97 | ]); 98 | let interior = AccountId32 { id: ALICE.into(), network: None }; 99 | assert_ok!(ParachainPalletXcm::send_xcm(interior, Parent, message.clone())); 100 | }); 101 | 102 | Relay::execute_with(|| { 103 | assert_eq!( 104 | relay_chain::Balances::locks(¶chain_sovereign_account_id(1)), 105 | vec![BalanceLock { id: *b"py/xcmlk", amount: 10 * CENTS, reasons: Reasons::All }] 106 | ); 107 | }); 108 | 109 | // 2) 110 | ParaB::execute_with(|| { 111 | assert_eq!( 112 | parachain::MsgQueue::received_dmp(), 113 | vec![Xcm(vec![NoteUnlockable { 114 | owner: (Parent, Parachain(1), AccountId32 { id: ALICE.into(), network: None }) 115 | .into(), 116 | asset: (Parent, 10 * CENTS).into() 117 | }])] 118 | ); 119 | }); 120 | 121 | ParaC::execute_with(|| { 122 | assert_eq!( 123 | parachain::MsgQueue::received_dmp(), 124 | vec![Xcm(vec![NoteUnlockable { 125 | owner: (Parent, Parachain(1), AccountId32 { id: ALICE.into(), network: None }) 126 | .into(), 127 | asset: (Parent, 5 * CENTS).into() 128 | }])] 129 | ); 130 | }); 131 | 132 | let new_fee = parachain::estimate_message_fee(3); 133 | 134 | // 3) 135 | ParaA::execute_with(|| { 136 | let message = Xcm(vec![ 137 | WithdrawAsset((Parent, new_fee).into()), 138 | BuyExecution { 139 | fees: (Parent, new_fee).into(), 140 | weight_limit: WeightLimit::Unlimited, 141 | }, 142 | RequestUnlock { asset: (Parent, 8 * CENTS).into(), locker: Parent.into() }, 143 | ]); 144 | let interior = AccountId32 { id: ALICE.into(), network: None }; 145 | assert_ok!(ParachainPalletXcm::send_xcm( 146 | interior, 147 | (Parent, Parachain(2)), 148 | message.clone() 149 | )); 150 | }); 151 | 152 | // 4) 153 | Relay::execute_with(|| { 154 | assert_eq!( 155 | relay_chain::Balances::locks(¶chain_sovereign_account_id(1)), 156 | vec![BalanceLock { id: *b"py/xcmlk", amount: 5 * CENTS, reasons: Reasons::All }] 157 | ); 158 | }); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /examples/src/1_transfers/mod.rs: -------------------------------------------------------------------------------- 1 | mod reserve; 2 | mod teleport; 3 | -------------------------------------------------------------------------------- /examples/src/1_transfers/reserve.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use crate::simple_test_net::*; 4 | use frame_support::assert_ok; 5 | use xcm::latest::prelude::*; 6 | use xcm_simulator::TestExt; 7 | 8 | /// Scenario: 9 | /// ALICE transfers relay native tokens from parachain A to parachain B. 10 | #[test] 11 | fn reserve_backed_transfer_para_to_para() { 12 | MockNet::reset(); 13 | 14 | let withdraw_amount = 50 * CENTS; 15 | 16 | // Estimated from the number of instructions and knowledge of the config 17 | let fee_in_source = parachain::estimate_message_fee(3); 18 | let fee_in_relay = relay_chain::estimate_message_fee(4); 19 | let fee_in_destination = parachain::estimate_message_fee(4); 20 | 21 | // In this case, we know exactly how much fees we need for each step of the process 22 | let message: Xcm = Xcm(vec![ 23 | WithdrawAsset((Parent, withdraw_amount).into()), // Fees are paid in the relay's token 24 | BuyExecution { 25 | fees: (Parent, fee_in_source).into(), 26 | weight_limit: WeightLimit::Unlimited, 27 | }, 28 | InitiateReserveWithdraw { 29 | assets: All.into(), 30 | reserve: Parent.into(), 31 | xcm: Xcm(vec![ 32 | BuyExecution { 33 | fees: (Here, fee_in_relay).into(), 34 | weight_limit: WeightLimit::Unlimited, 35 | }, 36 | DepositReserveAsset { 37 | assets: All.into(), 38 | dest: Parachain(2).into(), 39 | xcm: Xcm(vec![ 40 | BuyExecution { 41 | fees: (Parent, fee_in_destination).into(), 42 | weight_limit: WeightLimit::Unlimited, 43 | }, 44 | DepositAsset { 45 | assets: All.into(), 46 | beneficiary: Junction::AccountId32 { 47 | id: ALICE.into(), 48 | network: None, 49 | } 50 | .into(), 51 | }, 52 | ]), 53 | }, 54 | ]), 55 | }, 56 | ]); 57 | 58 | let fee_until_relay = fee_in_source + fee_in_relay; 59 | let fee_until_destination = fee_until_relay + fee_in_destination; 60 | 61 | ParaA::execute_with(|| { 62 | assert_ok!(parachain::PolkadotXcm::execute( 63 | parachain::RuntimeOrigin::signed(ALICE), 64 | Box::new(xcm::VersionedXcm::V3(message.into())), 65 | (100_000_000_000, 100_000_000_000).into(), 66 | )); 67 | 68 | assert_eq!(parachain::Assets::balance(0, &ALICE), INITIAL_BALANCE - withdraw_amount); 69 | }); 70 | 71 | Relay::execute_with(|| { 72 | assert_eq!( 73 | relay_chain::Balances::free_balance(¶chain_sovereign_account_id(2)), 74 | INITIAL_BALANCE + withdraw_amount - fee_until_relay 75 | ); 76 | }); 77 | 78 | ParaB::execute_with(|| { 79 | assert_eq!( 80 | parachain::Assets::balance(0, &ALICE), 81 | INITIAL_BALANCE + withdraw_amount - fee_until_destination 82 | ); 83 | }); 84 | } 85 | 86 | /// Scenario: 87 | /// ALICE transfers relay native tokens from relay to parachain B. 88 | #[test] 89 | fn reserve_backed_transfer_relay_to_para() { 90 | MockNet::reset(); 91 | 92 | let withdraw_amount = 50 * CENTS; 93 | 94 | let fee_in_source = relay_chain::estimate_message_fee(3); 95 | let fee_in_destination = parachain::estimate_message_fee(4); 96 | 97 | let message: Xcm = Xcm(vec![ 98 | WithdrawAsset((Here, fee_in_source).into()), 99 | BuyExecution { 100 | fees: (Here, fee_in_source).into(), 101 | weight_limit: WeightLimit::Unlimited, 102 | }, 103 | TransferReserveAsset { 104 | assets: (Here, withdraw_amount).into(), 105 | dest: Parachain(2).into(), 106 | xcm: Xcm(vec![ 107 | BuyExecution { 108 | fees: (Parent, fee_in_destination).into(), 109 | weight_limit: WeightLimit::Unlimited, 110 | }, 111 | DepositAsset { 112 | assets: All.into(), 113 | beneficiary: Junction::AccountId32 { id: ALICE.into(), network: None } 114 | .into(), 115 | }, 116 | ]), 117 | }, 118 | ]); 119 | 120 | Relay::execute_with(|| { 121 | assert_ok!(relay_chain::XcmPallet::execute( 122 | relay_chain::RuntimeOrigin::signed(ALICE), 123 | Box::new(xcm::VersionedXcm::V3(message.into())), 124 | (100_000_000_000, 100_000_000_000).into(), 125 | )); 126 | 127 | // ALICE's balance in the relay chain decreases 128 | assert_eq!( 129 | relay_chain::Balances::free_balance(&ALICE), 130 | INITIAL_BALANCE - withdraw_amount - fee_in_source 131 | ); 132 | 133 | // Parachain(2)'s sovereign account's balance increases 134 | assert_eq!( 135 | relay_chain::Balances::free_balance(¶chain_sovereign_account_id(2)), 136 | INITIAL_BALANCE + withdraw_amount 137 | ); 138 | }); 139 | 140 | ParaB::execute_with(|| { 141 | assert_eq!( 142 | parachain::Assets::balance(0, &ALICE), 143 | INITIAL_BALANCE + (withdraw_amount - fee_in_destination) 144 | ); 145 | }); 146 | } 147 | 148 | #[test] 149 | fn reserve_backed_transfer_para_to_relay() { 150 | MockNet::reset(); 151 | 152 | let withdraw_amount = 50 * CENTS; 153 | 154 | let fee_in_source = parachain::estimate_message_fee(3); 155 | let fee_in_destination = relay_chain::estimate_message_fee(4); 156 | 157 | let message: Xcm = Xcm(vec![ 158 | WithdrawAsset((Parent, withdraw_amount).into()), 159 | BuyExecution { 160 | fees: (Parent, fee_in_source).into(), 161 | weight_limit: WeightLimit::Unlimited, 162 | }, 163 | InitiateReserveWithdraw { 164 | assets: All.into(), 165 | reserve: Parent.into(), 166 | xcm: Xcm(vec![ 167 | BuyExecution { 168 | fees: (Here, fee_in_destination).into(), 169 | weight_limit: WeightLimit::Unlimited, 170 | }, 171 | DepositAsset { 172 | assets: All.into(), 173 | beneficiary: Junction::AccountId32 { id: ALICE.into(), network: None } 174 | .into(), 175 | }, 176 | ]), 177 | }, 178 | ]); 179 | 180 | ParaA::execute_with(|| { 181 | assert_ok!(parachain::PolkadotXcm::execute( 182 | parachain::RuntimeOrigin::signed(ALICE), 183 | Box::new(xcm::VersionedXcm::V3(message.into())), 184 | (100_000_000_000, 100_000_000_000).into(), 185 | )); 186 | 187 | // ALICE's balance in the parachain decreases 188 | assert_eq!(parachain::Assets::balance(0, &ALICE), INITIAL_BALANCE - withdraw_amount); 189 | }); 190 | 191 | Relay::execute_with(|| { 192 | // Parachain(1)'s sovereign account balance decreases 193 | assert_eq!( 194 | relay_chain::Balances::free_balance(parachain_sovereign_account_id(1)), 195 | INITIAL_BALANCE - (withdraw_amount - fee_in_source) 196 | ); 197 | 198 | // ALICE's balance in the relay chain increases 199 | assert_eq!( 200 | relay_chain::Balances::free_balance(&ALICE), 201 | INITIAL_BALANCE + (withdraw_amount - fee_in_source - fee_in_destination) 202 | ); 203 | }); 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /examples/src/1_transfers/teleport.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use crate::simple_test_net::*; 4 | use frame_support::assert_ok; 5 | use xcm::latest::prelude::*; 6 | use xcm_simulator::TestExt; 7 | 8 | /// Scenario: 9 | /// ALICE teleports her native assets from the relay chain to parachain A. 10 | #[test] 11 | fn teleport_fungible() { 12 | MockNet::reset(); 13 | 14 | let withdraw_amount = 50 * CENTS; 15 | 16 | let fee_in_source = relay_chain::estimate_message_fee(3); 17 | let fee_in_destination = parachain::estimate_message_fee(4); 18 | 19 | let message: Xcm = Xcm(vec![ 20 | WithdrawAsset((Here, withdraw_amount).into()), 21 | BuyExecution { 22 | fees: (Here, fee_in_source).into(), 23 | weight_limit: WeightLimit::Unlimited, 24 | }, 25 | InitiateTeleport { 26 | assets: All.into(), 27 | dest: Parachain(1).into(), 28 | xcm: Xcm(vec![ 29 | BuyExecution { 30 | fees: (Parent, fee_in_destination).into(), 31 | weight_limit: WeightLimit::Unlimited, 32 | }, 33 | DepositAsset { 34 | assets: All.into(), 35 | beneficiary: Junction::AccountId32 { network: None, id: ALICE.into() } 36 | .into(), 37 | }, 38 | ]), 39 | }, 40 | ]); 41 | 42 | Relay::execute_with(|| { 43 | assert_ok!(relay_chain::XcmPallet::execute( 44 | relay_chain::RuntimeOrigin::signed(ALICE), 45 | Box::new(xcm::VersionedXcm::V3(message.into())), 46 | (100_000_000_000, 100_000_000_000).into() 47 | )); 48 | 49 | assert_eq!( 50 | relay_chain::Balances::free_balance(ALICE), 51 | INITIAL_BALANCE - withdraw_amount 52 | ); 53 | }); 54 | 55 | ParaA::execute_with(|| { 56 | let expected_message_received: Xcm = Xcm(vec![ 57 | ReceiveTeleportedAsset( 58 | vec![(Parent, withdraw_amount - fee_in_source).into()].into(), 59 | ), 60 | ClearOrigin, 61 | BuyExecution { 62 | fees: (Parent, fee_in_destination).into(), 63 | weight_limit: WeightLimit::Unlimited, 64 | }, 65 | DepositAsset { 66 | assets: All.into(), 67 | beneficiary: Junction::AccountId32 { network: None, id: ALICE.into() }.into(), 68 | }, 69 | ]); 70 | 71 | assert_eq!(parachain::MsgQueue::received_dmp(), vec![expected_message_received]); 72 | 73 | assert_eq!( 74 | parachain::Assets::balance(0, &ALICE), 75 | INITIAL_BALANCE + (withdraw_amount - fee_in_source - fee_in_destination) 76 | ); 77 | }); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /examples/src/2_fees/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use crate::simple_test_net::{parachain::estimate_message_fee, *}; 4 | use frame_support::assert_ok; 5 | use xcm::latest::prelude::*; 6 | use xcm_simulator::TestExt; 7 | 8 | /// Scenario: 9 | /// Relay chain sends a XCM to Parachain A. 10 | /// Enough execution is bought for the full message. 11 | /// However, the message errors at the Trap(1) instruction (simulates error in other instructions). 12 | /// The last three instructions are not executed 13 | /// and the weight surplus of these instructions is refunded to the relay chain account. 14 | #[test] 15 | fn refund_surplus() { 16 | MockNet::reset(); 17 | let message_fee = parachain::estimate_message_fee(9); 18 | let message = Xcm(vec![ 19 | WithdrawAsset((Parent, message_fee).into()), 20 | BuyExecution { 21 | fees: (Parent, message_fee).into(), 22 | weight_limit: WeightLimit::Unlimited, 23 | }, 24 | SetErrorHandler(Xcm(vec![ 25 | RefundSurplus, 26 | DepositAsset { 27 | assets: All.into(), 28 | beneficiary: AccountId32 { 29 | network: Some(ByGenesis([0; 32])), 30 | id: relay_sovereign_account_id().into(), 31 | } 32 | .into(), 33 | }, 34 | ])), 35 | Trap(1), 36 | ClearOrigin, 37 | ClearOrigin, 38 | ClearOrigin, 39 | ]); 40 | 41 | Relay::execute_with(|| { 42 | assert_ok!(RelaychainPalletXcm::send_xcm(Here, Parachain(1), message.clone(),)); 43 | }); 44 | 45 | ParaA::execute_with(|| { 46 | assert_eq!( 47 | ParachainAssets::balance(0, relay_sovereign_account_id()), 48 | INITIAL_BALANCE - message_fee + estimate_message_fee(3) 49 | ); 50 | }) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /examples/src/3_transact/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use crate::simple_test_net::*; 4 | use codec::Encode; 5 | use frame_support::{assert_ok, pallet_prelude::Weight}; 6 | use xcm::latest::prelude::*; 7 | use xcm_simulator::TestExt; 8 | 9 | /// Scenario: 10 | /// Relay chain sets the balance of Alice on Parachain(1). 11 | /// The relay chain is able to do this, because Parachain(1) trusts the relay chain to execute runtime calls as root. 12 | #[test] 13 | fn transact_set_balance() { 14 | MockNet::reset(); 15 | // Runtime call dispatched by the Transact instruction. 16 | // force_set_balance requires root origin. 17 | let call = parachain::RuntimeCall::Balances( 18 | pallet_balances::Call::::force_set_balance { 19 | who: ALICE, 20 | new_free: 5 * CENTS, 21 | }, 22 | ); 23 | 24 | let message_fee = parachain::estimate_message_fee(3); 25 | let set_balance_weight_estimation = Weight::from_parts(1_000_000_000, 10_000); 26 | let set_balance_fee_estimation = 27 | parachain::estimate_fee_for_weight(set_balance_weight_estimation); 28 | let fees = message_fee + set_balance_fee_estimation; 29 | 30 | let message = Xcm(vec![ 31 | WithdrawAsset((Parent, fees).into()), 32 | BuyExecution { fees: (Parent, fees).into(), weight_limit: WeightLimit::Unlimited }, 33 | Transact { 34 | origin_kind: OriginKind::Superuser, 35 | require_weight_at_most: set_balance_weight_estimation, 36 | call: call.encode().into(), 37 | }, 38 | ]); 39 | 40 | Relay::execute_with(|| { 41 | assert_ok!(RelaychainPalletXcm::send_xcm(Here, Parachain(1), message.clone(),)); 42 | }); 43 | 44 | ParaA::execute_with(|| { 45 | assert_eq!(ParachainBalances::free_balance(ALICE), 5 * CENTS); 46 | }) 47 | } 48 | 49 | /// Scenario: 50 | /// Parachain A sends two transact instructions to the relay chain. 51 | /// The first instruction creates a NFT collection with as admin Parachain A. 52 | /// The second instruction mints a NFT for the collection with as Owner ALICE. 53 | #[test] 54 | fn transact_mint_nft() { 55 | MockNet::reset(); 56 | 57 | let create_collection = relay_chain::RuntimeCall::Uniques(pallet_uniques::Call::< 58 | relay_chain::Runtime, 59 | >::create { 60 | collection: 1u32, 61 | admin: parachain_sovereign_account_id(1), 62 | }); 63 | 64 | let message_fee = relay_chain::estimate_message_fee(4); 65 | let create_collection_weight_estimation = Weight::from_parts(1_000_000_000, 10_000); 66 | let create_collection_fee_estimation = 67 | relay_chain::estimate_fee_for_weight(create_collection_weight_estimation); 68 | let mint_nft_weight_estimation = Weight::from_parts(1_000_000_000, 10_000); 69 | let mint_nft_fee_estimation = 70 | relay_chain::estimate_fee_for_weight(mint_nft_weight_estimation); 71 | let fees = message_fee + create_collection_fee_estimation + mint_nft_fee_estimation; 72 | 73 | let mint = 74 | relay_chain::RuntimeCall::Uniques(pallet_uniques::Call::::mint { 75 | collection: 1u32, 76 | item: 1u32, 77 | owner: ALICE, 78 | }); 79 | 80 | let message = Xcm(vec![ 81 | WithdrawAsset((Here, fees).into()), 82 | BuyExecution { fees: (Here, fees).into(), weight_limit: WeightLimit::Unlimited }, 83 | Transact { 84 | origin_kind: OriginKind::SovereignAccount, 85 | require_weight_at_most: create_collection_weight_estimation, 86 | call: create_collection.encode().into(), 87 | }, 88 | Transact { 89 | origin_kind: OriginKind::SovereignAccount, 90 | require_weight_at_most: mint_nft_weight_estimation, 91 | call: mint.encode().into(), 92 | }, 93 | ]); 94 | 95 | // Create collection with Alice as owner. 96 | ParaA::execute_with(|| { 97 | assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent, message.clone())); 98 | }); 99 | 100 | Relay::execute_with(|| { 101 | assert_eq!( 102 | relay_chain::Uniques::collection_owner(1u32), 103 | Some(parachain_sovereign_account_id(1)) 104 | ); 105 | assert_eq!(relay_chain::Uniques::owner(1u32, 1u32), Some(ALICE)); 106 | }); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /examples/src/4_origins/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use crate::simple_test_net::*; 4 | use frame_support::{assert_ok, pallet_prelude::Weight}; 5 | use xcm::latest::prelude::*; 6 | use xcm_simulator::TestExt; 7 | 8 | const QUERY_ID: u64 = 1234; 9 | 10 | /// Scenario: 11 | #[test] 12 | fn descend_origin() { 13 | MockNet::reset(); 14 | ParaA::execute_with(|| { 15 | let message_fee = parachain::estimate_message_fee(6); 16 | let message = Xcm(vec![ 17 | WithdrawAsset((Here, message_fee).into()), 18 | BuyExecution { 19 | fees: (Here, message_fee).into(), 20 | weight_limit: WeightLimit::Unlimited, 21 | }, 22 | // Set the instructions that are executed when ExpectOrigin does not pass. 23 | // In this case, reporting back an error to the Parachain. 24 | SetErrorHandler(Xcm(vec![ReportError(QueryResponseInfo { 25 | destination: Parachain(1).into(), 26 | query_id: QUERY_ID, 27 | max_weight: Weight::from_all(0), 28 | })])), 29 | DescendOrigin((PalletInstance(1)).into()), 30 | // Checks if the XcmContext origin descended to `Parachain(1)/PalletInstance(1)`. 31 | ExpectOrigin(Some((Parachain(1), PalletInstance(1)).into())), 32 | ]); 33 | assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent, message.clone(),)); 34 | }); 35 | 36 | Relay::execute_with(|| { 37 | assert!(relay_successful_execution()); 38 | }); 39 | 40 | // Check that message queue is empty. 41 | // The ExpectOrigin instruction passed so we should not receive an error response. 42 | ParaA::execute_with(|| assert_eq!(parachain::MsgQueue::received_dmp(), vec![])); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /examples/src/5_holding_modifiers/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use crate::simple_test_net::*; 4 | use frame_support::{assert_ok, pallet_prelude::Weight}; 5 | use xcm::latest::prelude::*; 6 | use xcm_simulator::TestExt; 7 | 8 | const QUERY_ID: u64 = 1234; 9 | 10 | /// Scenario: 11 | /// Parachain A withdraws funds from its sovereign account on the relay chain and burns part of them. 12 | /// The relay chain then reports back the status of the Holding Register to Parachain A. 13 | #[test] 14 | fn burn_assets() { 15 | let message = Xcm(vec![ 16 | UnpaidExecution { weight_limit: WeightLimit::Unlimited, check_origin: None }, 17 | WithdrawAsset((Here, 10 * CENTS).into()), 18 | BurnAsset((Here, 4 * CENTS).into()), 19 | ReportHolding { 20 | response_info: QueryResponseInfo { 21 | destination: Parachain(1).into(), 22 | query_id: QUERY_ID, 23 | max_weight: Weight::from_parts(1_000_000_000, 64 * 64), 24 | }, 25 | assets: All.into(), 26 | }, 27 | ]); 28 | ParaA::execute_with(|| { 29 | assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent, message.clone())); 30 | }); 31 | 32 | ParaA::execute_with(|| { 33 | assert_eq!( 34 | parachain::MsgQueue::received_dmp(), 35 | vec![Xcm(vec![QueryResponse { 36 | query_id: QUERY_ID, 37 | response: Response::Assets((Parent, 6 * CENTS).into()), 38 | max_weight: Weight::from_parts(1_000_000_000, 64 * 64), 39 | querier: Some(Here.into()), 40 | }])], 41 | ) 42 | }); 43 | } 44 | 45 | /// Scenario: 46 | /// The relay chain sends an XCM to Parachain A that: 47 | /// 1) Withdraws some native assets 48 | /// 2) Exchanges these assets for relay chain derivative tokens, with maximal set to true. 49 | /// 3) Deposit all the assets that are in the Holding in the account of Alice. 50 | /// 51 | /// NOTE: The implementation of the AssetExchanger is simple 52 | /// and in this case swaps all the assets in the exchange for the assets in `give`. 53 | /// Depending on the implementation of AssetExchanger, the test results could differ. 54 | #[test] 55 | fn exchange_asset_maximal_true() { 56 | // Exchange contains 10 CENTS worth of parachain A's derivative of the relay token 57 | let assets_in_exchange = vec![(Parent, 10 * CENTS).into()]; 58 | parachain::set_exchange_assets(assets_in_exchange); 59 | 60 | let message = Xcm(vec![ 61 | UnpaidExecution { weight_limit: WeightLimit::Unlimited, check_origin: None }, 62 | WithdrawAsset((Here, 10 * CENTS).into()), 63 | // Maximal field set to true. 64 | ExchangeAsset { 65 | give: Definite((Here, 5 * CENTS).into()), 66 | want: (Parent, 5 * CENTS).into(), 67 | maximal: true, 68 | }, 69 | DepositAsset { 70 | assets: AllCounted(2).into(), 71 | beneficiary: AccountId32 { 72 | network: Some(parachain::RelayNetwork::get()), 73 | id: ALICE.into(), 74 | } 75 | .into(), 76 | }, 77 | ]); 78 | 79 | Relay::execute_with(|| { 80 | assert_ok!(RelaychainPalletXcm::send_xcm(Here, Parachain(1), message.clone())); 81 | }); 82 | 83 | ParaA::execute_with(|| { 84 | assert_eq!(parachain::exchange_assets(), vec![(Here, 5 * CENTS).into()].into()); 85 | assert_eq!(ParachainAssets::balance(0, &ALICE), INITIAL_BALANCE + 10 * CENTS); 86 | assert_eq!(ParachainBalances::free_balance(ALICE), INITIAL_BALANCE + 5 * CENTS); 87 | }) 88 | } 89 | 90 | /// Scenario: 91 | /// The relay chain sends an XCM to Parachain A that: 92 | /// 1) Withdraws some native assets 93 | /// 2) Exchanges these assets for relay chain derivative tokens, with maximal set to false. 94 | /// 3) Deposit all the assets that are in the Holding in the account of Alice. 95 | /// 96 | /// NOTE: The implementation of the AssetExchanger is simple 97 | /// and in this case swaps all the assets in the exchange for the assets in `give`. 98 | /// Depending on the implementation of AssetExchanger, the test results could differ. 99 | #[test] 100 | fn exchange_asset_maximal_false() { 101 | // Exchange contains 10 CENTS worth of parachain A's derivative of the relay token 102 | let assets_in_exchange = vec![(Parent, 10 * CENTS).into()]; 103 | parachain::set_exchange_assets(assets_in_exchange); 104 | 105 | let message = Xcm(vec![ 106 | UnpaidExecution { weight_limit: WeightLimit::Unlimited, check_origin: None }, 107 | WithdrawAsset((Here, 10 * CENTS).into()), 108 | // Maximal field set to false. 109 | ExchangeAsset { 110 | give: Definite((Here, 5 * CENTS).into()), 111 | want: (Parent, 5 * CENTS).into(), 112 | maximal: false, 113 | }, 114 | DepositAsset { 115 | assets: AllCounted(2).into(), 116 | beneficiary: AccountId32 { 117 | network: Some(parachain::RelayNetwork::get()), 118 | id: ALICE.into(), 119 | } 120 | .into(), 121 | }, 122 | ]); 123 | 124 | Relay::execute_with(|| { 125 | assert_ok!(RelaychainPalletXcm::send_xcm(Here, Parachain(1), message.clone())); 126 | }); 127 | 128 | ParaA::execute_with(|| { 129 | assert_eq!( 130 | parachain::exchange_assets(), 131 | vec![(Parent, 5 * CENTS).into(), (Here, 5 * CENTS).into()].into() 132 | ); 133 | assert_eq!(ParachainAssets::balance(0, &ALICE), INITIAL_BALANCE + 5 * CENTS); 134 | assert_eq!(ParachainBalances::free_balance(ALICE), INITIAL_BALANCE + 5 * CENTS); 135 | }) 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /examples/src/6_trap_and_claim/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use crate::simple_test_net::*; 4 | use frame_support::{assert_ok, pallet_prelude::Weight}; 5 | use xcm::latest::prelude::*; 6 | use xcm_simulator::TestExt; 7 | 8 | const QUERY_ID: u64 = 1234; 9 | 10 | /// Scenario: 11 | /// Parachain A withdraws funds from its sovereign account on the relay chain. 12 | /// The assets are trapped because an error is thrown and the execution is halted. 13 | /// Parachain A claims the trapped assets and receives a report of the holding register. 14 | /// It then deposits the assets in the account of ALICE. 15 | #[test] 16 | fn trap_and_claim_assets() { 17 | MockNet::reset(); 18 | 19 | let message = Xcm(vec![ 20 | UnpaidExecution { weight_limit: WeightLimit::Unlimited, check_origin: None }, 21 | WithdrawAsset((Here, 10 * CENTS).into()), 22 | Trap(0), // <-- Errors 23 | DepositAsset { 24 | // <-- Not executed because of error. 25 | assets: All.into(), 26 | beneficiary: AccountId32 { 27 | network: Some(parachain::RelayNetwork::get()), 28 | id: ALICE.into(), 29 | } 30 | .into(), 31 | }, 32 | ]); 33 | ParaA::execute_with(|| { 34 | assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent, message.clone())); 35 | }); 36 | 37 | let claim_message = Xcm(vec![ 38 | UnpaidExecution { weight_limit: WeightLimit::Unlimited, check_origin: None }, 39 | ClaimAsset { assets: (Here, 10 * CENTS).into(), ticket: Here.into() }, 40 | ReportHolding { 41 | response_info: QueryResponseInfo { 42 | destination: Parachain(1).into(), 43 | query_id: QUERY_ID, 44 | max_weight: Weight::from_parts(1_000_000_000, 64 * 64), 45 | }, 46 | assets: All.into(), 47 | }, 48 | DepositAsset { 49 | assets: All.into(), 50 | beneficiary: AccountId32 { 51 | network: Some(parachain::RelayNetwork::get()), 52 | id: ALICE.into(), 53 | } 54 | .into(), 55 | }, 56 | ]); 57 | ParaA::execute_with(|| { 58 | assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent, claim_message.clone())); 59 | }); 60 | 61 | Relay::execute_with(|| { 62 | assert_eq!(RelaychainBalances::free_balance(ALICE), INITIAL_BALANCE + 10 * CENTS); 63 | }); 64 | 65 | ParaA::execute_with(|| { 66 | assert_eq!( 67 | parachain::MsgQueue::received_dmp(), 68 | vec![Xcm(vec![QueryResponse { 69 | query_id: QUERY_ID, 70 | response: Response::Assets((Parent, 10 * CENTS).into()), 71 | max_weight: Weight::from_parts(1_000_000_000, 64 * 64), 72 | querier: Some(Here.into()), 73 | }])], 74 | ) 75 | }); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /examples/src/7_expects/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use crate::simple_test_net::*; 4 | use bounded_collections::BoundedVec; 5 | use codec::Encode; 6 | use frame_support::{assert_ok, pallet_prelude::Weight}; 7 | use xcm::latest::prelude::*; 8 | use xcm_simulator::TestExt; 9 | 10 | const AMOUNT: u128 = 50 * CENTS; 11 | const QUERY_ID: u64 = 1234; 12 | 13 | /// Scenario: 14 | /// Parachain wants to execute specific instructions on the relay chain that use assets in the holding register. 15 | /// Before executing these instructions it want to check if the assets in the holding register are expected. 16 | /// If the assets are not expected, it wants to be notified with an `ExpectationFalse` error. 17 | /// It first sets an error handler that reports back an error using the `ReportError` instruction. 18 | /// And adds a `ExpectAsset` instruction just before executing the specific instructions. 19 | #[test] 20 | fn expect_asset() { 21 | MockNet::reset(); 22 | 23 | let message_fee = relay_chain::estimate_message_fee(5); 24 | 25 | ParaA::execute_with(|| { 26 | let message = Xcm(vec![ 27 | WithdrawAsset((Here, AMOUNT + message_fee).into()), 28 | BuyExecution { 29 | fees: (Here, message_fee).into(), 30 | weight_limit: WeightLimit::Unlimited, 31 | }, 32 | // Set the instructions that are executed when ExpectAsset does not pass. 33 | // In this case, reporting back an error to the Parachain. 34 | SetErrorHandler(Xcm(vec![ReportError(QueryResponseInfo { 35 | destination: Parachain(1).into(), 36 | query_id: QUERY_ID, 37 | max_weight: Weight::from_all(0), 38 | })])), 39 | ExpectAsset((Here, AMOUNT + 10 * CENTS).into()), 40 | // Add Instructions that do something with assets in holding when ExpectAsset passes. 41 | ]); 42 | assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent, message.clone(),)); 43 | }); 44 | 45 | let instruction_index_that_errored = 3; 46 | 47 | // Check that QueryResponse message with ExpectationFalse error was received. 48 | ParaA::execute_with(|| { 49 | assert_eq!( 50 | parachain::MsgQueue::received_dmp(), 51 | vec![Xcm(vec![QueryResponse { 52 | query_id: QUERY_ID, 53 | response: Response::ExecutionResult(Some(( 54 | instruction_index_that_errored, 55 | XcmError::ExpectationFalse 56 | ))), 57 | max_weight: Weight::from_all(0), 58 | querier: Some(Here.into()), 59 | }])], 60 | ); 61 | }); 62 | } 63 | 64 | /// Scenario: 65 | /// Parachain wants to make sure that XcmContext contains the expected `origin` at a certain point during execution. 66 | /// It sets the `ExpectOrigin` instruction to check for the expected `origin`. 67 | /// If the origin is not as expected, the instruction errors, and the ErrorHandler reports back the corresponding error to the parachain. 68 | #[test] 69 | fn expect_origin() { 70 | MockNet::reset(); 71 | 72 | let message_fee = relay_chain::estimate_message_fee(6); 73 | 74 | ParaA::execute_with(|| { 75 | let message = Xcm(vec![ 76 | WithdrawAsset((Here, AMOUNT + message_fee).into()), 77 | BuyExecution { 78 | fees: (Here, message_fee).into(), 79 | weight_limit: WeightLimit::Unlimited, 80 | }, 81 | // Set the instructions that are executed when ExpectOrigin does not pass. 82 | // In this case, reporting back an error to the Parachain. 83 | SetErrorHandler(Xcm(vec![ReportError(QueryResponseInfo { 84 | destination: Parachain(1).into(), 85 | query_id: QUERY_ID, 86 | max_weight: Weight::from_all(0), 87 | })])), 88 | ClearOrigin, 89 | // Checks if the XcmContext origin is `Parachain(1). 90 | ExpectOrigin(Some(Parachain(1).into())), 91 | ]); 92 | assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent, message.clone(),)); 93 | }); 94 | 95 | let instruction_index_that_errored = 4; 96 | 97 | // Check that QueryResponse message with ExpectationFalse error was received. 98 | ParaA::execute_with(|| { 99 | assert_eq!( 100 | parachain::MsgQueue::received_dmp(), 101 | vec![Xcm(vec![QueryResponse { 102 | query_id: QUERY_ID, 103 | response: Response::ExecutionResult(Some(( 104 | instruction_index_that_errored, 105 | XcmError::ExpectationFalse 106 | ))), 107 | max_weight: Weight::from_all(0), 108 | querier: None, 109 | }])], 110 | ); 111 | }); 112 | } 113 | 114 | /// Scenario: 115 | /// Parachain wants to make sure that the relay chain has configured a specific pallet with a specific version. 116 | /// It sets the `ExpectPallet` instruction to check for the expected pallet. 117 | /// If the pallet is not as expected, the instruction errors, and the ErrorHandler reports back the corresponding error to the parachain. 118 | #[test] 119 | fn expect_pallet() { 120 | MockNet::reset(); 121 | 122 | let message_fee = relay_chain::estimate_message_fee(5); 123 | 124 | ParaA::execute_with(|| { 125 | let message = Xcm(vec![ 126 | WithdrawAsset((Here, message_fee).into()), 127 | BuyExecution { 128 | fees: (Here, message_fee).into(), 129 | weight_limit: WeightLimit::Unlimited, 130 | }, 131 | // Set the instructions that are executed when ExpectPallet does not pass. 132 | // In this case, reporting back an error to the Parachain. 133 | SetErrorHandler(Xcm(vec![ReportError(QueryResponseInfo { 134 | destination: Parachain(1).into(), 135 | query_id: QUERY_ID, 136 | max_weight: Weight::from_all(0), 137 | })])), 138 | // Configured pallet has different `crate_major` so `VersionIncompatible` error is thrown. 139 | ExpectPallet { 140 | index: 1, 141 | name: "Balances".into(), 142 | module_name: "pallet_balances".into(), 143 | crate_major: 3, 144 | min_crate_minor: 0, 145 | }, 146 | // Could execute pallet specific instructions after the expect pallet succeeds. 147 | ]); 148 | assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent, message.clone(),)); 149 | }); 150 | 151 | // Check that QueryResponse message with `VersionIncompatible` error was received. 152 | // Can also be a different error based on the pallet mismatch (i.e. PalletNotFound, NameMismatch). 153 | ParaA::execute_with(|| { 154 | assert_eq!( 155 | parachain::MsgQueue::received_dmp(), 156 | vec![Xcm(vec![QueryResponse { 157 | query_id: QUERY_ID, 158 | response: Response::ExecutionResult(Some((3, XcmError::VersionIncompatible))), 159 | max_weight: Weight::from_all(0), 160 | querier: Some(Here.into()), 161 | }])], 162 | ); 163 | }); 164 | } 165 | 166 | /// Scenario: 167 | /// Parachain wants to make sure that the `ErrorHandler` that it sets is only executed when a specific error is thrown. 168 | /// It sets an `ExpectError` instruction in the `SetErrorHandler` to check for the specific error. 169 | /// If a different Error is thrown, the `ErrorHandler` execution is halted. 170 | /// 171 | /// Asserts that the ExpectPallet instruction throws an `PalletNotFound` error instead of the expected `VersionIncompatible` error. 172 | #[test] 173 | fn expect_error() { 174 | MockNet::reset(); 175 | 176 | let message_fee = relay_chain::estimate_message_fee(6); 177 | 178 | ParaA::execute_with(|| { 179 | let message = Xcm(vec![ 180 | WithdrawAsset((Here, message_fee).into()), 181 | BuyExecution { 182 | fees: (Here, message_fee).into(), 183 | weight_limit: WeightLimit::Unlimited, 184 | }, 185 | // ReportError is only executed if the thrown error is the `VersionIncompatible` error. 186 | SetErrorHandler(Xcm(vec![ 187 | ExpectError(Some((1, XcmError::VersionIncompatible))), 188 | ReportError(QueryResponseInfo { 189 | destination: Parachain(1).into(), 190 | query_id: QUERY_ID, 191 | max_weight: Weight::from_all(0), 192 | }), 193 | ])), 194 | // Pallet index is wrong, so throws `PalletNotFound` error. 195 | ExpectPallet { 196 | index: 100, 197 | name: "Balances".into(), 198 | module_name: "pallet_balances".into(), 199 | crate_major: 4, 200 | min_crate_minor: 0, 201 | }, 202 | ]); 203 | assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent, message.clone(),)); 204 | }); 205 | 206 | // Does not receive a message as the incorrect error was thrown during execution. 207 | ParaA::execute_with(|| { 208 | assert_eq!(parachain::MsgQueue::received_dmp(), vec![]); 209 | }); 210 | } 211 | 212 | /// Scenario: 213 | /// Parachain wants to make sure that the `Transact` instruction succeeded as it does not throw an XcmError. 214 | /// It sets an `ExpectTransactStatus` instruction to MaybeErrorCode::Success to check if the transact succeeded. 215 | /// If the status was not succesful, the `ExpectTransactStatus` errors, 216 | /// and the ErrorHandler will report the error back to the Parachain. 217 | /// 218 | /// Assert that `force_set_balance` execution fails as it requires the origin to be root, 219 | /// and the origin_kind is `SovereignAccount`. 220 | #[test] 221 | fn expect_transact_status() { 222 | MockNet::reset(); 223 | 224 | // Runtime call dispatched by the Transact instruction. 225 | // force_set_balance requires root origin. 226 | let call = relay_chain::RuntimeCall::Balances(pallet_balances::Call::< 227 | relay_chain::Runtime, 228 | >::force_set_balance { 229 | who: ALICE, 230 | new_free: 100, 231 | }); 232 | 233 | let message_fee = relay_chain::estimate_message_fee(6); 234 | let set_balance_weight_estimation = Weight::from_parts(1_000_000_000, 10_000); 235 | let set_balance_fee_estimation = 236 | relay_chain::estimate_fee_for_weight(set_balance_weight_estimation); 237 | let fees = message_fee + set_balance_fee_estimation; 238 | 239 | let message = Xcm(vec![ 240 | WithdrawAsset((Here, fees).into()), 241 | BuyExecution { fees: (Here, fees).into(), weight_limit: WeightLimit::Unlimited }, 242 | SetErrorHandler(Xcm(vec![ReportTransactStatus(QueryResponseInfo { 243 | destination: Parachain(1).into(), 244 | query_id: QUERY_ID, 245 | max_weight: Weight::from_all(0), 246 | })])), 247 | Transact { 248 | origin_kind: OriginKind::SovereignAccount, 249 | require_weight_at_most: set_balance_weight_estimation, 250 | call: call.encode().into(), 251 | }, 252 | ExpectTransactStatus(MaybeErrorCode::Success), 253 | ]); 254 | 255 | ParaA::execute_with(|| { 256 | assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent, message.clone())); 257 | }); 258 | 259 | // The execution of force_set_balance does not succeed, and error is reported back to the parachain. 260 | ParaA::execute_with(|| { 261 | assert_eq!( 262 | parachain::MsgQueue::received_dmp(), 263 | vec![Xcm(vec![QueryResponse { 264 | query_id: QUERY_ID, 265 | response: Response::DispatchResult(MaybeErrorCode::Error( 266 | // The 2 is the scale encoded Error from the balances pallet 267 | BoundedVec::truncate_from(vec![2]) 268 | )), 269 | max_weight: Weight::from_all(0), 270 | querier: Some(Here.into()), 271 | }])], 272 | ); 273 | }); 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /examples/src/8_queries/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use crate::simple_test_net::*; 4 | use bounded_collections::BoundedVec; 5 | use codec::Encode; 6 | use frame_support::{assert_ok, pallet_prelude::Weight}; 7 | use xcm::latest::prelude::*; 8 | use xcm_simulator::TestExt; 9 | 10 | const AMOUNT: u128 = 50 * CENTS; 11 | /// Arbitrary query id 12 | const QUERY_ID: u64 = 1234; 13 | 14 | /// Scenario: 15 | /// A parachain wants to be notified that a transfer worked correctly. 16 | /// It sends a `ReportHolding` after the deposit to get notified on success. 17 | /// 18 | /// Asserts that the balances are updated correctly and the expected XCM is sent. 19 | #[test] 20 | fn query_holding() { 21 | MockNet::reset(); 22 | 23 | let fee_in_relay = relay_chain::estimate_message_fee(4); 24 | 25 | // Send a message which succeeds to the relay chain. 26 | // And then report the status of the holding register back to ParaA 27 | ParaA::execute_with(|| { 28 | let message = Xcm(vec![ 29 | WithdrawAsset((Here, AMOUNT).into()), 30 | BuyExecution { fees: (Here, fee_in_relay).into(), weight_limit: Unlimited }, 31 | DepositAsset { 32 | assets: Definite((Here, AMOUNT - (5 * CENTS)).into()), 33 | beneficiary: Parachain(2).into(), 34 | }, 35 | ReportHolding { 36 | response_info: QueryResponseInfo { 37 | destination: Parachain(1).into(), 38 | query_id: QUERY_ID, 39 | max_weight: Weight::from_all(0), 40 | }, 41 | assets: All.into(), 42 | }, 43 | ]); 44 | 45 | assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent, message.clone(),)); 46 | }); 47 | 48 | // Check that transfer was executed 49 | Relay::execute_with(|| { 50 | // Withdraw executed 51 | assert_eq!( 52 | relay_chain::Balances::free_balance(parachain_sovereign_account_id(1)), 53 | INITIAL_BALANCE - AMOUNT 54 | ); 55 | // Deposit executed 56 | assert_eq!( 57 | relay_chain::Balances::free_balance(parachain_sovereign_account_id(2)), 58 | INITIAL_BALANCE + (AMOUNT - 5 * CENTS) 59 | ); 60 | }); 61 | 62 | // Check that QueryResponse message was received 63 | ParaA::execute_with(|| { 64 | assert_eq!( 65 | parachain::MsgQueue::received_dmp(), 66 | vec![Xcm(vec![QueryResponse { 67 | query_id: QUERY_ID, 68 | response: Response::Assets( 69 | (Parent, AMOUNT - (AMOUNT - 5 * CENTS) - fee_in_relay).into() 70 | ), 71 | max_weight: Weight::from_all(0), 72 | querier: Some(Here.into()), 73 | }])], 74 | ); 75 | }); 76 | } 77 | 78 | /// Scenario: 79 | /// Parachain A wants to query for information on the balances pallet in the relay chain. 80 | /// It sends a `QueryPallet` instruction to the relay chain. 81 | /// The relay chain responds with a `QueryResponse` instruction containing the `PalletInfo`. 82 | /// 83 | /// Asserts that the relay chain has the balances pallet configured. 84 | #[test] 85 | fn query_pallet() { 86 | MockNet::reset(); 87 | 88 | let fee_in_relay = relay_chain::estimate_message_fee(3); 89 | 90 | ParaA::execute_with(|| { 91 | let message = Xcm(vec![ 92 | WithdrawAsset((Here, fee_in_relay).into()), 93 | BuyExecution { 94 | fees: (Here, fee_in_relay).into(), 95 | weight_limit: WeightLimit::Unlimited, 96 | }, 97 | QueryPallet { 98 | module_name: "pallet_balances".into(), 99 | response_info: QueryResponseInfo { 100 | destination: Parachain(1).into(), 101 | query_id: QUERY_ID, 102 | max_weight: Weight::from_all(0), 103 | }, 104 | }, 105 | ]); 106 | 107 | assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent, message.clone(),)); 108 | }); 109 | 110 | ParaA::execute_with(|| { 111 | assert_eq!( 112 | parachain::MsgQueue::received_dmp(), 113 | vec![Xcm(vec![QueryResponse { 114 | query_id: QUERY_ID, 115 | response: Response::PalletsInfo(BoundedVec::truncate_from(vec![ 116 | PalletInfo::new(1, "Balances".into(), "pallet_balances".into(), 4, 0, 0) 117 | .unwrap() 118 | ])), 119 | max_weight: Weight::from_all(0), 120 | querier: Some(Here.into()), 121 | }])], 122 | ); 123 | }) 124 | } 125 | 126 | /// Scenario: 127 | /// Parachain A wants to know if the execution of their message on the relay chain succeeded without errors. 128 | /// They set the ErrorHandler to report the value of the error register. 129 | /// If the execution of the xcm instructions errors on the relay chain, the error is reported back to the Parachain. 130 | /// 131 | /// The Relay chain errors on the Trap instruction (Trap always throws an error). 132 | #[test] 133 | fn report_error() { 134 | MockNet::reset(); 135 | 136 | let fee_in_relay = 137 | relay_chain::estimate_message_fee(4) + relay_chain::estimate_message_fee(1); 138 | 139 | let message = Xcm(vec![ 140 | WithdrawAsset((Here, fee_in_relay).into()), 141 | BuyExecution { 142 | fees: (Here, fee_in_relay).into(), 143 | weight_limit: WeightLimit::Unlimited, 144 | }, 145 | // Set the Error Handler to report back status of Error register. 146 | SetErrorHandler(Xcm(vec![ReportError(QueryResponseInfo { 147 | destination: Parachain(1).into(), 148 | query_id: QUERY_ID, 149 | max_weight: Weight::from_all(0), 150 | })])), 151 | Trap(1u64), // Error is thrown on index 3 152 | ]); 153 | 154 | ParaA::execute_with(|| { 155 | assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent, message.clone())); 156 | }); 157 | 158 | let index_of_error = 3; 159 | 160 | ParaA::execute_with(|| { 161 | assert_eq!( 162 | parachain::MsgQueue::received_dmp(), 163 | vec![Xcm(vec![QueryResponse { 164 | query_id: QUERY_ID, 165 | response: Response::ExecutionResult(Some((index_of_error, XcmError::Trap(1)))), 166 | max_weight: Weight::from_all(0), 167 | querier: Some(Here.into()), 168 | }])], 169 | ); 170 | }); 171 | } 172 | 173 | /// Scenario: 174 | /// Parachain A wants to know if the execution of their `Transact` instruction on the relay chain succeeded without errors. 175 | /// They add the `ReportTransactStatus` instruction to the XCM to get the status of the transact status register reported back. 176 | #[test] 177 | fn report_transact_status() { 178 | MockNet::reset(); 179 | 180 | // Runtime call dispatched by the Transact instruction 181 | let call = relay_chain::RuntimeCall::System( 182 | frame_system::Call::::remark_with_event { 183 | remark: "Hallo Relay!".as_bytes().to_vec(), 184 | }, 185 | ); 186 | 187 | let message_fee = relay_chain::estimate_message_fee(5); 188 | let remark_weight_estimation = Weight::from_parts(20_000_000, 100_000); // We overestimate the weight taken by this extrinsic 189 | let remark_fee_estimation = relay_chain::estimate_fee_for_weight(remark_weight_estimation); 190 | 191 | let message = Xcm(vec![ 192 | WithdrawAsset((Here, message_fee + remark_fee_estimation).into()), 193 | BuyExecution { 194 | fees: (Here, message_fee + remark_fee_estimation).into(), 195 | weight_limit: WeightLimit::Unlimited, 196 | }, 197 | Transact { 198 | origin_kind: OriginKind::SovereignAccount, 199 | require_weight_at_most: remark_weight_estimation, 200 | call: call.encode().into(), 201 | }, 202 | ReportTransactStatus(QueryResponseInfo { 203 | destination: Parachain(1).into(), 204 | query_id: QUERY_ID, 205 | max_weight: Weight::from_all(0), 206 | }), 207 | ]); 208 | 209 | ParaA::execute_with(|| { 210 | assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent, message.clone(),)); 211 | }); 212 | 213 | ParaA::execute_with(|| { 214 | assert_eq!( 215 | parachain::MsgQueue::received_dmp(), 216 | vec![Xcm(vec![QueryResponse { 217 | query_id: QUERY_ID, 218 | response: Response::DispatchResult(MaybeErrorCode::Success), 219 | max_weight: Weight::from_all(0), 220 | querier: Some(Here.into()), 221 | }])], 222 | ); 223 | }); 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /examples/src/9_version_subscription/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use crate::simple_test_net::*; 4 | use frame_support::{assert_ok, pallet_prelude::Weight}; 5 | use xcm::latest::prelude::*; 6 | use xcm_simulator::TestExt; 7 | 8 | /// Scenario: 9 | /// Parachain A wants to know which version of Xcm the relay chain uses. 10 | /// It sends the `SubscribeVersion` instruction to get the Xcm version of the relay chain 11 | /// and to receive updates if the version changes. 12 | /// When the parachain receives the version it unsubscribes from version updates. 13 | #[test] 14 | fn subscribe_and_unsubscribe_version() { 15 | MockNet::reset(); 16 | 17 | let message_fee = relay_chain::estimate_message_fee(3); 18 | 19 | let query_id_set = 1234; 20 | let message = Xcm(vec![ 21 | WithdrawAsset((Here, message_fee).into()), 22 | BuyExecution { fees: (Here, message_fee).into(), weight_limit: WeightLimit::Unlimited }, 23 | SubscribeVersion { query_id: query_id_set, max_response_weight: Weight::from_all(0) }, 24 | ]); 25 | 26 | ParaA::execute_with(|| { 27 | assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent, message.clone())); 28 | }); 29 | 30 | ParaA::execute_with(|| { 31 | assert_eq!( 32 | parachain::MsgQueue::received_dmp(), 33 | vec![Xcm(vec![QueryResponse { 34 | query_id: query_id_set, 35 | response: Response::Version(3), 36 | max_weight: Weight::from_all(0), 37 | querier: None, 38 | }])], 39 | ); 40 | 41 | let unsub_message = Xcm(vec![UnsubscribeVersion]); 42 | assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent, unsub_message)); 43 | }); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /examples/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[path = "7_expects/mod.rs"] 2 | mod expects; 3 | #[path = "2_fees/mod.rs"] 4 | mod fees; 5 | #[path = "0_first_look/mod.rs"] 6 | mod first_look; 7 | #[path = "5_holding_modifiers/mod.rs"] 8 | mod holding_modifiers; 9 | #[path = "10_locks/mod.rs"] 10 | mod locks; 11 | #[path = "4_origins/mod.rs"] 12 | mod origins; 13 | #[path = "8_queries/mod.rs"] 14 | mod queries; 15 | mod simple_test_net; 16 | #[path = "3_transact/mod.rs"] 17 | mod transact; 18 | #[path = "1_transfers/mod.rs"] 19 | mod transfers; 20 | #[path = "6_trap_and_claim/mod.rs"] 21 | mod trap_and_claim; 22 | #[path = "9_version_subscription/mod.rs"] 23 | mod version_subscription; 24 | -------------------------------------------------------------------------------- /examples/src/simple_test_net/mock_msg_queue.rs: -------------------------------------------------------------------------------- 1 | // Copyright Parity Technologies (UK) Ltd. 2 | // This file is part of Polkadot. 3 | 4 | // Polkadot is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | 9 | // Polkadot is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | 14 | // You should have received a copy of the GNU General Public License 15 | // along with Polkadot. If not, see . 16 | 17 | //! Parachain runtime mock. 18 | 19 | use codec::{Decode, Encode}; 20 | 21 | use frame_support::weights::Weight; 22 | use polkadot_parachain::primitives::{ 23 | DmpMessageHandler, Id as ParaId, XcmpMessageFormat, XcmpMessageHandler, 24 | }; 25 | use polkadot_primitives::BlockNumber as RelayBlockNumber; 26 | use sp_runtime::traits::{Get, Hash}; 27 | 28 | use sp_std::prelude::*; 29 | use xcm::{latest::prelude::*, VersionedXcm}; 30 | 31 | #[frame_support::pallet] 32 | pub mod pallet { 33 | use super::*; 34 | use frame_support::pallet_prelude::*; 35 | 36 | #[pallet::config] 37 | pub trait Config: frame_system::Config { 38 | type RuntimeEvent: From> + IsType<::RuntimeEvent>; 39 | type XcmExecutor: ExecuteXcm; 40 | } 41 | 42 | #[pallet::call] 43 | impl Pallet {} 44 | 45 | #[pallet::pallet] 46 | #[pallet::without_storage_info] 47 | pub struct Pallet(_); 48 | 49 | #[pallet::storage] 50 | #[pallet::getter(fn parachain_id)] 51 | pub(super) type ParachainId = StorageValue<_, ParaId, ValueQuery>; 52 | 53 | #[pallet::storage] 54 | #[pallet::getter(fn received_dmp)] 55 | /// A queue of received DMP messages 56 | pub(super) type ReceivedDmp = StorageValue<_, Vec>, ValueQuery>; 57 | 58 | impl Get for Pallet { 59 | fn get() -> ParaId { 60 | Self::parachain_id() 61 | } 62 | } 63 | 64 | pub type MessageId = [u8; 32]; 65 | 66 | #[pallet::event] 67 | #[pallet::generate_deposit(pub(super) fn deposit_event)] 68 | pub enum Event { 69 | // XCMP 70 | /// Some XCM was executed OK. 71 | Success(Option), 72 | /// Some XCM failed. 73 | Fail(Option, XcmError), 74 | /// Bad XCM version used. 75 | BadVersion(Option), 76 | /// Bad XCM format used. 77 | BadFormat(Option), 78 | 79 | // DMP 80 | /// Downward message is invalid XCM. 81 | InvalidFormat(MessageId), 82 | /// Downward message is unsupported version of XCM. 83 | UnsupportedVersion(MessageId), 84 | /// Downward message executed with the given outcome. 85 | ExecutedDownward(MessageId, Outcome), 86 | } 87 | 88 | impl Pallet { 89 | pub fn set_para_id(para_id: ParaId) { 90 | ParachainId::::put(para_id); 91 | } 92 | 93 | fn handle_xcmp_message( 94 | sender: ParaId, 95 | _sent_at: RelayBlockNumber, 96 | xcm: VersionedXcm, 97 | max_weight: Weight, 98 | ) -> Result { 99 | let hash = Encode::using_encoded(&xcm, T::Hashing::hash); 100 | let message_hash = Encode::using_encoded(&xcm, sp_io::hashing::blake2_256); 101 | let (result, event) = match Xcm::::try_from(xcm) { 102 | Ok(xcm) => { 103 | let location = (Parent, Parachain(sender.into())); 104 | match T::XcmExecutor::execute_xcm(location, xcm, message_hash, max_weight) { 105 | Outcome::Error(e) => (Err(e.clone()), Event::Fail(Some(hash), e)), 106 | Outcome::Complete(w) => (Ok(w), Event::Success(Some(hash))), 107 | // As far as the caller is concerned, this was dispatched without error, so 108 | // we just report the weight used. 109 | Outcome::Incomplete(w, e) => (Ok(w), Event::Fail(Some(hash), e)), 110 | } 111 | } 112 | Err(()) => ( 113 | Err(XcmError::UnhandledXcmVersion), 114 | Event::BadVersion(Some(hash)), 115 | ), 116 | }; 117 | Self::deposit_event(event); 118 | result 119 | } 120 | } 121 | 122 | impl XcmpMessageHandler for Pallet { 123 | fn handle_xcmp_messages<'a, I: Iterator>( 124 | iter: I, 125 | max_weight: Weight, 126 | ) -> Weight { 127 | for (sender, sent_at, data) in iter { 128 | let mut data_ref = data; 129 | let _ = XcmpMessageFormat::decode(&mut data_ref) 130 | .expect("Simulator encodes with versioned xcm format; qed"); 131 | 132 | let mut remaining_fragments = &data_ref[..]; 133 | while !remaining_fragments.is_empty() { 134 | if let Ok(xcm) = 135 | VersionedXcm::::decode(&mut remaining_fragments) 136 | { 137 | let _ = Self::handle_xcmp_message(sender, sent_at, xcm, max_weight); 138 | } else { 139 | debug_assert!(false, "Invalid incoming XCMP message data"); 140 | } 141 | } 142 | } 143 | max_weight 144 | } 145 | } 146 | 147 | impl DmpMessageHandler for Pallet { 148 | fn handle_dmp_messages( 149 | iter: impl Iterator)>, 150 | limit: Weight, 151 | ) -> Weight { 152 | for (_i, (_sent_at, data)) in iter.enumerate() { 153 | let id = sp_io::hashing::blake2_256(&data[..]); 154 | let maybe_versioned = VersionedXcm::::decode(&mut &data[..]); 155 | match maybe_versioned { 156 | Err(_) => { 157 | Self::deposit_event(Event::InvalidFormat(id)); 158 | } 159 | Ok(versioned) => match Xcm::try_from(versioned) { 160 | Err(()) => Self::deposit_event(Event::UnsupportedVersion(id)), 161 | Ok(x) => { 162 | let outcome = T::XcmExecutor::execute_xcm(Parent, x.clone(), id, limit); 163 | >::append(x); 164 | Self::deposit_event(Event::ExecutedDownward(id, outcome)); 165 | } 166 | }, 167 | } 168 | } 169 | limit 170 | } 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /examples/src/simple_test_net/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright Parity Technologies (UK) Ltd. 2 | // This file is part of Polkadot. 3 | 4 | // Polkadot is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | 9 | // Polkadot is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | 14 | // You should have received a copy of the GNU General Public License 15 | // along with Polkadot. If not, see . 16 | #![allow(dead_code)] 17 | pub mod mock_msg_queue; 18 | pub mod parachain; 19 | pub mod relay_chain; 20 | 21 | use core::{borrow::Borrow, marker::PhantomData}; 22 | 23 | use frame_support::{ 24 | ensure, 25 | pallet_prelude::Weight, 26 | sp_tracing, 27 | traits::{GenesisBuild, ProcessMessageError}, 28 | }; 29 | use sp_core::blake2_256; 30 | use xcm::prelude::*; 31 | use xcm_executor::traits::{Convert, ShouldExecute}; 32 | use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain, TestExt}; 33 | 34 | // Accounts 35 | pub const ADMIN: sp_runtime::AccountId32 = sp_runtime::AccountId32::new([0u8; 32]); 36 | pub const ALICE: sp_runtime::AccountId32 = sp_runtime::AccountId32::new([1u8; 32]); 37 | pub const BOB: sp_runtime::AccountId32 = sp_runtime::AccountId32::new([2u8; 32]); 38 | 39 | // Balances 40 | pub type Balance = u128; 41 | pub const UNITS: Balance = 10_000_000_000; 42 | pub const CENTS: Balance = UNITS / 100; // 100_000_000 43 | pub const INITIAL_BALANCE: u128 = 10 * UNITS; 44 | 45 | decl_test_parachain! { 46 | pub struct ParaA { 47 | Runtime = parachain::Runtime, 48 | XcmpMessageHandler = parachain::MsgQueue, 49 | DmpMessageHandler = parachain::MsgQueue, 50 | new_ext = para_ext(1), 51 | } 52 | } 53 | 54 | decl_test_parachain! { 55 | pub struct ParaB { 56 | Runtime = parachain::Runtime, 57 | XcmpMessageHandler = parachain::MsgQueue, 58 | DmpMessageHandler = parachain::MsgQueue, 59 | new_ext = para_ext(2), 60 | } 61 | } 62 | 63 | decl_test_parachain! { 64 | pub struct ParaC { 65 | Runtime = parachain::Runtime, 66 | XcmpMessageHandler = parachain::MsgQueue, 67 | DmpMessageHandler = parachain::MsgQueue, 68 | new_ext = para_ext(2), 69 | } 70 | } 71 | 72 | decl_test_relay_chain! { 73 | pub struct Relay { 74 | Runtime = relay_chain::Runtime, 75 | RuntimeCall = relay_chain::RuntimeCall, 76 | RuntimeEvent = relay_chain::RuntimeEvent, 77 | XcmConfig = relay_chain::XcmConfig, 78 | MessageQueue = relay_chain::MessageQueue, 79 | System = relay_chain::System, 80 | new_ext = relay_ext(), 81 | } 82 | } 83 | 84 | decl_test_network! { 85 | pub struct MockNet { 86 | relay_chain = Relay, 87 | parachains = vec![ 88 | (1, ParaA), 89 | (2, ParaB), 90 | (3, ParaC), 91 | ], 92 | } 93 | } 94 | 95 | pub fn relay_sovereign_account_id() -> parachain::AccountId { 96 | let location = (Parent,); 97 | parachain::SovereignAccountOf::convert(location.into()).unwrap() 98 | } 99 | 100 | pub fn parachain_sovereign_account_id(para: u32) -> relay_chain::AccountId { 101 | let location = (Parachain(para),); 102 | relay_chain::SovereignAccountOf::convert(location.into()).unwrap() 103 | } 104 | 105 | pub fn parachain_account_sovereign_account_id( 106 | para: u32, 107 | who: sp_runtime::AccountId32, 108 | ) -> relay_chain::AccountId { 109 | let location = ( 110 | Parachain(para), 111 | AccountId32 { network: Some(relay_chain::RelayNetwork::get()), id: who.into() }, 112 | ); 113 | relay_chain::SovereignAccountOf::convert(location.into()).unwrap() 114 | } 115 | 116 | pub fn sibling_sovereign_account_id(para: u32) -> parachain::AccountId { 117 | let location = (Parent, Parachain(para)); 118 | parachain::SovereignAccountOf::convert(location.into()).unwrap() 119 | } 120 | 121 | pub fn sibling_account_sovereign_account_id( 122 | para: u32, 123 | who: sp_runtime::AccountId32, 124 | ) -> parachain::AccountId { 125 | let location = (Parent, Parachain(para), AccountId32 { network: None, id: who.into() }); 126 | parachain::SovereignAccountOf::convert(location.into()).unwrap() 127 | } 128 | 129 | pub fn relay_account_sovereign_account_id(who: sp_runtime::AccountId32) -> parachain::AccountId { 130 | let location = (Parent, AccountId32 { network: None, id: who.into() }); 131 | parachain::SovereignAccountOf::convert(location.into()).unwrap() 132 | } 133 | 134 | pub fn para_ext(para_id: u32) -> sp_io::TestExternalities { 135 | use parachain::{MsgQueue, Runtime, System}; 136 | 137 | let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); 138 | let other_para_ids = match para_id { 139 | 1 => [2, 3], 140 | 2 => [1, 3], 141 | 3 => [1, 2], 142 | _ => panic!("No parachain exists with para_id = {para_id}"), 143 | }; 144 | 145 | pallet_balances::GenesisConfig:: { 146 | balances: vec![ 147 | (ALICE, INITIAL_BALANCE), 148 | (relay_sovereign_account_id(), INITIAL_BALANCE), 149 | (BOB, INITIAL_BALANCE), 150 | ] 151 | .into_iter() 152 | .chain(other_para_ids.iter().map( 153 | // Initial balance of native token for ALICE on all sibling sovereign accounts 154 | |¶_id| (sibling_account_sovereign_account_id(para_id, ALICE), INITIAL_BALANCE), 155 | )) 156 | .chain(other_para_ids.iter().map( 157 | // Initial balance of native token all sibling sovereign accounts 158 | |¶_id| (sibling_sovereign_account_id(para_id), INITIAL_BALANCE), 159 | )) 160 | .collect(), 161 | } 162 | .assimilate_storage(&mut t) 163 | .unwrap(); 164 | 165 | pallet_assets::GenesisConfig:: { 166 | assets: vec![ 167 | (0u128, ADMIN, false, 1u128), // Create derivative asset for relay's native token 168 | ] 169 | .into_iter() 170 | .chain(other_para_ids.iter().map(|¶_id| (para_id as u128, ADMIN, false, 1u128))) // Derivative assets for the other parachains' native tokens 171 | .collect(), 172 | metadata: Default::default(), 173 | accounts: vec![ 174 | (0u128, ALICE, INITIAL_BALANCE), 175 | (0u128, relay_sovereign_account_id(), INITIAL_BALANCE), 176 | ] 177 | .into_iter() 178 | .chain(other_para_ids.iter().map(|¶_id| (para_id as u128, ALICE, INITIAL_BALANCE))) // Initial balance for derivatives of other parachains' tokens 179 | .chain(other_para_ids.iter().map(|¶_id| { 180 | (0u128, sibling_account_sovereign_account_id(para_id, ALICE), INITIAL_BALANCE) 181 | })) // Initial balance for sovereign accounts (for fee payment) 182 | .chain( 183 | other_para_ids 184 | .iter() 185 | .map(|¶_id| (0u128, sibling_sovereign_account_id(para_id), INITIAL_BALANCE)), 186 | ) // Initial balance for sovereign accounts (for fee payment) 187 | .collect(), 188 | } 189 | .assimilate_storage(&mut t) 190 | .unwrap(); 191 | 192 | let mut ext = sp_io::TestExternalities::new(t); 193 | ext.execute_with(|| { 194 | sp_tracing::try_init_simple(); 195 | System::set_block_number(1); 196 | MsgQueue::set_para_id(para_id.into()); 197 | }); 198 | ext 199 | } 200 | 201 | pub fn relay_ext() -> sp_io::TestExternalities { 202 | use relay_chain::{Runtime, System}; 203 | 204 | let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); 205 | 206 | pallet_balances::GenesisConfig:: { 207 | balances: vec![ 208 | (ALICE, INITIAL_BALANCE), 209 | (parachain_sovereign_account_id(1), INITIAL_BALANCE), 210 | (parachain_sovereign_account_id(2), INITIAL_BALANCE), 211 | (parachain_sovereign_account_id(3), INITIAL_BALANCE), 212 | (parachain_account_sovereign_account_id(1, ALICE), INITIAL_BALANCE), 213 | (parachain_account_sovereign_account_id(2, ALICE), INITIAL_BALANCE), 214 | (parachain_account_sovereign_account_id(3, ALICE), INITIAL_BALANCE), 215 | ], 216 | } 217 | .assimilate_storage(&mut t) 218 | .unwrap(); 219 | 220 | let mut ext = sp_io::TestExternalities::new(t); 221 | ext.execute_with(|| { 222 | System::set_block_number(1); 223 | }); 224 | ext 225 | } 226 | 227 | pub fn print_para_events() { 228 | use parachain::System; 229 | System::events().iter().for_each(|r| println!(">>> {:?}", r.event)); 230 | } 231 | 232 | pub fn print_relay_events() { 233 | use relay_chain::System; 234 | System::events().iter().for_each(|r| println!(">>> {:?}", r.event)); 235 | } 236 | 237 | pub fn relay_successful_execution() -> bool { 238 | use relay_chain::System; 239 | System::events().iter().any(|e| match &e.event { 240 | relay_chain::RuntimeEvent::MessageQueue( 241 | pallet_message_queue::Event::Processed{id: _, origin: _, weight_used: _, success: true}, 242 | ) => true, 243 | _ => false, 244 | }) 245 | } 246 | 247 | pub type RelaychainPalletXcm = pallet_xcm::Pallet; 248 | pub type ParachainPalletXcm = pallet_xcm::Pallet; 249 | pub type RelaychainBalances = pallet_balances::Pallet; 250 | pub type ParachainBalances = pallet_balances::Pallet; 251 | pub type ParachainAssets = pallet_assets::Pallet; 252 | 253 | /// Prefix for generating alias account for accounts coming 254 | /// from chains that use 32 byte long representations. 255 | pub const FOREIGN_CHAIN_PREFIX_PARA_32: [u8; 37] = *b"ForeignChainAliasAccountPrefix_Para32"; 256 | 257 | /// Prefix for generating alias account for accounts coming 258 | /// from the relay chain using 32 byte long representations. 259 | pub const FOREIGN_CHAIN_PREFIX_RELAY: [u8; 36] = *b"ForeignChainAliasAccountPrefix_Relay"; 260 | 261 | pub struct ForeignChainAliasAccount(PhantomData); 262 | impl + Clone> Convert 263 | for ForeignChainAliasAccount 264 | { 265 | fn convert_ref(location: impl Borrow) -> Result { 266 | let entropy = match location.borrow() { 267 | // Used on the relay chain for sending paras that use 32 byte accounts 268 | MultiLocation { 269 | parents: 0, 270 | interior: X2(Parachain(para_id), AccountId32 { id, .. }), 271 | } => ForeignChainAliasAccount::::from_para_32(para_id, id, 0), 272 | 273 | // Used on para-chain for sending paras that use 32 byte accounts 274 | MultiLocation { 275 | parents: 1, 276 | interior: X2(Parachain(para_id), AccountId32 { id, .. }), 277 | } => ForeignChainAliasAccount::::from_para_32(para_id, id, 1), 278 | 279 | // Used on para-chain for sending from the relay chain 280 | MultiLocation { parents: 1, interior: X1(AccountId32 { id, .. }) } => 281 | ForeignChainAliasAccount::::from_relay_32(id, 1), 282 | 283 | // No other conversions provided 284 | _ => return Err(()), 285 | }; 286 | 287 | Ok(entropy.into()) 288 | } 289 | 290 | fn reverse_ref(_: impl Borrow) -> Result { 291 | Err(()) 292 | } 293 | } 294 | 295 | impl ForeignChainAliasAccount { 296 | fn from_para_32(para_id: &u32, id: &[u8; 32], parents: u8) -> [u8; 32] { 297 | (FOREIGN_CHAIN_PREFIX_PARA_32, para_id, id, parents).using_encoded(blake2_256) 298 | } 299 | 300 | fn from_relay_32(id: &[u8; 32], parents: u8) -> [u8; 32] { 301 | (FOREIGN_CHAIN_PREFIX_RELAY, id, parents).using_encoded(blake2_256) 302 | } 303 | } 304 | 305 | // TODO: Is this vulnerable to DoS? It's how the instructions work 306 | pub struct AllowNoteUnlockables; 307 | impl ShouldExecute for AllowNoteUnlockables { 308 | fn should_execute( 309 | _origin: &MultiLocation, 310 | instructions: &mut [Instruction], 311 | _max_weight: Weight, 312 | _weight_credit: &mut Weight, 313 | ) -> Result<(), ProcessMessageError> { 314 | ensure!(instructions.len() == 1, ProcessMessageError::BadFormat); 315 | match instructions.first() { 316 | Some(NoteUnlockable { .. }) => Ok(()), 317 | _ => Err(ProcessMessageError::BadFormat), 318 | } 319 | } 320 | } 321 | 322 | pub struct AllowUnlocks; 323 | impl ShouldExecute for AllowUnlocks { 324 | fn should_execute( 325 | _origin: &MultiLocation, 326 | instructions: &mut [Instruction], 327 | _max_weight: Weight, 328 | _weight_credit: &mut Weight, 329 | ) -> Result<(), ProcessMessageError> { 330 | ensure!(instructions.len() == 1, ProcessMessageError::BadFormat); 331 | match instructions.first() { 332 | Some(UnlockAsset { .. }) => Ok(()), 333 | _ => Err(ProcessMessageError::BadFormat), 334 | } 335 | } 336 | } 337 | -------------------------------------------------------------------------------- /examples/src/simple_test_net/relay_chain.rs: -------------------------------------------------------------------------------- 1 | // Copyright Parity Technologies (UK) Ltd. 2 | // This file is part of Polkadot. 3 | 4 | // Polkadot is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | 9 | // Polkadot is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | 14 | // You should have received a copy of the GNU General Public License 15 | // along with Polkadot. If not, see . 16 | 17 | //! Relay chain runtime mock. 18 | 19 | use frame_support::{ 20 | construct_runtime, parameter_types, 21 | traits::{AsEnsureOriginWithArg, Contains, Everything, Nothing}, 22 | weights::{ 23 | constants::{WEIGHT_PROOF_SIZE_PER_MB, WEIGHT_REF_TIME_PER_SECOND}, 24 | Weight, 25 | }, 26 | }; 27 | 28 | use frame_system::EnsureRoot; 29 | use sp_core::{ConstU128, ConstU32, H256}; 30 | use sp_runtime::{testing::Header, traits::IdentityLookup, AccountId32}; 31 | 32 | use polkadot_parachain::primitives::Id as ParaId; 33 | use polkadot_runtime_parachains::{configuration, origin, shared}; 34 | use xcm::latest::prelude::*; 35 | use xcm_builder::{ 36 | AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowSubscriptionsFrom, 37 | AllowTopLevelPaidExecutionFrom, AsPrefixedGeneralIndex, ChildParachainAsNative, 38 | ChildParachainConvertsVia, ChildSystemParachainAsSuperuser, ConvertedConcreteId, 39 | CurrencyAdapter as XcmCurrencyAdapter, FixedRateOfFungible, FixedWeightBounds, IsConcrete, 40 | NoChecking, NonFungiblesAdapter, SiblingParachainConvertsVia, SignedAccountId32AsNative, 41 | SignedToAccountId32, SovereignSignedViaLocation, WithComputedOrigin, 42 | }; 43 | use xcm_executor::{traits::JustTry, Config, XcmExecutor}; 44 | use xcm_simulator::{ 45 | AggregateMessageOrigin, ProcessMessage, ProcessMessageError, UmpQueueId, WeightMeter, 46 | }; 47 | 48 | use super::{AllowNoteUnlockables, AllowUnlocks, Balance, ForeignChainAliasAccount}; 49 | 50 | pub type AccountId = AccountId32; 51 | 52 | parameter_types! { 53 | pub const BlockHashCount: u64 = 250; 54 | } 55 | 56 | impl frame_system::Config for Runtime { 57 | type RuntimeOrigin = RuntimeOrigin; 58 | type RuntimeCall = RuntimeCall; 59 | type Index = u64; 60 | type BlockNumber = u64; 61 | type Hash = H256; 62 | type Hashing = ::sp_runtime::traits::BlakeTwo256; 63 | type AccountId = AccountId; 64 | type Lookup = IdentityLookup; 65 | type Header = Header; 66 | type RuntimeEvent = RuntimeEvent; 67 | type BlockHashCount = BlockHashCount; 68 | type BlockWeights = (); 69 | type BlockLength = (); 70 | type Version = (); 71 | type PalletInfo = PalletInfo; 72 | type AccountData = pallet_balances::AccountData; 73 | type OnNewAccount = (); 74 | type OnKilledAccount = (); 75 | type DbWeight = (); 76 | type BaseCallFilter = Everything; 77 | type SystemWeightInfo = (); 78 | type SS58Prefix = (); 79 | type OnSetCode = (); 80 | type MaxConsumers = ConstU32<16>; 81 | } 82 | 83 | parameter_types! { 84 | pub ExistentialDeposit: Balance = 1; 85 | pub const MaxLocks: u32 = 50; 86 | pub const MaxReserves: u32 = 50; 87 | } 88 | 89 | impl pallet_balances::Config for Runtime { 90 | type MaxLocks = MaxLocks; 91 | type Balance = Balance; 92 | type RuntimeEvent = RuntimeEvent; 93 | type DustRemoval = (); 94 | type ExistentialDeposit = ExistentialDeposit; 95 | type AccountStore = System; 96 | type WeightInfo = (); 97 | type MaxReserves = MaxReserves; 98 | type ReserveIdentifier = [u8; 8]; 99 | type HoldIdentifier = (); 100 | type FreezeIdentifier = (); 101 | type MaxHolds = ConstU32<0>; 102 | type MaxFreezes = ConstU32<0>; 103 | } 104 | 105 | impl pallet_uniques::Config for Runtime { 106 | type RuntimeEvent = RuntimeEvent; 107 | type CollectionId = u32; 108 | type ItemId = u32; 109 | type Currency = Balances; 110 | type CreateOrigin = AsEnsureOriginWithArg>; 111 | type ForceOrigin = EnsureRoot; 112 | type CollectionDeposit = ConstU128<1_000>; 113 | type ItemDeposit = ConstU128<1_000>; 114 | type MetadataDepositBase = ConstU128<1_000>; 115 | type AttributeDepositBase = ConstU128<1_000>; 116 | type DepositPerByte = ConstU128<1>; 117 | type StringLimit = ConstU32<64>; 118 | type KeyLimit = ConstU32<64>; 119 | type ValueLimit = ConstU32<128>; 120 | type Locker = (); 121 | type WeightInfo = (); 122 | #[cfg(feature = "runtime-benchmarks")] 123 | type Helper = (); 124 | } 125 | 126 | impl shared::Config for Runtime {} 127 | 128 | impl configuration::Config for Runtime { 129 | type WeightInfo = configuration::TestWeightInfo; 130 | } 131 | 132 | parameter_types! { 133 | pub RelayNetwork: NetworkId = ByGenesis([0; 32]); 134 | pub const TokenLocation: MultiLocation = Here.into_location(); 135 | pub UniversalLocation: InteriorMultiLocation = Here; 136 | pub UnitWeightCost: u64 = 1_000; 137 | } 138 | 139 | pub type SovereignAccountOf = ( 140 | ForeignChainAliasAccount, 141 | AccountId32Aliases, 142 | ChildParachainConvertsVia, 143 | SiblingParachainConvertsVia, 144 | ); 145 | 146 | pub type LocalBalancesTransactor = 147 | XcmCurrencyAdapter, SovereignAccountOf, AccountId, ()>; 148 | 149 | pub type LocalUniquesTransactor = NonFungiblesAdapter< 150 | Uniques, 151 | ConvertedConcreteId, JustTry>, 152 | SovereignAccountOf, 153 | AccountId, 154 | NoChecking, 155 | (), 156 | >; 157 | 158 | pub type AssetTransactors = (LocalBalancesTransactor, LocalUniquesTransactor); 159 | 160 | type LocalOriginConverter = ( 161 | SovereignSignedViaLocation, 162 | ChildParachainAsNative, 163 | SignedAccountId32AsNative, 164 | ChildSystemParachainAsSuperuser, 165 | ); 166 | 167 | parameter_types! { 168 | pub const XcmInstructionWeight: Weight = Weight::from_parts(1_000, 1_000); 169 | pub TokensPerSecondPerMegabyte: (AssetId, u128, u128) = 170 | (Concrete(TokenLocation::get()), 1_000_000_000_000, 1024 * 1024); 171 | pub const MaxInstructions: u32 = 100; 172 | pub const MaxAssetsIntoHolding: u32 = 64; 173 | } 174 | 175 | pub fn estimate_message_fee(number_of_instructions: u64) -> u128 { 176 | let weight = estimate_message_weight(number_of_instructions); 177 | 178 | estimate_fee_for_weight(weight) 179 | } 180 | 181 | pub fn estimate_message_weight(number_of_instructions: u64) -> Weight { 182 | XcmInstructionWeight::get().saturating_mul(number_of_instructions) 183 | } 184 | 185 | pub fn estimate_fee_for_weight(weight: Weight) -> u128 { 186 | let (_, units_per_second, units_per_mb) = TokensPerSecondPerMegabyte::get(); 187 | 188 | units_per_second * (weight.ref_time() as u128) / (WEIGHT_REF_TIME_PER_SECOND as u128) + 189 | units_per_mb * (weight.proof_size() as u128) / (WEIGHT_PROOF_SIZE_PER_MB as u128) 190 | } 191 | 192 | pub struct ChildrenParachains; 193 | impl Contains for ChildrenParachains { 194 | fn contains(location: &MultiLocation) -> bool { 195 | matches!(location, MultiLocation { parents: 0, interior: X1(Parachain(_)) }) 196 | } 197 | } 198 | 199 | pub type XcmRouter = super::RelayChainXcmRouter; 200 | pub type Barrier = WithComputedOrigin< 201 | ( 202 | AllowNoteUnlockables, 203 | AllowUnlocks, 204 | AllowExplicitUnpaidExecutionFrom, 205 | AllowTopLevelPaidExecutionFrom, 206 | AllowSubscriptionsFrom, 207 | ), 208 | UniversalLocation, 209 | ConstU32<1>, 210 | >; 211 | 212 | pub struct XcmConfig; 213 | impl Config for XcmConfig { 214 | type RuntimeCall = RuntimeCall; 215 | type XcmSender = XcmRouter; 216 | type AssetTransactor = AssetTransactors; 217 | type OriginConverter = LocalOriginConverter; 218 | type IsReserve = (); 219 | type IsTeleporter = (); 220 | type UniversalLocation = UniversalLocation; 221 | type Barrier = Barrier; 222 | type Weigher = FixedWeightBounds; 223 | type Trader = FixedRateOfFungible; 224 | type ResponseHandler = XcmPallet; 225 | type AssetTrap = XcmPallet; 226 | type AssetLocker = XcmPallet; 227 | type AssetExchanger = (); 228 | type AssetClaims = XcmPallet; 229 | type SubscriptionService = XcmPallet; 230 | type PalletInstancesInfo = AllPalletsWithSystem; 231 | type FeeManager = (); 232 | type MaxAssetsIntoHolding = MaxAssetsIntoHolding; 233 | type MessageExporter = (); 234 | type UniversalAliases = Nothing; 235 | type CallDispatcher = RuntimeCall; 236 | type SafeCallFilter = Everything; 237 | } 238 | 239 | pub type LocalOriginToLocation = SignedToAccountId32; 240 | 241 | #[cfg(feature = "runtime-benchmarks")] 242 | parameter_types! { 243 | pub ReachableDest: Option = Some(Parachain(1).into()); 244 | } 245 | 246 | impl pallet_xcm::Config for Runtime { 247 | type RuntimeEvent = RuntimeEvent; 248 | type SendXcmOrigin = xcm_builder::EnsureXcmOrigin; 249 | type XcmRouter = XcmRouter; 250 | // Anyone can execute XCM messages locally... 251 | type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; 252 | type XcmExecuteFilter = Everything; 253 | type XcmExecutor = XcmExecutor; 254 | type XcmTeleportFilter = Everything; 255 | type XcmReserveTransferFilter = Everything; 256 | type Weigher = FixedWeightBounds; 257 | type UniversalLocation = UniversalLocation; 258 | type RuntimeOrigin = RuntimeOrigin; 259 | type RuntimeCall = RuntimeCall; 260 | const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; 261 | type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; 262 | type Currency = Balances; 263 | type CurrencyMatcher = IsConcrete; 264 | type TrustedLockers = (); 265 | type SovereignAccountOf = SovereignAccountOf; 266 | type MaxLockers = ConstU32<8>; 267 | type MaxRemoteLockConsumers = ConstU32<0>; 268 | type RemoteLockConsumerIdentifier = (); 269 | type WeightInfo = pallet_xcm::TestWeightInfo; 270 | #[cfg(feature = "runtime-benchmarks")] 271 | type ReachableDest = ReachableDest; 272 | type AdminOrigin = EnsureRoot; 273 | } 274 | 275 | parameter_types! { 276 | pub const FirstMessageFactorPercent: u64 = 100; 277 | } 278 | 279 | impl origin::Config for Runtime {} 280 | 281 | type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; 282 | type Block = frame_system::mocking::MockBlock; 283 | 284 | parameter_types! { 285 | /// Amount of weight that can be spent per block to service messages. 286 | pub MessageQueueServiceWeight: Weight = Weight::from_parts(1_000_000_000, 1_000_000); 287 | pub const MessageQueueHeapSize: u32 = 65_536; 288 | pub const MessageQueueMaxStale: u32 = 16; 289 | } 290 | 291 | /// Message processor to handle any messages that were enqueued into the `MessageQueue` pallet. 292 | pub struct MessageProcessor; 293 | impl ProcessMessage for MessageProcessor { 294 | type Origin = AggregateMessageOrigin; 295 | 296 | fn process_message( 297 | message: &[u8], 298 | origin: Self::Origin, 299 | meter: &mut WeightMeter, 300 | id: &mut [u8; 32], 301 | ) -> Result { 302 | let para = match origin { 303 | AggregateMessageOrigin::Ump(UmpQueueId::Para(para)) => para, 304 | }; 305 | xcm_builder::ProcessXcmMessage::< 306 | Junction, 307 | xcm_executor::XcmExecutor, 308 | RuntimeCall, 309 | >::process_message(message, Junction::Parachain(para.into()), meter, id) 310 | } 311 | } 312 | 313 | impl pallet_message_queue::Config for Runtime { 314 | type RuntimeEvent = RuntimeEvent; 315 | type Size = u32; 316 | type HeapSize = MessageQueueHeapSize; 317 | type MaxStale = MessageQueueMaxStale; 318 | type ServiceWeight = MessageQueueServiceWeight; 319 | type MessageProcessor = MessageProcessor; 320 | type QueueChangeHandler = (); 321 | type WeightInfo = (); 322 | } 323 | 324 | construct_runtime!( 325 | pub enum Runtime where 326 | Block = Block, 327 | NodeBlock = Block, 328 | UncheckedExtrinsic = UncheckedExtrinsic, 329 | { 330 | System: frame_system::{Pallet, Call, Storage, Config, Event}, 331 | Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, 332 | Uniques: pallet_uniques, 333 | ParasOrigin: origin::{Pallet, Origin}, 334 | XcmPallet: pallet_xcm::{Pallet, Call, Storage, Event, Origin}, 335 | MessageQueue: pallet_message_queue::{Pallet, Event}, 336 | } 337 | ); 338 | -------------------------------------------------------------------------------- /src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | [XCM: Cross-Consensus Messaging](xcm.md) 4 | 5 | - [Overview](overview/README.md) 6 | - [Introduction](overview/interoperability.md) 7 | - [A Format, not a Protocol](overview/format.md) 8 | - [The XCVM](overview/xcvm.md) 9 | - [Architecture](overview/architecture.md) 10 | - [Quickstart](quickstart/README.md) 11 | - [XCM Simulator](quickstart/xcm-simulator.md) 12 | - [First Look at an XCM](quickstart/first-look.md) 13 | - [Fundamentals](fundamentals/README.md) 14 | - [MultiLocation](fundamentals/multilocation/README.md) 15 | - [Junction(s)](fundamentals/multilocation/junction.md) 16 | - [Example](fundamentals/multilocation/example.md) 17 | - [MultiAsset](fundamentals/multiasset.md) 18 | - [XCVM](fundamentals/xcvm.md) 19 | - [Weight and fees](fundamentals/weight_and_fees.md) 20 | - [A Journey through XCM](journey/README.md) 21 | - [Transfers](journey/transfers/README.md) 22 | - [Asset teleportation](journey/transfers/teleports.md) 23 | - [Reserve-backed transfers](journey/transfers/reserve.md) 24 | - [Fee handling](journey/fees/README.md) 25 | - [Transact: A general solution](journey/transact.md) 26 | - [Origin manipulation](journey/origins.md) 27 | - [More register modifiers](journey/register-modifiers.md) 28 | - [More Holding Modifiers](./journey/holding-modifiers.md) 29 | - [Trap and Claim assets](./journey/trap-and-claim.md) 30 | - [Expectations](journey/expects.md) 31 | - [Queries](journey/queries.md) 32 | - [XCM Version](journey/version.md) 33 | - [Locks](journey/locks/locks.md) 34 | - [Channels and Bridges](journey/channels-and-bridges.md) 35 | - [Config Deep Dive](executor_config/README.md) 36 | - [Testing](testing/README.md) 37 | 38 | # Reference 39 | 40 | 42 | - [All XCVM Registers](reference/xcvm-registers.md) 43 | - [Glossary](reference/glossary.md) 44 | -------------------------------------------------------------------------------- /src/executor_config/images/executor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paritytech/xcm-docs/c86f214b5bc47e100d5b85cc436c6a6a453c81f8/src/executor_config/images/executor.png -------------------------------------------------------------------------------- /src/fundamentals/README.md: -------------------------------------------------------------------------------- 1 | # Fundamentals 2 | In this chapter we explore all the fundamentals that you should understand before diving deeper into XCM. 3 | -------------------------------------------------------------------------------- /src/fundamentals/images/MultiLocation_Example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paritytech/xcm-docs/c86f214b5bc47e100d5b85cc436c6a6a453c81f8/src/fundamentals/images/MultiLocation_Example.png -------------------------------------------------------------------------------- /src/fundamentals/images/MultiLocation_simple_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paritytech/xcm-docs/c86f214b5bc47e100d5b85cc436c6a6a453c81f8/src/fundamentals/images/MultiLocation_simple_example.png -------------------------------------------------------------------------------- /src/fundamentals/multiasset.md: -------------------------------------------------------------------------------- 1 | # MultiAsset 2 | When working with XCM, it is often needed to represent an asset of some sort. 3 | This is because practically all public blockchains in existence rely on some native digital asset to provide the backbone for its internal economy and security mechanism. 4 | For example, the native asset for the Polkadot relay chain is DOT. 5 | 6 | Some blockchains manage multiple assets, e.g. Ethereum’s ERC-20 framework allows for many different assets to be managed on-chain. 7 | Some manage assets that are not fungible, such as Ethereum’s Crypto-kitties — each kitty is a one-of-a-kind instance. 8 | It was an early example of such non-fungible tokens or NFTs. 9 | 10 | XCM is designed to be able to describe all such assets without breaking a sweat. 11 | For this purpose, there is the `MultiAsset` datatype, along with its related types `MultiAssets`, `WildMultiAsset`, and `MultiAssetFilter`. 12 | 13 | ## MultiAsset Breakdown 14 | Let's take a look at the MultiAsset struct: 15 | ```rust,noplayground 16 | pub struct MultiAsset { 17 | pub id: AssetId, 18 | pub fun: Fungibility, 19 | } 20 | ``` 21 | 22 | So two fields define our asset: id and fun. 23 | These fields are indicative of how XCM approaches assets. 24 | Firstly, an overall asset identity must be provided. 25 | For fungible assets, this is simply a symbol that identifies the asset. 26 | For NFTs this identifies the overall asset “class” — different asset instances may be within this class. 27 | 28 | ```rust,noplayground 29 | enum AssetId { 30 | Concrete(MultiLocation), 31 | Abstract([u8; 32]), 32 | } 33 | ``` 34 | 35 | The asset identity is expressed in one of two ways; either Concrete or Abstract. 36 | Abstract identities allow assets to be specified by a 32-byte blob. 37 | This is convenient, but it relies on the receiver to interpret the blob in the way that the sender expects, which will require a common definition between the sender and the receiver, and may not be simple to achieve. 38 | Concrete identities use a `MultiLocation` to identify an asset unambiguously. 39 | For native assets (such as DOT), the asset is identified as the chain which mints the asset (the Polkadot Relay Chain in this case, which would be the location `..` from one of its parachains). 40 | Other assets (e.g. non-native assets or NFTs) can be identified by a `GeneralIndex` junction. Depending on the implementation of the encapsulating consensus system, the exact location may differ (e.g. `GeneralIndex(AssetID)` or `PalletInstance(PalletID)/GeneralIndex(AssetID)` can both be valid asset identities). 41 | 42 | ```rust,noplayground 43 | enum Fungibility { 44 | // Fungible cannot be 0 45 | Fungible(u128), 46 | NonFungible(AssetInstance), 47 | } 48 | ``` 49 | Secondly, they must be either fungible or non-fungible. 50 | If they’re fungible, then there should be some associated non-zero amount of assets specified. 51 | If they’re not fungible, then instead of an amount, there should be some indication of which [AssetInstance](https://paritytech.github.io/polkadot/doc/xcm/v3/enum.AssetInstance.html) they are. 52 | (This is commonly expressed with an index, but XCM also allows arrays.) 53 | 54 | 55 | ## How to use Multiple Assets Together? 56 | There are multiple ways to group Assets. 57 | In this section, we go over these methods. 58 | 59 | ### MultiAssets 60 | One way to group a set of `MultiAsset` items is the [MultiAssets](https://paritytech.github.io/polkadot/doc/xcm/v3/struct.MultiAssets.html) type. 61 | 62 | ```rust,noplayground 63 | struct MultiAssets(Vec); 64 | ``` 65 | 66 | This structure must uphold some rules: 67 | - It may not contain duplicate `MultiAsset`s (`Fungible` assets are considered the same if their IDs match. However, `NonFungible` assets are different if the `AssetInstance` is different); 68 | - All items must be ordered; 69 | - The number of items should grow no larger than MAX_ITEMS_IN_MULTIASSETS (currently set to 20). 70 | 71 | 72 | 73 | ### WildMultiAsset 74 | Then we have WildMultiAsset; this is a wildcard that can be used to match against one or more MultiAsset items. 75 | All the WildMultiAsset wildcards can be used to select/filter assets in the [Holding register](../overview/xcvm.md). 76 | 77 | ```rust,noplayground 78 | pub enum WildMultiAsset { 79 | /// All assets in Holding. 80 | All, 81 | /// All assets in Holding of a given fungibility and ID. 82 | AllOf { id: AssetId, fun: WildFungibility }, 83 | /// All assets in Holding, up to `u32` individual assets (different instances of non-fungibles 84 | /// are separate assets). 85 | AllCounted(#[codec(compact)] u32), 86 | /// All assets in Holding of a given fungibility and ID up to `count` individual assets 87 | /// (different instances of non-fungibles are separate assets). 88 | AllOfCounted { 89 | id: AssetId, 90 | fun: WildFungibility, 91 | #[codec(compact)] 92 | count: u32, 93 | }, 94 | } 95 | ``` 96 | 97 | ### MultiAssetFilter 98 | Finally, there is `MultiAssetFilter`. 99 | This is used most often and is just a combination of MultiAssets and WildMultiAsset allowing either a wildcard or a list of definite (i.e. not wildcard) assets to be specified. 100 | 101 | ```rust,noplayground 102 | pub enum MultiAssetFilter { 103 | /// Specify the filter as being everything contained by the given `MultiAssets` inner. 104 | Definite(MultiAssets), 105 | /// Specify the filter as the given `WildMultiAsset` wildcard. 106 | Wild(WildMultiAsset), 107 | } 108 | ``` 109 | 110 | ## Examples 111 | ### MultiAsset 112 | For more information about the MultiLocations used to define concrete assets, see [MultiLocation](multilocation/README.md) and [Junction](multilocation/junction.md). 113 | ```rust,noplayground 114 | // Location Relay Chain 115 | // 100 Native Asset (three ways) 116 | MultiAsset {id: Concrete(MultiLocation {parents: 0, interior: Here}), fun: Fungible(100u128)}; 117 | MultiAsset {id: Here.into(), fun: 100.into()}; 118 | let _: MultiAsset = (Here, 100u128).into(); 119 | 120 | // 100 Parachain's Native Asset 121 | let _: MultiAsset = (X1(Parachain(1000)), 100u128).into(); 122 | // 100 Fungible assets in Parachain 1000 with id 1234 123 | let _: MultiAsset = (X2(Parachain(1000), GeneralIndex(1234)), 100u128).into(); 124 | // Non Fungible asset with asset class 1234 containing only one nft instance in Parachain 1000 125 | let _: MultiAsset = (X2(Parachain(1000), GeneralIndex(1234)), Undefined).into(); 126 | // Non Fungible asset with asset class 1234 and AssetInstance 1 in Parachain 1000 127 | let _: MultiAsset = (X2(Parachain(1000), GeneralIndex(1234)), Index(1)).into(); 128 | ``` 129 | 130 | ### MultiAssetFilter 131 | 132 | ```rust,noplayground 133 | let a1: MultiAssets = MultiAssets::from(vec![MultiAsset {id: Here.into(), fun: 100u128.into()}]); 134 | let b1: MultiAssets = (Here, 100u128).into(); 135 | assert_eq!(a1, b1); 136 | 137 | let a2: MultiAssetFilter = a1.into(); 138 | let b2 = MultiAssetFilter::Definite((Here, 100u128).into()); 139 | assert_eq!(a2, b2); 140 | 141 | let a3 = MultiAssetFilter::Wild(WildMultiAsset::All); 142 | let b3: MultiAssetFilter = All.into(); 143 | assert_eq!(a3, b3); 144 | ``` 145 | 146 | -------------------------------------------------------------------------------- /src/fundamentals/multilocation/README.md: -------------------------------------------------------------------------------- 1 | # MultiLocation 2 | The [MultiLocation](https://paritytech.github.io/polkadot/doc/xcm/v3/struct.MultiLocation.html) type identifies any single location that exists within the world of consensus. 3 | It can represent all manner of things that exist within consensus, from a scalable multi-shard blockchain such as Polkadot down to an ERC-20 asset account on a parachain. 4 | MultiLocations are used to identify places to send XCMs, places that can receive assets, and can even help describe the type of an asset itself, as we will see in [MultiAsset](../multiasset.md). 5 | 6 | ### Location is relative 7 | MultiLocation always expresses a location relative to the current location. 8 | It can be thought of as a file system path, without the ability to directly express the “root” of the file system tree. 9 | This is for a simple reason: In the world of Polkadot, blockchains can be merged into, and split from other blockchains. 10 | A blockchain can begin as a standalone sovereign chain, and could eventually be elevated to become a parachain within a larger consensus. 11 | If it did that, then the meaning of “root” would change overnight and this could spell chaos for XCMs and anything else using MultiLocation. 12 | To keep things simple, we exclude this possibility altogether. 13 | 14 | ### Hierarchical structure 15 | Locations in XCM are hierarchical; some places in consensus are wholly encapsulated within other places in consensus. 16 | A parachain of Polkadot exists wholly within the overall Polkadot consensus; we call this an interior location. 17 | Or a pallet exists wholly within a parachain or relay chain. 18 | Putting it more strictly, say we have two consensus systems, A and B. 19 | If any change in A implies a change in B, then we say A is interior to B. 20 | 21 | ### So what is a MultiLocation: Simple example 22 | A quick summary of the previous points: 23 | - A MultiLocation identifies any single location that exists within the world of consensus. 24 | - A MultiLocation is always relative to the current location. 25 | - MultiLocations in XCM are hierarchical. 26 | 27 | Now take a look at the MultiLocation struct: 28 | ```rust,noplayground 29 | pub struct MultiLocation { 30 | pub parents: u8, 31 | pub interior: Junctions, 32 | } 33 | ``` 34 | As we have already discussed, locations in XCM are hierarchical. 35 | The following image shows an example of such a Hierarchy. 36 | 37 | ![Simple Example](./../images/MultiLocation_simple_example.png) 38 | 39 | Relay chain A completely encapsulates Parachain A and B (indicated by the arrows) and parachain A encapsulates an account `0x00...`. 40 | So RelayA is higher in the hierarchy than ParaA and ParaB and can be described as the `parent` of these parachains. 41 | The `parents: u8` in the MultiLocation struct describes the number of steps in the hierarchy we want to move up. 42 | The `interior: Junctions` express the steps in the hierarchy we want to move down. 43 | The `Junctions` type will be further discussed in the next chapter about [Junctions](junction.md), but for now, it's just a way to express a way down the hierarchy. 44 | As all MultiLocations are relative to the current location, Parachain B relative to Parachain A is one step up and one step down in the hierarchy. 45 | 46 | To get a better understanding of this concept, we show some simple MultiLocations in the code example below. 47 | The first two examples are relative to RelayA and the second set of examples is relative to ParaB. 48 | In the `Location` comments, we expressed the locations in text. 49 | The `..` express a step up in the hierarchical structure (the “parent” or the encapsulating consensus system). 50 | The `..` are followed by some number of [Junctions](junction.md), all separated by `/`. 51 | The `X1` and `X2` variants are expressing the number of `Junction`s that we step down in the hierarchical structure (see [Junctions](junction.md) for an explanation). 52 | 53 | 54 | ```rust,noplayground 55 | // From: RelayA 56 | // To: ParaB 57 | // Location: Parachain(2000) 58 | MultiLocation {parents: 0, interior: X1(Parachain(2000))}; 59 | // To: Account in ParaA 60 | // Location: Parachain(1000)/AccountId32(0x00..) 61 | MultiLocation { 62 | parents: 0, 63 | interior: X2( 64 | Parachain(1000), 65 | AccountId32{network: None, id: [0u8; 32]} 66 | ) 67 | }; 68 | 69 | // From: ParaB 70 | // To: RelayA 71 | // Location: ../Here 72 | MultiLocation {parents: 1, interior: Here}; 73 | // To: Account in ParaA 74 | // Location: ../Parachain(1000)/AccountId32(0x00..) 75 | MultiLocation { 76 | parents: 1, 77 | interior: X2( 78 | Parachain(1000), 79 | AccountId32{network: None, id: [0u8; 32]} 80 | ) 81 | }; 82 | ``` 83 | 84 | ## What's next: 85 | - More information about [junctions](junction.md) 86 | - More MultiLocation [examples](example.md) 87 | - Expressing assets using Multilocations: [MultiAsset][../multiasset.md] -------------------------------------------------------------------------------- /src/fundamentals/multilocation/example.md: -------------------------------------------------------------------------------- 1 | # Example 2 | In this example we show different `MultiLocation`s for the system hierarchy in the image below. 3 | ![Example](./../images/MultiLocation_Example.png) 4 | 5 | From the perspective of RelayA 6 | ```rust,noplayground 7 | // ParaA 8 | let _: MultiLocation = Parachain(1000).into(); 9 | // AccountId32 in Parachain A 10 | let _: MultiLocation = (Parachain(1000), AccountId32 { network: RELAY_A_NETWORK, id: [0u8; 32]}).into(); 11 | // Asset in Parachain A 12 | let _: MultiLocation = (Parachain(1000), PalletInstance(1), GeneralIndex(1)).into(); 13 | // Ethereum based account on Parachain B 14 | let _: MultiLocation = (Parachain(2000), AccountKey20 { network: RELAY_A_NETWORK, key: [0u8; 20] }).into(); 15 | // Smart Contract 16 | let _: MultiLocation = (Parachain(2000), PalletInstance(1), AccountKey20 { network: RELAY_A_NETWORK, key: [0u8; 20] }).into(); 17 | // RelayB 18 | let _: MultiLocation = (Parent, GlobalConsensus(RELAY_B_NETWORK)).into(); 19 | // NFT on Parachain C 20 | let _: MultiLocation = (Parent, GlobalConsensus(RELAY_B_NETWORK), Parachain(1000), GeneralIndex(1)).into(); 21 | ``` 22 | 23 | From the perspective of Parachain C 24 | ```rust,noplayground 25 | // Relay A 26 | let _: MultiLocation = Parent.into(); 27 | // Plurality Example. Many more BodyId/BodyPart combos imaginable 28 | let _: MultiLocation = (Parent, Plurality { id: BodyId::Index(0), part: BodyPart::Members { count: 10 } }).into(); 29 | // Account in Relay 30 | let _: MultiLocation = (Parent, AccountId32 { network: None, id: [0u8; 32] }).into(); 31 | ``` 32 | 33 | From the perspective of the Smart Contract 34 | ```rust,noplayground 35 | // Asset in Parachain A 36 | let _: MultiLocation = (Parent, Parent, Parachain(1000), PalletInstance(1), GeneralIndex(1)).into(); 37 | 38 | ``` -------------------------------------------------------------------------------- /src/fundamentals/multilocation/junction.md: -------------------------------------------------------------------------------- 1 | # Junction(s) 2 | In the section on [MultiLocations](index.html), we looked at the MultiLocation struct. 3 | We talked about the Multilocation being a way to describe moving from one place in the system hierarchy to another. 4 | The `parents` parameter expresses the number of steps up in the hierarchy. 5 | In this section, we dive further into the MultiLocation struct and explain how we can use the Junctions type to describe steps in the system hierarchy. 6 | Take a look at the MultiLocation struct again: 7 | 8 | ```rust,noplayground 9 | pub struct MultiLocation { 10 | pub parents: u8, 11 | pub interior: Junctions, 12 | } 13 | ``` 14 | 15 | The system hierarchy consists of 1-to-n relations. 16 | Each place in the system hierarchy can only ever have one parent, so there is only one way up the hierarchy. 17 | That is why we can use a `u8` to describe the number of `parents` we want to move up. 18 | But moving down is a bit more difficult, as one consensus system can encapsulate multiple other consensus systems(e.g. a relay chain can have multiple parachains). 19 | So to describe the correct steps down the hierarchy, we use the `Junctions` [type](https://paritytech.github.io/polkadot/doc/xcm/v3/enum.Junctions.html). 20 | 21 | 22 | ## Junctions Type 23 | ```rust,noplayground 24 | pub enum Junctions { 25 | /// The interpreting consensus system. 26 | Here, 27 | /// A relative path comprising 1 junction. 28 | X1(Junction), 29 | ... 30 | /// A relative path comprising 8 junctions. 31 | X8(Junction, Junction, Junction, Junction, Junction, Junction, Junction, Junction), 32 | } 33 | ``` 34 | The `Junctions` enum can represent zero to eight steps down the hierarchy. 35 | When the `Here` variant is used, it means that we do not have to take steps down the hierarchy. 36 | We can for example describe the current location with `{parents: 0, interior: Here}` or the Parent location with `{parents: 1, interior: Here}`. 37 | If we want to take steps down the hierarchy, we express each step as a Junction. 38 | 39 | ## Junction Type 40 | A [Junction](https://paritytech.github.io/polkadot/doc/xcm/v3/enum.Junction.html) describes a step down in the Hierarchy. 41 | The `Junction`s are defined as follows: 42 | 43 | ```rust,noplayground 44 | pub enum Junction { 45 | Parachain(u32), 46 | AccountId32 { 47 | network: Option, 48 | id: [u8; 32], 49 | }, 50 | AccountIndex64 { 51 | network: Option, 52 | index: u64, 53 | }, 54 | AccountKey20 { 55 | network: Option, 56 | key: [u8; 20], 57 | }, 58 | PalletInstance(u8), 59 | GeneralIndex(u128), 60 | GeneralKey { 61 | length: u8, 62 | data: [u8; 32], 63 | }, 64 | OnlyChild, 65 | Plurality { 66 | id: BodyId, 67 | part: BodyPart, 68 | }, 69 | GlobalConsensus(NetworkId), 70 | } 71 | ``` 72 | 73 | #### Parachain 74 | The `Parachain` junction is used to describe a parachain from the point of a relay chain. 75 | Each parachain has an Id, e.g. Statemine in the Kusama network has Id 1000. 76 | 77 | #### PalletInstance 78 | The `PalletInstance` junction is used to describe a pallet in one of the parachains or relay chain. 79 | Each pallet has an Id that can be used for the `PalletInstance`. 80 | This junction is mainly used for FRAME based systems. 81 | 82 | #### AccountId32 and AccountKey20 83 | Each of these junctions can be used to describe an account located in the current consensus system. 84 | The `AccountId32` is used to describe substrate-based accounts, while the `AccountKey20` is mainly used to describe Ethereum or Bitcoin-based accounts or smart contracts. 85 | Both junctions express an account based on the context they are used in. 86 | If the current location is the relay chain, then the junctions describe an account in the relay chain. 87 | The same is true for each parachain location. 88 | 89 | #### GeneralIndex and GeneralKey 90 | Non-descript indices and keys within the current context location. 91 | The usage will vary widely owing to its generality. 92 | An example use case for the `GeneralIndex` is to describe an Asset within an Assets Parachain. 93 | 94 | NOTE: If possible, try to avoid using this and instead use a more specific junction. 95 | 96 | #### AccountIndex64 97 | The `AccountIndex64` can be used to describe an account index. 98 | This may be used when the context is a Frame-based chain and includes e.g. an indices pallet. 99 | 100 | #### OnlyChild 101 | The `OnlyChild` junction can be used to describe the child of a location if there exists a 1-to-1 relation between the parent and child in the system hierarchy. 102 | The `OnlyChild` junction is currently not used except as a fallback when deriving context. 103 | 104 | #### Plurality 105 | The `Plurality` junction is used to describe a pluralistic body existing within the current consensus location. 106 | Typical to be used to represent a governance origin of a chain, but could in principle be used to represent 107 | things such as multisigs also. 108 | See the [BodyId documentation](https://paritytech.github.io/polkadot/doc/xcm/v3/enum.BodyId.html) for a better understanding of the bodies that the `Plurality` junction can represent. 109 | 110 | #### GlobalConsensus 111 | A global network (e.g. Polkadot or Kusama) is capable of externalizing its own consensus. 112 | This is not generally meaningful outside of the universal level. 113 | An example would be describing the Kusama relay chain from the perspective of the Polkadot relay chain as `{parents: 1, interior: GlobalConsensus(Kusama)}`. 114 | An example use case could be routing XCMs between global consensus networks using bridges. 115 | 116 | ## Multiple ways to create a MultiLocation 117 | ```rust,noplayground 118 | // Current Location 119 | MultiLocation {parents: 0, interior: Here}; 120 | MultiLocation::new(0, Here); 121 | MultiLocation::here(); 122 | MultiLocation::default(); 123 | let _: MultiLocation = Here.into(); 124 | 125 | // Parent Location 126 | MultiLocation {parents: 1, interior: Here}; 127 | MultiLocation::parent(); 128 | let _: MultiLocation = Parent.into(); 129 | 130 | // Conversion 131 | MultiLocation { parents: 2, interior: X2(Parachain(1), GeneralIndex(1))}; 132 | let _: MultiLocation = (Parent, Parent, Parachain(1), GeneralIndex(1)).into(); 133 | ``` 134 | 135 | -------------------------------------------------------------------------------- /src/fundamentals/weight_and_fees.md: -------------------------------------------------------------------------------- 1 | # Weight and fees 2 | 3 | The resources available to a blockchain are limited, so it's important to manage how operations on-chain use them. 4 | Not managing how resources are used can open an attack vector, known as DoS (Denial of Service), where an attacker floods the chain with operations in order to get it to stop producing blocks. 5 | In order to manage how resources are used and to protect against DoS attacks, XCM uses a concept of *weight*. 6 | This concept, which has the purpose of quantifying usage of blockchain resources, comes from the [Substrate](https://docs.substrate.io/build/tx-weights-fees/) world. 7 | 8 | Weight is two-dimensional, it tracks both time (execution time) and space (state accesses). 9 | Weight determines how much fees need to be paid in order to perform some operation. 10 | The logic for turning it into fees is configurable. 11 | 12 | Some systems have the concept of *gas metering*, which is calculated during execution and only measures execution time. 13 | Weight, however, is static, defined beforehand, which makes XCM execution lighter by not including gas metering. 14 | 15 | The principle behind weight payment is to pay for what you use, so the two stages of XCM where fees are paid are *sending* the message and actually *executing* it. 16 | The fees for sending are paid on the local system, usually by the origin of the message, because we are using the message delivery mechanism maintained by the origin. 17 | Similarly, the execution fees are paid on the destination system, via the `BuyExecution` instruction. In other words, XCMs are paid for via their own instructions. 18 | We'll talk more about `BuyExecution` in the [fee handling chapter](../journey/fees/index.html). 19 | 20 | XCM is agnostic, which means it doesn't assume fees need to be paid. 21 | It's entirely possible to not pay for the effects of an XCM on the destination system. 22 | Even in systems where fees have to be paid, special cases of free execution can be made. 23 | There are security measures systems can put in place (see [barrier](../executor_config/index.html#barrier)) to not execute XCMs that do not pay for their fees. 24 | 25 | ## Executor config 26 | 27 | The executor has a `Weigher` [configuration item](../executor_config/index.html#weigher) that specifies the weight of each instruction. 28 | It weighs the whole message by adding the weight of each instruction. 29 | A simple way of weighing instructions is to assign them a base weight value to all of them. 30 | This works, but it is not very accurate, as different instructions use more resources when being executed. 31 | A better approach is to benchmark each instruction to find out the actual weight used by each. 32 | 33 | Another configuration item, `Trader`, converts the required weight units into fees, which are represented as `MultiAsset`s. 34 | There are two basic approaches: one is to just assign a value (measured in assets) to each unit of weight; the other is to reuse some existing transaction payment method for XCM weight. 35 | Custom configurations allow for things like NFT coupons that give you a certain amount of weight for executing the XCM. 36 | 37 | Naturally, this configuration items allow for any approach you can think of for weighing messages and charging execution fees. 38 | 39 | ## XCM pallet 40 | 41 | FRAME pallets, like the XCM pallet, specify weights for each extrinsic they expose. 42 | That means that when interacting with pallets that deal with XCM, there will be an additional fee at the beginning for calling the extrinsic locally. 43 | -------------------------------------------------------------------------------- /src/fundamentals/xcvm.md: -------------------------------------------------------------------------------- 1 | # XCVM 2 | 3 | We've already seen an overview of the XCVM. 4 | In this section, we'll dive deeper into how it works. 5 | 6 | ## Coming soon 7 | 8 | This chapter is still being worked on. 9 | -------------------------------------------------------------------------------- /src/journey/README.md: -------------------------------------------------------------------------------- 1 | # A Journey through XCM 2 | 3 | This section will be a step-by-step, practical introduction to all the features XCM has. 4 | We'll create XCMs for a variety of use cases, learning about all the instructions available to us along the way. 5 | Let's step right in. 6 | -------------------------------------------------------------------------------- /src/journey/channels-and-bridges.md: -------------------------------------------------------------------------------- 1 | # Channels 2 | XCM has instructions that aid in the establishment of a HRMP channel between parachains. 3 | HRMP channels are always unidirectional (one-way); every channel has a static sender and a static recipient. 4 | To send messages in the opposite direction (i.e. from recipient to sender), another new HRMP channel must be opened. 5 | Unlike other XCM instructions, these HRMP instructions are related to the underlying transport mechanism, and will normally not be sent by developers. 6 | We still want to list them, as they are part of XCM: 7 | 8 | - `HrmpNewChannelOpenRequest` 9 | - `HrmpChannelAccepted` 10 | - `HrmpChannelClosing` 11 | 12 | ## HrmpNewChannelOpenRequest 13 | ```rust,noplayground 14 | HrmpNewChannelOpenRequest { 15 | #[codec(compact)] 16 | sender: u32, 17 | #[codec(compact)] 18 | max_message_size: u32, 19 | #[codec(compact)] 20 | max_capacity: u32, 21 | } 22 | ``` 23 | The `HrmpNewChannelOpenRequest` is an instruction to notify about a new incoming HRMP channel. 24 | This message is meant to be sent by the relay chain to a parachain. 25 | 26 | The `sender` field represents the ParaId of the parachain initializing the channel. 27 | This parachain will also be the sender in the to-be opened channel. 28 | 29 | The `max_message_size` field is the maximum size of a message that is send through the channel. 30 | This field is the size proposed by the sender, and needs to be accepted by the recipient. 31 | 32 | The `max_capacity` is the maximum number of messages that can be queued in the channel. 33 | 34 | ## HrmpChannelAccepted 35 | ```rust,noplayground 36 | HrmpChannelAccepted { 37 | #[codec(compact)] 38 | recipient: u32, 39 | } 40 | ``` 41 | The `HrmpChannelAccepted` instruction is used to notify about that a previously sent open channel request has been accepted by the recipient. 42 | That means that the channel will be opened during the next relay chain session change. 43 | This message is meant to be sent by the relay chain to a parachain. 44 | 45 | The `recipient` field represents the ParaId of the parachain that initialized the channel, so it equals the `sender` field in the preceding `HrmpNewChannelOpenRequest` instruction. 46 | 47 | ## HrmpChannelClosing 48 | ```rust,noplayground 49 | HrmpChannelClosing { 50 | #[codec(compact)] 51 | initiator: u32, 52 | #[codec(compact)] 53 | sender: u32, 54 | #[codec(compact)] 55 | recipient: u32, 56 | } 57 | ``` 58 | 59 | The `HrmpChannelClosing` instruction is used to notify that the other party in an open channel decided to close it. 60 | In particular, `initiator` is going to close the channel opened from `sender` to the `recipient`. 61 | The close will be enacted at the next relay chain session change. 62 | This message is meant to be sent by the relay chain to a para. 63 | 64 | The `initiator` field represents the ParaId of the parachain that is closing the channel. 65 | It is equal to either the `sender` or `recipient` field. 66 | 67 | The `sender` field represents the ParaId of the parachain that is the sender side of the channel. 68 | 69 | The `recipient` field represents the ParaId of the parachain that is the recipient side of the channel. 70 | 71 | Important to note is that both the sender and recipient can close the channel. 72 | 73 | 74 | # Message Export (Bridging) 75 | 76 | XCM has an instruction that allows us to send an XCM to a Non-Local Consensus System, meaning to MultiLocation that is outside our current GlobalConsensus. 77 | For example, it allows us to send an XCM from Kusama to Polkadot or from Polkadot to an Ethereum-based chain. 78 | Exporting an XCM to another Non-Local Consensus System will tend to utilize some extra consensus layer/mechanism, the obvious one being a bridge. 79 | The instruction to export an XCM is called `ExportMessage`. 80 | 81 | ## ExportMessage 82 | ```rust,noplayground 83 | ExportMessage { network: NetworkId, destination: InteriorMultiLocation, xcm: Xcm<()> }, 84 | ``` 85 | The `ExportMessage` instruction can be used to export a message to a Non-Local Consensus System. 86 | The message is sent to the bridge (or other consensus mechanism) that is able to export the message. 87 | A fee is charged for exporting the message via the bridge. 88 | 89 | The `network` field is the remote consensus system to which the message should be exported. 90 | 91 | The `destination` field is the location relative to the remote consensus system to which the message should be sent on arrival. 92 | 93 | The `xcm` field is the message to be exported. 94 | 95 | As an example, to export a message for execution on Statemine (parachain `#1000` in the Kusama network), you would call with `network: NetworkId::Kusama` and `destination: X1(Parachain(1000))`. 96 | Alternatively, to export a message for execution on Polkadot, you would call with `network: NetworkId:: Polkadot` and `destination: Here`. -------------------------------------------------------------------------------- /src/journey/expects.md: -------------------------------------------------------------------------------- 1 | # Expects 2 | XCM contains instructions to check for specific conditions during the execution of the message. 3 | These 'expect' instructions check for a specific condition and if it's not fulfilled, an error is then thrown. 4 | These instructions are used for things like checking the state of the registers before executing specific instructions. 5 | XCM contains the following expect instructions: 6 | - `ExpectAsset` 7 | - `ExpectOrigin` 8 | - `ExpectPallet` 9 | - `ExpectError` 10 | - `ExpectTransactStatus` 11 | 12 | 13 | ## ExpectAsset 14 | The `ExpectAsset` instruction throws an `ExpectationFalse` error if the holding register does not contain at least the given assets. 15 | ```rust,noplayground 16 | ExpectAsset(MultiAssets) 17 | ``` 18 | 19 | ### Example 20 | 21 | For the full example, check [here](https://github.com/paritytech/xcm-docs/tree/main/examples). 22 | 23 | ```rust, noplayground 24 | WithdrawAsset((Here, AMOUNT).into()), 25 | BuyExecution { fees: (Here, AMOUNT).into(), weight_limit: WeightLimit::Unlimited }, 26 | // Set the instructions that are executed when ExpectAsset does not pass. 27 | // In this case, reporting back an error to the Parachain. 28 | SetErrorHandler(Xcm(vec![ 29 | ReportError(QueryResponseInfo { 30 | destination: Parachain(1).into(), 31 | query_id: QUERY_ID, 32 | max_weight: Weight::from_all(0), 33 | }) 34 | ])), 35 | ExpectAsset((Here, AMOUNT + 10).into()), 36 | // Add Instructions that do something with assets in holding when ExpectAsset passes. 37 | 38 | ``` 39 | 40 | ## ExpectOrigin 41 | The `ExpectOrigin` instruction throws an `ExpectationFalse` error if the origin register does not equal the expected origin. 42 | ```rust,noplayground 43 | ExpectOrigin(Option) 44 | ``` 45 | 46 | ### Example 47 | 48 | For the full example, check [here](https://github.com/paritytech/xcm-docs/tree/main/examples). 49 | The `ExpectOrigin` instruction errors because the `ClearOrigin` clears the origin register and we expect it to be equal to `Parachain(1)`. 50 | ```rust,noplayground 51 | // Set the instructions that are executed when ExpectOrigin does not pass. 52 | // In this case, reporting back an error to the Parachain. 53 | SetErrorHandler(Xcm(vec![ReportError(QueryResponseInfo { 54 | destination: Parachain(1).into(), 55 | query_id: QUERY_ID, 56 | max_weight: Weight::from_all(0), 57 | })])), 58 | ClearOrigin, 59 | // Checks if the XcmContext origin is equal to `Parachain(1)`. 60 | ExpectOrigin(Some(Parachain(1).into())), 61 | ``` 62 | 63 | ## ExpectPallet 64 | The `ExpectPallet` instruction ensures that a particular pallet with a particular version exists in the destination's runtime. 65 | It throws a `PalletNotFound` error if there is no pallet at the given index. 66 | It throws a `NameMismatch` error is the `name` or `module_name` mismatch and a `VersionIncompatible` error if the `crate_major` or `crate_minor` mismatch. 67 | The `name` and `module_name` represent a byte representation of the pallet's name and module name (e.g. 'Balances' and 'pallet_balances'). 68 | Consensus systems that are not substrate-based may throw an `Unimplemented` error for this instruction. 69 | 70 | ```rust,noplayground 71 | ExpectPallet { 72 | #[codec(compact)] 73 | index: u32, 74 | name: Vec, 75 | module_name: Vec, 76 | #[codec(compact)] 77 | crate_major: u32, 78 | #[codec(compact)] 79 | min_crate_minor: u32, 80 | }, 81 | ``` 82 | 83 | ### Example 84 | For the full example, check [here](https://github.com/paritytech/xcm-docs/tree/main/examples). 85 | ```rust, noplayground 86 | // Set the instructions that are executed when ExpectPallet does not pass. 87 | // In this case, reporting back an error to the Parachain. 88 | SetErrorHandler(Xcm(vec![ 89 | ReportError(QueryResponseInfo { 90 | destination: Parachain(1).into(), 91 | query_id: QUERY_ID, 92 | max_weight: Weight::from_all(0), 93 | }) 94 | ])), 95 | // Configured pallet has different `crate_major` so `VersionIncompatible` error is thrown. 96 | ExpectPallet { 97 | index: 1, 98 | name: "Balances".into(), 99 | module_name: "pallet_balances".into(), 100 | crate_major: 3, 101 | min_crate_minor: 0, 102 | } 103 | ``` 104 | 105 | ## ExpectError 106 | The `ExpectError` instruction throws an `ExpectationFalse` error if the error register does not equal the expected error at that point in the execution. 107 | This instruction is useful during the error handler execution to halt the error handler if the error that started the execution of the error handler is not as expected. 108 | The `ExpectError` instruction allows to only execute the instructions in the error handler, when a specific error is thrown. 109 | ```rust,noplayground 110 | ExpectError(Option<(u32, Error)>) 111 | ``` 112 | 113 | ### Example 114 | 115 | For the full example, check [here](https://github.com/paritytech/xcm-docs/tree/main/examples). 116 | 117 | ```rust,noplayground 118 | SetErrorHandler(Xcm(vec![ 119 | ExpectError(Some((1, XcmError::VersionIncompatible))), 120 | ReportError(QueryResponseInfo { 121 | destination: Parachain(1).into(), 122 | query_id: QUERY_ID, 123 | max_weight: Weight::from_all(0), 124 | }), 125 | ])), 126 | // Pallet index is wrong, so throws `PalletNotFound` error. 127 | ExpectPallet { 128 | index: 100, 129 | name: "Balances".into(), 130 | module_name: "pallet_balances".into(), 131 | crate_major: 4, 132 | min_crate_minor: 0, 133 | }, 134 | ``` 135 | 136 | ## ExpectTransactStatus 137 | The `ExpectTransactStatus` instruction throws an `ExpectationFalse` error if the transact status register does not equal the expected transact status. 138 | 139 | ### Example 140 | 141 | For the full example, check [here](https://github.com/paritytech/xcm-docs/tree/main/examples). 142 | The transact status is reported to `Parachain(1)` if the call in the `Transact` errors. 143 | 144 | ```rust,noplayground 145 | SetErrorHandler(Xcm(vec![ReportTransactStatus(QueryResponseInfo { 146 | destination: Parachain(1).into(), 147 | query_id: QUERY_ID, 148 | max_weight: Weight::from_all(0), 149 | })])), 150 | Transact { 151 | origin_kind: OriginKind::SovereignAccount, 152 | require_weight_at_most: Weight::from_parts(INITIAL_BALANCE as u64, 1024 * 1024), 153 | call: call.encode().into(), 154 | }, 155 | ExpectTransactStatus(MaybeErrorCode::Success), 156 | ``` -------------------------------------------------------------------------------- /src/journey/fees/README.md: -------------------------------------------------------------------------------- 1 | # Fee handling 2 | 3 | Like we learnt in the [weight and fees](../../fundamentals/weight_and_fees.md) chapter, the XCM operations our messages perform need to be paid for. 4 | To accomplish this, we'll make use of different instructions in this chapter. 5 | 6 | ## BuyExecution 7 | 8 | ```rust,noplayground 9 | BuyExecution { fees: MultiAsset, weight_limit: WeightLimit } 10 | ``` 11 | 12 | This instruction is used to buy weight using fees. 13 | While in some cases there's no need to pay for execution (if you control both systems for example), in most cases you'll need to add this instruction. 14 | There's a predefined [barrier](../../executor_config/index.md#barrier), `AllowTopLevelPaidExecutionFrom`, that explicitly drops messages that do not include this instruction. 15 | 16 | Let's grab the teleport message from the [transfers chapter](../transfers/teleports.md) and add fee payment. 17 | 18 | ```rust,noplayground 19 | let message = Xcm(vec![ 20 | WithdrawAsset((Here, withdraw_amount + fee_estimation).into()), 21 | BuyExecution { // <-- Added here 22 | fees: (Here, fee_estimation).into(), 23 | weight_limit: WeightLimit::Limited(weight_estimation), 24 | }, 25 | InitiateTeleport { 26 | assets: All.into(), 27 | dest: Parachain(1).into(), 28 | xcm: Xcm(vec![DepositAsset { 29 | assets: All.into(), 30 | beneficiary: Junction::AccountId32 { 31 | network: None, 32 | id: ALICE.into(), 33 | }, 34 | }]), 35 | }, 36 | ]); 37 | ``` 38 | 39 | `fee_estimation` and `weight_estimation` are values that can be calculated from the configuration of the receiving chain. 40 | As mentioned in the [weight and fees](../../fundamentals/weight_and_fees.md) chapter of the fundamentals, XCMs instructions are usually assigned weights separately, so, in order to estimate the weight, you need to estimate the weight of every instruction and add them together. 41 | By using `WeightLimit::Limited()`, you guarantee the message will error if it tries to use more weight than you expect, if you don't mind this, you can use `WeightLimit::Unlimited`. 42 | The `fee_estimation` value is the maximum assets you want to use, if it doesn't cover all fees, message execution will fail. 43 | You can add a higher value (all of `withdraw_amount` for example) to make sure you have enough assets for fee payment. 44 | If you plan to use the entirety of `withdraw_amount`, however, it's recommended to add a little extra for fee payment. 45 | 46 | In our examples, we use a very simple method, where all instructions weigh a constant value. 47 | This is very useful for testing purposes, but it's recommended to actually benchmark every instruction as they differ in resource usage. 48 | Given our setup, we estimate the weight and fee using only the number of instructions in each message. 49 | 50 | ## SetFeesMode 51 | 52 | ```rust,noplayground 53 | SetFeesMode { jit_withdraw: bool } 54 | ``` 55 | 56 | This instruction changes the fee mode of the XCVM. 57 | If `jit_withdraw` is set to true, then fee assets are taken directly from the origin's on-chain account, instead of the holding register. 58 | This means the fees are taken directly from the account, no need for a `BuyExecution` instruction. 59 | That means you make sure the message will get executed, as long as there are enough assets in the account. 60 | It's useful when paying sending fees, which are difficult to estimate, as they usually depend on network congestion. 61 | 62 | ## UnpaidExecution 63 | 64 | ```rust,noplayground 65 | UnpaidExecution { weight_limit: WeightLimit, check_origin: Option } 66 | ``` 67 | 68 | This instruction is used for explicitly stating this message shouldn't be paid for. 69 | It can be used as a way of identifying certain priviledged messages that don't pay fees, coming from a particular system. 70 | This instruction can be searched for in [barriers](../../executor_config/index.md#barrier) to allow this. 71 | Make sure you trust the origin system because it won't be paying fees. 72 | There's already a predefined barrier in xcm-builder, `AllowExplicitUnpaidExecutionFrom`, that makes sure this is the first instruction in the message. 73 | As always, you can build your own for your own use-cases. 74 | 75 | This is safer than allowing all messages from a particular system to not pay fees, as it's an exception to the rule and not the default. 76 | Extra measures can be taken to limit who can use this instruction. 77 | 78 | ## RefundSurplus 79 | 80 | ```rust,noplayground 81 | RefundSurplus 82 | ``` 83 | 84 | Refunds any surplus weight previously bought with `BuyExecution`. 85 | This is useful in many cases: 86 | - When you pay for execution of your whole message, but there's an error and not all instructions get executed 87 | - When you set an error handler, buy weight for it, but in the end there's no error so it doesn't get called 88 | - When you use the [`Transact` instruction](../transact.md) and the call takes less weight than expected 89 | 90 | ### Example 91 | 92 | ```rust,noplayground 93 | let message = Xcm(vec![ 94 | WithdrawAsset((Parent, message_fee).into()), 95 | BuyExecution { 96 | fees: (Parent, message_fee).into(), 97 | weight_limit: WeightLimit::Unlimited, 98 | }, 99 | SetErrorHandler(Xcm(vec![ 100 | RefundSurplus, 101 | DepositAsset { 102 | assets: All.into(), 103 | beneficiary: AccountId32 { 104 | network: Some(ByGenesis([0; 32])), 105 | id: relay_sovereign_account_id().into(), 106 | } 107 | .into(), 108 | }, 109 | ])), 110 | Trap(1), 111 | ClearOrigin, 112 | ClearOrigin, 113 | ClearOrigin, 114 | ]); 115 | ``` 116 | 117 | In this example, we pay upfront for all the instructions in the XCM. 118 | When the `Trap` instruction throws an error, the error handler will be called and the weight for all the instructions that weren't executed is refunded. 119 | For the full example, check our [repo](https://github.com/paritytech/xcm-docs/tree/main/examples). 120 | -------------------------------------------------------------------------------- /src/journey/holding-modifiers.md: -------------------------------------------------------------------------------- 1 | # Holding Register Modifiers 2 | Most of the XCM instructions alter the Holding Register. We already have seen instructions that alter the Holding Register, like the `WithdrawAsset` or `DepositAsset` instructions. In this chapter we go over more instructions that alter the holding register, namely: 3 | 4 | - BurnAsset 5 | - ExchangeAsset 6 | 7 | ## BurnAsset 8 | ```rust,noplayground 9 | BurnAsset(MultiAssets) 10 | ``` 11 | The `BurnAsset` instruction allows for the reduction of assets in the Holding Register by up to the specified assets. The execution of the instruction does not throw an error if the Holding Register does not contain the assets (to make this an error, use `ExpectAsset` prior). 12 | 13 | ### Example 14 | For the full example, check [the repo](https://github.com/paritytech/xcm-docs/tree/main/examples). 15 | The Scenario of the example is as follows: 16 | Parachain A withdraws 10 units from its sovereign account on the relay chain and burns 4 of them. 17 | The relay chain then reports back the status of the Holding Register to Parachain A. We expect the Holding Register to hold 6 units. 18 | Note: If we would have added more then 10 units worth of assets in the `BurnAsset` instruction, we would have burned all assets in the Holding Register and the execution would succeed. 19 | ```rust,noplayground 20 | let message = Xcm(vec![ 21 | WithdrawAsset((Here, 10 * CENTS).into()), 22 | BuyExecution { fees: (Here, CENTS).into(), weight_limit: WeightLimit::Unlimited }, 23 | BurnAsset((Here, 4 * CENTS).into()), 24 | ReportHolding { 25 | response_info: QueryResponseInfo { 26 | destination: Parachain(1).into(), 27 | query_id: QUERY_ID, 28 | max_weight: Weight::from_parts(1_000_000_000, 64*64) }, 29 | assets: All.into() 30 | } 31 | ]); 32 | ``` 33 | 34 | We expect the following response: 35 | ```rust,noplayground 36 | Response::Assets((Parent, 6 * CENTS).into()) 37 | ``` 38 | 39 | 40 | ## ExchangeAsset 41 | ```rust,noplayground 42 | ExchangeAsset { give: MultiAssetFilter, want: MultiAssets, maximal: bool } 43 | ``` 44 | The `ExchangeAsset` instruction allows us to remove asset(s) (`give`) from the Holding Register and replace them with alternative 45 | assets (`want`). The `ExchangeAsset` instruction has three fields. 46 | 47 | The `give` field indicates the maximum number of assets that can be removed from the Holding register. 48 | 49 | The `want` field indicates the minimum amount of assets which `give` should be exchanged for. We should at a mimimum get the assets in `want` for the execution of the instruction not to fail. 50 | 51 | If the `maximal` field is `true`, then we prefer to give as much as possible up to the limit of `give` 52 | and receive accordingly more assets then stated in `want`. If the `maximal` field is `false`, then we prefer to give as little as possible in 53 | order to receive as little as possible while receiving at least `want`. 54 | 55 | ### Example 56 | The full example can be found in [the repo](https://github.com/paritytech/xcm-docs/tree/main/examples). 57 | 58 | The scenario for the example is this: 59 | Scenario: 60 | The relay chain sends an XCM to Parachain A that: 61 | .1 Withdraws some native assets 62 | .2 Exchanges these assets for relay chain derivative tokens, with maximal set to true. 63 | .3 Deposit all the assets that are in the Holding in the account of Alice. 64 | 65 | NOTE: The implementation of the AssetExchanger is simple 66 | and in this case swaps all the assets in the exchange for the assets in `give`. 67 | Depending on the implementation of AssetExchanger, the test results could differ. 68 | 69 | The Assets in the exchange in Parachain(1). This is a custom exchange implementation just for testing purposes. 70 | ```rust,noplayground 71 | let assets_in_exchange = vec![(Parent, 10 * CENTS).into()]; 72 | parachain::set_exchange_assets(assets_in_exchange); 73 | ``` 74 | 75 | The message that is send: 76 | ```rust,noplayground 77 | let message = Xcm(vec![ 78 | WithdrawAsset((Here, 10 * CENTS).into()), 79 | BuyExecution { fees: (Here, CENTS).into(), weight_limit: WeightLimit::Unlimited }, 80 | // Maximal field set to true. 81 | ExchangeAsset { 82 | give: Definite((Here, 5 * CENTS).into()), 83 | want: (Parent, 5 * CENTS).into(), 84 | maximal: true, 85 | }, 86 | DepositAsset { 87 | assets: AllCounted(2).into(), 88 | beneficiary: AccountId32 { 89 | network: Some(parachain::RelayNetwork::get()), 90 | id: ALICE.into(), 91 | } 92 | .into(), 93 | }, 94 | ]); 95 | ``` 96 | 97 | Alice receives `5 CENTS` worth of native assets (`Here`) and `5 CENTS` worth of relay chain derivative assets (`Parent`). -------------------------------------------------------------------------------- /src/journey/locks/images/Example1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paritytech/xcm-docs/c86f214b5bc47e100d5b85cc436c6a6a453c81f8/src/journey/locks/images/Example1.png -------------------------------------------------------------------------------- /src/journey/locks/images/Example2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paritytech/xcm-docs/c86f214b5bc47e100d5b85cc436c6a6a453c81f8/src/journey/locks/images/Example2.png -------------------------------------------------------------------------------- /src/journey/locks/locks.md: -------------------------------------------------------------------------------- 1 | # Locking 2 | Assets can be locked via XCM, meaning, the transfer or withdrawal of assets can be restricted via messages. 3 | The XCM locking mechanism consists of four instructions: `LockAsset`, `UnlockAsset`, `NoteUnlockable`, and `RequestUnlock`. 4 | Let's explore each instruction in detail: 5 | 6 | ## LockAsset 7 | ```rust,noplayground 8 | LockAsset { asset: MultiAsset, unlocker: MultiLocation } 9 | ``` 10 | The LockAsset instruction is used to lock locally held assets and prevent further transfers or withdrawals. 11 | This instruction requires two parameters: 12 | 13 | - `asset`: The asset(s) to be locked. 14 | - `unlocker`: The MultiLocation that can unlock the asset(s). This value must match the origin of a corresponding `UnlockAsset` instruction to unlock the asset. 15 | 16 | When the locking operation succeeds, a `NoteUnlockable` instruction is sent to the unlocker. 17 | This instruction serves as a notification that the asset is now unlockable. 18 | 19 | ## UnlockAsset 20 | ```rust,noplayground 21 | UnlockAsset { asset: MultiAsset, target: MultiLocation } 22 | ``` 23 | The `UnlockAsset` instruction removes the lock on a specific asset on the local chain, allowing it to be transferred if there are no other restrictions. 24 | The following parameters are required: 25 | 26 | - `asset`: The asset to be unlocked. 27 | - `target`: The owner of the asset on the local chain. 28 | 29 | 30 | ## NoteUnlockable 31 | ```rust,noplayground 32 | NoteUnlockable { asset: MultiAsset, owner: MultiLocation } 33 | ``` 34 | The `NoteUnlockable` instruction indicates that an asset has been locked on the system which the message originated from. 35 | The locked assets can only be unlocked by receiving an `UnlockAsset` instruction from this chain. 36 | This instruction requires the following parameters: 37 | 38 | - `asset`: The asset(s) which are now unlockable from this origin. 39 | - `owner`: The owner of the asset on the chain in which it was locked. This may be a location specific to the origin network. 40 | The owner can request this origin to unlock the assets using a `RequestUnlock` instruction. 41 | However, the owner is not able to unlock the assets themselves. 42 | 43 | It is essential to trust the origin to have locked the corresponding asset before sending this message. 44 | 45 | 46 | ## RequestUnlock 47 | ```rust, noplayground 48 | RequestUnlock { asset: MultiAsset, locker: MultiLocation } 49 | ``` 50 | The `RequestUnlock` instruction is used to send an `UnlockAsset` instruction to the `locker` for a given asset. 51 | The following parameters are required: 52 | 53 | - `asset`: The asset(s) to be unlocked. 54 | - `locker`: The location from which a previous `NoteUnlockable` was sent, and where the `UnlockAsset` instruction should be sent. 55 | 56 | 57 | ## Example 58 | To get a better grasp on how these instructions work together, we give two examples in this section. 59 | The examples use the xcm-executor with the pallet-xcm as the implementation for the `AssetLocker` config item. 60 | An important note of this implementation is that only one lock with ID `py/xcmlk` is set per account. 61 | The pallet-xcm implementation keeps track of all the xcm-related locks that are placed on an account and sets the most restricting one with the `py/xcmlk` lock ID. 62 | This principle becomes more clear in the second example. 63 | 64 | 65 | ### Example 1 66 | Check out the full [example code](https://github.com/paritytech/xcm-docs/tree/main/examples). 67 | The scenario of this example is as follows: 68 | 69 | Parachain A locks 5 Cents of relay chain native assets of its Sovereign account on the relay chain and assigns Parachain B as unlocker. 70 | Parachain A then asks Parachain B to unlock the funds partly. 71 | Parachain B responds by sending an UnlockAssets instruction to the relay chain. 72 | 73 | ![Example](./images/Example1.png) 74 | 75 | 1. send `LockAsset` instruction from ParaA to relay. 76 | ```rust,noplayground 77 | ParaA::execute_with(|| { 78 | let message = Xcm(vec![LockAsset { 79 | asset: (Here, CENTS * 5).into(), 80 | unlocker: (Parachain(2)).into(), 81 | }]); 82 | assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent, message.clone())); 83 | }); 84 | ``` 85 | 86 | 2. Parachain B receives this `NoteUnlockable` instruction from the relay chain. 87 | ```rust,noplayground 88 | NoteUnlockable { 89 | owner: (Parent, Parachain(1)).into(), 90 | asset: (Parent, CENTS * 5).into() 91 | } 92 | ``` 93 | 94 | 3. Parachain A sends `RequestUnlock` instruction to Parachain B 95 | ```rust,noplayground 96 | ParaA::execute_with(|| { 97 | let message = Xcm(vec![RequestUnlock { 98 | asset: (Parent, 3 * CENTS).into(), 99 | locker: Parent.into(), 100 | }]); 101 | assert_ok!(ParachainPalletXcm::send_xcm(Here, (Parent, Parachain(2)), message.clone())); 102 | }); 103 | ``` 104 | 105 | 4. Parachain B sends an `UnlockAsset` instruction to the relay chain. We check if the lock is updated accordingly: 106 | ```rust,noplayground 107 | assert_eq!( 108 | relay_chain::Balances::locks(¶chain_sovereign_account_id(1)), 109 | vec![BalanceLock { id: *b"py/xcmlk", amount: 2 * CENTS, reasons: Reasons::All }] 110 | ); 111 | ``` 112 | 113 | 114 | ### Example 2 115 | 116 | Check out the full [example code](https://github.com/paritytech/xcm-docs/tree/main/examples). 117 | The scenario of this example is as follows: 118 | 119 | Parachain A sets two locks on the relay chain with as unlockers Parachain B and Parachain C. 120 | Parachain A then requests Parachain B to partly unlock. 121 | 122 | Note: The locks overlap. When there are two or more locks, the total assets that are locked is equal to the most restrictive lock (the lock that locks the most assets). When the most restrictive lock is unlocked, the total locked assets is than equal to the next most restrictive lock. 123 | 124 | ![Example](./images/Example2.png) 125 | 126 | 1. Set locks on the relay chain. Unlockers: B, C; Locks registered in pallet-xcm: 10, 5. Lock set in pallet-balances: 10. 127 | 128 | ```rust, noplayground 129 | ParaA::execute_with(|| { 130 | let message = Xcm(vec![ 131 | LockAsset { asset: (Here, 10 * CENTS).into(), unlocker: (Parachain(2)).into() }, 132 | LockAsset { asset: (Here, 5 * CENTS).into(), unlocker: (Parachain(3)).into() }, 133 | ]); 134 | assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent, message.clone())); 135 | }); 136 | 137 | Relay::execute_with(|| { 138 | assert_eq!( 139 | relay_chain::Balances::locks(¶chain_sovereign_account_id(1)), 140 | vec![BalanceLock { id: *b"py/xcmlk", amount: 10 * CENTS, reasons: Reasons::All }] 141 | ); 142 | }); 143 | ``` 144 | 145 | 2. Parachain B and C receive the `NoteUnlockable` instruction. 146 | ```rust, noplayground 147 | ParaB::execute_with(|| { 148 | assert_eq!( 149 | parachain::MsgQueue::received_dmp(), 150 | vec![Xcm(vec![NoteUnlockable { 151 | owner: (Parent, Parachain(1)).into(), 152 | asset: (Parent, 10 * CENTS).into() 153 | }])] 154 | ); 155 | }); 156 | 157 | ParaC::execute_with(|| { 158 | assert_eq!( 159 | parachain::MsgQueue::received_dmp(), 160 | vec![Xcm(vec![NoteUnlockable { 161 | owner: (Parent, Parachain(1)).into(), 162 | asset: (Parent, 5 * CENTS).into() 163 | }])] 164 | ); 165 | }); 166 | ``` 167 | 168 | 3. Parachain A sends a `RequestUnlock` instruction to Parachain B for 8 CENTS. 169 | ```rust, noplayground 170 | ParaA::execute_with(|| { 171 | let message = Xcm(vec![RequestUnlock { 172 | asset: (Parent, 8 * CENTS).into(), 173 | locker: Parent.into(), 174 | }]); 175 | 176 | assert_ok!(ParachainPalletXcm::send_xcm(Here, (Parent, Parachain(2)), message.clone())); 177 | }); 178 | ``` 179 | 180 | 4. Parachain B Unlocks a part of the funds by sending an `UnlockAsset` to the relay chain. we check the lock in the balances-pallet. 181 | Unlockers: B, C; Funds registered in pallet-xcm: 2, 5. 182 | Lock set in pallet-balances: 5. 183 | 184 | ```rust,noplayground 185 | Relay::execute_with(|| { 186 | assert_eq!( 187 | relay_chain::Balances::locks(¶chain_sovereign_account_id(1)), 188 | vec![BalanceLock { id: *b"py/xcmlk", amount: 5 * CENTS, reasons: Reasons::All }] 189 | ); 190 | }); 191 | ``` -------------------------------------------------------------------------------- /src/journey/origins.md: -------------------------------------------------------------------------------- 1 | # Origin manipulation 2 | 3 | An XCVM contains contextual information while executing XCM instructions. 4 | It uses the `XcmContext` struct to provide them. 5 | `XcmContext` contains information such as the origin of the corresponding XCM, the hash of the message, and the topic of the XCM. 6 | 7 | ```rust 8 | pub struct XcmContext { 9 | /// The `MultiLocation` origin of the corresponding XCM. 10 | pub origin: Option, 11 | /// The hash of the XCM. 12 | pub message_hash: XcmHash, 13 | /// The topic of the XCM. 14 | pub topic: Option<[u8; 32]>, 15 | } 16 | ``` 17 | 18 | In the XCVM, the origin field of the XcmContext indicates which `MultiLocation`'s privilege level that the current programme is using to execute. 19 | The origin is important for enforcing restrictions and ensuring appropriate execution of the instructions. 20 | 21 | There are multiple instructions in XCM that can alter the XcmContext origin field: 22 | 23 | - `ClearOrigin` 24 | - `DescendOrigin` 25 | - `UniversalOrigin` 26 | - `AliasOrigin` 27 | 28 | ## ClearOrigin 29 | ```rust,noplayground 30 | ClearOrigin 31 | ``` 32 | 33 | The `ClearOrigin` instruction clears the origin register in the XCVM. 34 | Specifically, it sets the origin field of the XCM context to None. 35 | This ensures that subsequent instructions in the XCM cannot use the privilege level of the cleared origin to execute operations. 36 | 37 | 38 | ## DescendOrigin 39 | ```rust,noplayground 40 | DescendOrigin(InteriorMultiLocation), 41 | ``` 42 | The `DescendOrigin` instruction is used to change the XcmContext origin to an interior location of the current origin. 43 | 44 | This can be useful when executing instructions that require a specific location within the current origin. 45 | 46 | Note that the XcmContext origin is a `MultiLocation` containing an `InteriorMultiLocation` enum; it can only hold up to a maximum of 8 `Junction`s, so when we try to execute multiple `DescendOrigin` instructions which would result in an `InteriorMultiLocation` containing more than 8 `Junction`s, a `LocationFull` error is thrown. 47 | 48 | ## UniversalOrigin 49 | ```rust,noplayground 50 | UniversalOrigin(Junction) 51 | ``` 52 | 53 | The UniversalOrigin XCM instruction sets the Origin Register to be a child of the Universal Location. The Junction parameter should generally be a `GlobalConsensus` variant since only these are children of the Universal Location. 54 | 55 | Safety Note: Should only be usable if the Origin is trusted to represent a child of the Universal location. In general, no Origin should be able to represent the Universal Location's child which is the root of the local consensus system since it would by extension allow it to act as any location within the local consensus, but it is necessary when bridging XCMs between `GlobalConsensus` systems. 56 | 57 | ## AliasOrigin 58 | ```rust,noplayground 59 | AliasOrigin(MultiLocation) 60 | ``` 61 | The AliasOrigin instruction is similar to the UniversalOrigin instruction, but it is primarily used for account IDs. 62 | When executed, it switches out the current origin for the given MultiLocation. 63 | THe AliasOrigin instruction would allow to remove certain prefix patterns such as Parent/Parachain(X)/ for certain values of X (thereby allowing sibling chains to use the same account IDs) or Parachain(X)/ (allowing a Relay-chain to use the account IDs native to its child parachains) or just Parent/ (allowing parachains to use AccountIds of the Relay-chain). 64 | -------------------------------------------------------------------------------- /src/journey/queries.md: -------------------------------------------------------------------------------- 1 | # Queries 2 | XCM contains query instructions that can be used to query information from another consensus system: 3 | - `ReportHolding` 4 | - `QueryPallet` 5 | - `ReportError` 6 | - `ReportTransactStatus` 7 | 8 | 9 | Each of these instructions is sent to the destination where we would like the information to be reported back to us. 10 | Each instruction has a `QueryResponseInfo` struct as one of its inputs. 11 | 12 | ```rust, noplayground 13 | pub struct QueryResponseInfo { 14 | pub destination: MultiLocation, 15 | #[codec(compact)] 16 | pub query_id: QueryId, 17 | pub max_weight: Weight, 18 | } 19 | ``` 20 | 21 | The `destination` tells the queried consensus system where to send the response to and the `query_id` field links the query and the query response together. The `max_weight` field tells the queried consensus system what the maximum weight is that the response instruction can take. 22 | 23 | When a query instruction is executed correctly, it sends a `QueryResponse` instruction to the location defined in the previously described `destination` field. 24 | The `QueryResponse` looks like this: 25 | 26 | ```rust,noplayground 27 | QueryResponse { 28 | #[codec(compact)] 29 | query_id: QueryId, 30 | response: Response, 31 | max_weight: Weight, 32 | querier: Option, 33 | } 34 | 35 | // Reponse Struct 36 | pub enum Response { 37 | /// No response. Serves as a neutral default. 38 | Null, 39 | /// Some assets. 40 | Assets(MultiAssets), 41 | /// The outcome of an XCM instruction. 42 | ExecutionResult(Option<(u32, Error)>), 43 | /// An XCM version. 44 | Version(super::Version), 45 | /// The index, instance name, pallet name and version of some pallets. 46 | PalletsInfo(BoundedVec), 47 | /// The status of a dispatch attempt using `Transact`. 48 | DispatchResult(MaybeErrorCode), 49 | } 50 | ``` 51 | 52 | The `QueryResponse` has the same `query_id` as the request to link the request and response and takes over the `max_weight` from the `QueryResponseInfo`. 53 | It has the requested information in the `response` field. 54 | And it has the location of the querier relative to the queried location in the querier field. 55 | The response can be sent back to the requester, or to another location, so the querier field is important to determine where the requested information is needed. 56 | 57 | Now we take a look at the query instructions. 58 | 59 | ## ReportHolding 60 | 61 | ```rust, noplayground 62 | ReportHolding { response_info: QueryResponseInfo, assets: MultiAssetFilter } 63 | ``` 64 | 65 | The `ReportHolding` instruction reports to the given destination the contents of the Holding Register. The `assets` field is a filter for the assets that should be reported back. The assets reported back will be, asset-wise, *the lesser of this value and the holding register*. For example, if the holding register contains 10 units of some fungible asset and the `assets` field specifies 15 units of the same asset, the result will return 10 units of that asset. Wild cards can be used to describe which assets in the holding register to report, but the response always contains assets and no wild cards. 66 | 67 | ### Example 68 | 69 | For the full example, check [here](https://github.com/paritytech/xcm-docs/tree/main/examples). Assets are withdrawn from the account of parachain 1 on the relay chain and partly deposited in the account of parachain 2. The remaining assets are reported back to parachain 1. 70 | 71 | ```rust, noplayground 72 | Xcm(vec![ 73 | WithdrawAsset((Here, AMOUNT).into()), 74 | BuyExecution { fees: (Here, AMOUNT).into(), weight_limit: Unlimited }, 75 | DepositAsset { assets: Definite((Here, AMOUNT - 5).into()), beneficiary: Parachain(2).into() }, 76 | ReportHolding { 77 | response_info: QueryResponseInfo { 78 | destination: Parachain(1).into(), 79 | query_id: QUERY_ID, 80 | max_weight: Weight::from_all(0), 81 | }, 82 | assets: All.into(), 83 | }, 84 | ]); 85 | ``` 86 | 87 | ## QueryPallet 88 | The `QueryPallet` instruction queries the existence of a particular pallet based on the module name specified in the `module_name` field. 89 | 90 | ```rust, noplayground 91 | QueryPallet { module_name: Vec, response_info: QueryResponseInfo } 92 | ``` 93 | 94 | The destination responds with a vec of `PalletInfo`s if the pallet exists. 95 | 96 | ```rust,noplayground 97 | pub struct PalletInfo { 98 | #[codec(compact)] 99 | index: u32, 100 | name: BoundedVec, 101 | module_name: BoundedVec, 102 | #[codec(compact)] 103 | major: u32, 104 | #[codec(compact)] 105 | minor: u32, 106 | #[codec(compact)] 107 | patch: u32, 108 | } 109 | ``` 110 | 111 | ### Example 112 | For the full example, check [here](https://github.com/paritytech/xcm-docs/tree/main/examples). It queries for all instances of pallet_balances and sends the result back to parachain 1. 113 | 114 | ```rust, noplayground 115 | Xcm(vec![ 116 | QueryPallet { 117 | module_name: "pallet_balances".into(), 118 | response_info: QueryResponseInfo { 119 | destination: Parachain(1).into(), 120 | query_id: QUERY_ID, 121 | max_weight: Weight::from_all(0), 122 | }, 123 | } 124 | ]); 125 | ``` 126 | 127 | 128 | ## ReportError 129 | The `ReportError` instruction report the contents of the Error Register to the given destination. This instruction is useful in combination with the `SetErrorHandler` instruction. It then only reports an error if an error is thrown. 130 | 131 | ```rust,noplayground 132 | ReportError(QueryResponseInfo) 133 | ``` 134 | 135 | ### Example 136 | For the full example, check [here](https://github.com/paritytech/xcm-docs/tree/main/examples). The message sets the error handler to report back any error that is thrown during execution of the instructions using the `ReportError` instruction. 137 | ```rust, noplayground 138 | Xcm(vec![ 139 | // Set the Error Handler to report back status of Error register. 140 | SetErrorHandler(Xcm(vec![ 141 | ReportError(QueryResponseInfo { 142 | destination: Parachain(1).into(), 143 | query_id: QUERY_ID, 144 | max_weight: Weight::from_all(0), 145 | }) 146 | ])), 147 | // If an instruction errors during further processing, the resulting error is reported back to Parachain(1). 148 | // MORE INSTRUCTIONS 149 | ]); 150 | ``` 151 | 152 | ## ReportTransactStatus 153 | The `ReportTransactStatus` instruction report the value of the Transact Status Register to the specified destination. 154 | ```rust,noplayground 155 | ReportTransactStatus(QueryResponseInfo) 156 | ``` 157 | 158 | ### Example 159 | 160 | For the full example, check [here](https://github.com/paritytech/xcm-docs/tree/main/examples). 161 | Dispatches a call on the consensus system receiving this Xcm and reports back the status of the Transact Status Register. 162 | 163 | ```rust,noplayground 164 | Xcm(vec![ 165 | Transact { 166 | origin_kind: OriginKind::SovereignAccount, 167 | require_weight_at_most: Weight::from_parts(INITIAL_BALANCE as u64, 1024 * 1024), 168 | call: remark.encode().into(), 169 | }, 170 | ReportTransactStatus(QueryResponseInfo { 171 | destination: Parachain(1).into(), 172 | query_id: QUERY_ID, 173 | max_weight: Weight::from_all(0), 174 | }), 175 | ]); 176 | ``` -------------------------------------------------------------------------------- /src/journey/register-modifiers.md: -------------------------------------------------------------------------------- 1 | # Register Modifiers 2 | In the previous chapters we already saw instructions that modified the XCVM registers. This chapter contains more instructions that change the XCVM registers. We will discuss the following instructions: 3 | - `SetErrorHandler` 4 | - `SetAppendixHandler` 5 | - `ClearError` 6 | - `ClearTransactStatus` 7 | - `SetTopic` 8 | - `ClearTopic` 9 | 10 | ## SetErrorHandler 11 | ```rust 12 | SetErrorHandler(Xcm) 13 | ``` 14 | The `SetErrorHandler` instructions is used to set the Error Handler Register. As discussed in the [XCVM chapter](../fundamentals/xcvm.md), the Error Handler is executed when an error is thrown during the regular instruction execution. 15 | 16 | ## SetAppendix 17 | ```rust 18 | SetAppendix(Xcm) 19 | ``` 20 | The `SetAppendix` instruction is used to set the Appendix Register. As discussed in the [XCVM chapter](../fundamentals/xcvm.md), the Appendix instructions are executed after the regular and error handler instruction are executed. These instructions are executed regardless of whether an error occurred. 21 | 22 | ## ClearError 23 | ```rust 24 | ClearError 25 | ``` 26 | The `ClearError` instruction clears the Error Register by setting it to None. 27 | 28 | ## ClearTransactStatus 29 | ```rust 30 | ClearTransactStatus 31 | ``` 32 | The `ClearTransactStatus` instruction sets the Transact Status Register to its default, cleared, value. 33 | 34 | ## SetTopic 35 | ```rust 36 | SetTopic([u8; 32]) 37 | ``` 38 | The `SetTopic` instruction sets the Topic Register. 39 | 40 | ## ClearTopic 41 | ```rust 42 | ClearTopic 43 | ``` 44 | The `ClearTopic` instruction clears the Topic Register. 45 | 46 | -------------------------------------------------------------------------------- /src/journey/transact.md: -------------------------------------------------------------------------------- 1 | # Transact 2 | 3 | XCM contains an instruction that allows for the execution of calls (from a `RuntimeCall` in a FRAME-based system, to a smart contract function call in an EVM-based system) in a consensus system. 4 | It is the `Transact` instruction and it looks like this: 5 | 6 | ```rust,noplayground 7 | Transact { 8 | origin_kind: OriginKind, 9 | require_weight_at_most: Weight, 10 | call: DoubleEncoded 11 | } 12 | ``` 13 | 14 | The Transact instruction has three fields. 15 | The `origin_kind` is of type [OriginKind](https://paritytech.github.io/polkadot/doc/xcm/v2/enum.OriginKind.html) and specifies how the origin of the call should be interpreted. 16 | In the xcm-executor, the `origin_kind` is used to determine how to convert a `MultiLocation` origin into a `RuntimeOrigin`. 17 | For more information, check out the [xcm-executor config docs](../executor_config/index.html). 18 | 19 | The `require_weight_at_most` field tells the XCVM executing the call how much [weight](../fundamentals/weight_and_fees.md) it can use. 20 | If the call uses more weight than the specified `require_weight_at_most`, the execution of the call fails. 21 | 22 | The `call` field is of type `DoubleEncoded`. 23 | 24 | ```rust,noplayground 25 | pub struct DoubleEncoded { 26 | encoded: Vec, 27 | #[codec(skip)] 28 | decoded: Option, 29 | } 30 | ``` 31 | 32 | XCM is consensus system agnostic; it does not know what is being encoded in the call field. 33 | Hence, the field is a byte vector that can be freely interpreted in whatever form possible. 34 | However, the XCVM does not inherently know how to interpret this call field nor how to decode it; it is reliant on the `T` type parameter to specify the proper codec for the byte vector. 35 | Instead of just using a `Vec` we use `DoubleEncoded` as a wrapper around a pre-encoded call (`Vec`) with extra functionalities such as caching of the decoded value. 36 | We like to emphasize that the call in the `Transact` instruction can be anything from a `RuntimeCall` in a FRAME-based system, to a smart contract function call in an EVM-based system. 37 | 38 | Each XCVM has a Transact Status Register, to record the execution result of the call that is dispatched by the `Transact` instruction. 39 | *Important note:* The execution of the XCM instruction does *not* error when the dispatched call errors. 40 | 41 | The status is described by the `MaybeErrorCode` enum, and can either be a Success, Error or TruncatedError if the length of the error exceeds the MaxDispatchErrorLen. 42 | For pallet-based calls, the Error is represented as the scale encoded `Error` enum of the called pallet. 43 | ```rust,noplayground 44 | ExpectTransactStatus(MaybeErrorCode) 45 | 46 | pub enum MaybeErrorCode { 47 | Success, 48 | Error(BoundedVec), 49 | TruncatedError(BoundedVec), 50 | } 51 | ``` 52 | 53 | ## XCM Executor 54 | In this section, we quickly look at how the XCM executor executes the `Transact` instruction. 55 | 56 | It executes, among other things, the following steps: 57 | 1. Decode the call field into the actual call that we want to dispatch. 58 | 2. Check with the [SafeCallFilter](../executor_config/index.html#safecallfilter) on whether the execution of this call is allowed. 59 | 3. Use the [OriginConverter](../executor_config/index.html#originconverter) to convert the `MultiLocation` origin into a `RuntimeOrigin`. 60 | 4. Check whether the call weight does not exceed `require_weight_at_most`. 61 | 5. Dispatch the call with the converted origin and set the `transact_status` register to be the result of the dispatch. 62 | 6. Calculate the weight that was actually used during the dispatch. 63 | 64 | 65 | ## Example 1 66 | For the full example, check [the repo](https://github.com/paritytech/xcm-docs/tree/main/examples). 67 | 68 | In this example, the relay chain executes the `set_balance` function of `pallet_balances` on `Parachain(1)`. 69 | This function requires the origin to be root. We enable the root origin for the relay chain by setting `ParentAsSuperuser` for the `OriginConverter` config type. 70 | ```rust,noplayground 71 | let call = parachain::RuntimeCall::Balances( 72 | pallet_balances::Call::::set_balance { 73 | who: ALICE, 74 | new_free: 5 * AMOUNT, 75 | new_reserved: 0, 76 | }, 77 | ); 78 | 79 | let message = Xcm(vec![ 80 | WithdrawAsset((Here, AMOUNT).into()), 81 | BuyExecution { fees: (Here, AMOUNT).into(), weight_limit: WeightLimit::Unlimited }, 82 | Transact { 83 | origin_kind: OriginKind::Superuser, 84 | require_weight_at_most: Weight::from_parts(INITIAL_BALANCE as u64, 1024 * 1024), 85 | call: call.encode().into(), 86 | }, 87 | ]); 88 | ``` 89 | 90 | ## Example 2 91 | For the full example, check [the repo](https://github.com/paritytech/xcm-docs/tree/main/examples). 92 | 93 | In this example, as Parachain(1), we create an NFT collection on the relay chain and we then mint an NFT with ID 1. 94 | The admin for the nft collection is parachain(1). The call looks as follows: 95 | ```rust,noplayground 96 | let create_collection = relay_chain::RuntimeCall::Uniques( 97 | pallet_uniques::Call::::create { 98 | collection: 1u32, 99 | admin: parachain_sovereign_account_id(1), 100 | } 101 | ); 102 | ``` 103 | 104 | The owner of the NFT is Alice. The nft mint call looks as follows: 105 | ```rust,noplayground 106 | let mint = relay_chain::RuntimeCall::Uniques( 107 | pallet_uniques::Call::::mint { 108 | collection: 1u32, 109 | item: 1u32, 110 | owner: ALICE, 111 | } 112 | ); 113 | ``` 114 | 115 | The xcm message contains the following instructions: 116 | 1. Withdraw native assets from the `Parachain(1)`'s sovereign account. 117 | 2. Buy weight with these assets. 118 | 3. Create a collection with as admin and owner the sovereign account of `Parachain(1)`. 119 | 4. Mints an NFT in the collection with item ID 1 and as owner Alice. 120 | ```rust,noplayground 121 | let message = Xcm(vec![ 122 | WithdrawAsset((Here, AMOUNT).into()), 123 | BuyExecution { fees: (Here, AMOUNT).into(), weight_limit: WeightLimit::Unlimited }, 124 | Transact { 125 | origin_kind: OriginKind::SovereignAccount, 126 | require_weight_at_most: Weight::from_parts(INITIAL_BALANCE as u64, 1024 * 1024), 127 | call: create_collection.encode().into(), 128 | }, 129 | Transact { 130 | origin_kind: OriginKind::SovereignAccount, 131 | require_weight_at_most: Weight::from_parts(INITIAL_BALANCE as u64, 1024 * 1024), 132 | call: mint.encode().into(), 133 | }, 134 | ]); 135 | ``` 136 | 137 | ## Next: 138 | Check out the following instructions that interact with the Transact Status Register: 139 | - [ClearTransactStatus](register-modifiers.md#cleartransactstatus) 140 | - [ReportTransactStatus](queries.md#reporttransactstatus) 141 | - [ExpectTransactStatus](expects.md#expecttransactstatus) -------------------------------------------------------------------------------- /src/journey/transfers/README.md: -------------------------------------------------------------------------------- 1 | # Transfers 2 | 3 | The first feature you'll be interested in when dealing with XCM is being able to transfer assets between consensus systems. 4 | In the [quickstart](../../quickstart/index.md) chapter, we saw a simple XCM that when executed, would send assets between two accounts on the same consensus system. 5 | Now that we've learnt the [fundamentals](../../fundamentals/index.md), let's go over those same instructions once again. 6 | 7 | ## WithdrawAsset 8 | 9 | ```rust,noplayground 10 | WithdrawAsset(MultiAssets), 11 | ``` 12 | 13 | This instruction is the most common way to get assets to the holding register of the XCVM. 14 | The `MultiAssets` in the operand will be stored in the holding register to be later used for other instructions. 15 | As we've seen, we can use the expression `(Here, amount).into()` to take a certain `amount` of the native token. 16 | 17 | ## BuyExecution 18 | 19 | ```rust,noplayground 20 | BuyExecution { fees: MultiAssets, weight_limit: WeightLimit }, 21 | ``` 22 | 23 | Because XCM is designed to be agnostic to the underlying consensus system, it doesn't have fee payment baked in. 24 | This instruction lets you pay for the execution of the XCM using the assets in the holding register. 25 | Most XCMs are not allowed to be executed (blocked by the [barrier](../../executor_config/index.md#barrier)) if they don't contain this instruction as one of the first ones to pay for all future ones. 26 | 27 | ## DepositAsset 28 | 29 | ```rust,noplayground 30 | DepositAsset { assets: MultiAssetFilter, beneficiary: MultiLocation }, 31 | ``` 32 | 33 | This instruction will put assets from the holding register that match the [MultiAssetFilter](../../fundamentals/multiasset.md#multiassetfilter) into the `beneficiary`. 34 | Note that `beneficiary` must be a location where the local consensus system can actually deposit assets to, e.g. it doesn't make sense to deposit assets to `../AccountId32(0x0)`. 35 | 36 | ## Example 37 | 38 | ```rust,noplayground 39 | let message = Xcm(vec![ 40 | WithdrawAsset((Here, amount).into()), 41 | BuyExecution { fees: (Here, amount).into(), weight_limit: Unlimited }, 42 | DepositAsset { 43 | assets: All.into(), 44 | beneficiary: AccountId32 { id: ALICE.into(), network: None }.into() 45 | }, 46 | ]); 47 | ``` 48 | 49 | As we've seen, the above message results in withdrawing assets from the origin of the message, paying for execution and depositing the rest to another account on the same system. 50 | The full example can be seen in [the repo](https://github.com/paritytech/xcm-docs/tree/main/examples). 51 | 52 | ## Transferring between systems 53 | 54 | But what if you want to make a transfer from one system to another? 55 | There are two ways of doing this: 56 | - Asset teleportation 57 | - Reserve-backed transfers 58 | 59 | We'll be discussing both in the following chapters. 60 | -------------------------------------------------------------------------------- /src/journey/transfers/images/asset_teleportation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paritytech/xcm-docs/c86f214b5bc47e100d5b85cc436c6a6a453c81f8/src/journey/transfers/images/asset_teleportation.png -------------------------------------------------------------------------------- /src/journey/transfers/images/reserve_asset_transfer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paritytech/xcm-docs/c86f214b5bc47e100d5b85cc436c6a6a453c81f8/src/journey/transfers/images/reserve_asset_transfer.png -------------------------------------------------------------------------------- /src/journey/transfers/images/source_is_reserve.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paritytech/xcm-docs/c86f214b5bc47e100d5b85cc436c6a6a453c81f8/src/journey/transfers/images/source_is_reserve.png -------------------------------------------------------------------------------- /src/journey/transfers/reserve.md: -------------------------------------------------------------------------------- 1 | # Reserve-backed transfers 2 | 3 | For consensus systems that don't have the level of trust required for asset teleportation, they can instead opt for trusting a third party called a reserve to store the real assets (think Statemine on Kusama, or Statemint on Polkadot). 4 | The source and the destination need a way to keep track of the real assets they own on the reserve, this is usually done by minting a new derivative token. 5 | Both source and destination now need accounts on the reserve to hold their assets, we call these their sovereign accounts on that system. 6 | 7 | ## Process 8 | 9 | ![Reserve Backed Transfer diagram](images/reserve_asset_transfer.png) 10 | 11 | The flow in this diagram is further explained below: 12 | 13 | ### 1. InitiateReserveWithdraw 14 | 15 | The source gathers the derivative assets to be transferred from the sending account and burns them, taking note of the amount of derivatives that were burned. 16 | 17 | ### 2. WithdrawAsset 18 | 19 | The source sends a `WithdrawAsset` instruction to the reserve, instructing it to withdraw real assets equivalent to the amount of derivatives burned from the source chain. 20 | 21 | ### 3. DepositReserveAsset 22 | 23 | The reserve deposits the assets withdrawn from the previous step to the destination's sovereign account, taking note of the amount of assets deposited. 24 | 25 | ### 4. ReserveAssetDeposited 26 | 27 | The reserve creates a `ReserveAssetDeposited` instruction with the amount of assets deposited to the destination's sovereign account, and sends this instruction onwards to the destination. 28 | The destination receives the instruction and processes it, minting the correct amount of derivative assets. 29 | 30 | ### 5. DepositAsset 31 | 32 | The destination deposits the derivative assets minted to the receiving account. 33 | 34 | ### Thoughts 35 | 36 | The addition of a third consensus system is already a hint of the disadvantages of a reserve asset transfer model. 37 | Firstly, the reserve could easily become a point of centralization when too many consensus systems rely on it to be the reserve of choice for their assets. 38 | Secondly, the sheer amount of steps required necessarily makes it more prone to errors, and as such, implementors will have to consider more possible pitfalls and provide technical support accordingly when an end user encounters issues arising from these steps. 39 | Last, but not least, either the source or destination can opt to designate multiple consensus systems to be their reserves. 40 | In such a situation, care must be taken in order to ensure that the sovereign accounts on the reserves are balanced, so that one doesn't get drained while the others still contain a healthy balance. 41 | 42 | ### A note on trust 43 | 44 | We mentioned that reserve-backed transfers require the sender and the destination to trust a third party, the reserve, and not each other. 45 | This is true, but it doesn't mean the sender and destination have to trust ONLY the reserve, they also have to trust the issuer of the token. 46 | Whenever you are dealing with a particular asset, you are always trusting the issuer of said asset, because at any point they could mint a huge amount of that asset, wreaking havoc. 47 | You have to make sure you trust the asset, based on the security mechanisms used to protect its issuance. 48 | For this reason, reserves work best when they are the issuers of the asset being transacted. 49 | In that scenario, you only have to trust the reserve, period. 50 | 51 | ## Example 52 | 53 | We'll create a program for the scenario in the diagram. 54 | Let's assume that the reserve is a relay chain and both source and destination are parachains 1 and 2 respectively. 55 | Let's also say that an account ALICE in parachain 1 wants to transfer the relay chain's native token to their other account (also ALICE) on parachain 2. 56 | The program might look like this: 57 | 58 | ```rust,noplayground 59 | let message = Xcm(vec![ 60 | WithdrawAsset((Parent, amount).into()), 61 | InitiateReserveWithdraw { 62 | assets: All.into(), 63 | reserve: Parent.into(), 64 | xcm: Xcm(vec![DepositReserveAsset { 65 | assets: All.into(), 66 | dest: Parachain(2).into(), 67 | xcm: Xcm(vec![DepositAsset { 68 | assets: All.into(), 69 | beneficiary: AccountId32 { id: ALICE.into(), network: None }.into(), 70 | }]), 71 | }]), 72 | }, 73 | ]); 74 | ``` 75 | 76 | This program should be executed on the source, so on parachain 1. 77 | We start, as usual, with a `WithdrawAsset` instruction. 78 | The `MultiAsset` here references the relay chain's native token, which means we'll be gathering the derivative on this chain. 79 | 80 | ### InitiateReserveWithdraw 81 | 82 | ```rust,noplayground 83 | InitiateReserveWithdraw { assets: MultiAssetFilter, reserve: MultiLocation, xcm: Xcm<()> } 84 | ``` 85 | 86 | The `InitiateReserveWithdraw` instruction takes the derivative token from the holding register and burns it. 87 | Then it sends a new XCM to the specified `reserve`, in this example, the relay chain. 88 | This new XCM contains the following instructions, in order: 89 | 1. WithdrawAsset 90 | 2. ClearOrigin 91 | 3. All instructions specified in the `xcm` operand, in this case `DepositReserveAsset` 92 | 93 | As was the case with [teleports](teleports.md), instructions 1. and 2. are added automatically by the executor when using `InitiateReserveWithdraw`. 94 | 95 | Upon receiving this XCM, the reserve will withdraw the asset from parachain 1's sovereign account (where the real asset is stored), and deposit it on parachain 2's sovereign account. 96 | 97 | ### DepositReserveAsset 98 | 99 | ```rust,noplayground 100 | DepositReserveAsset { assets: MultiAssetFilter, dest: MultiLocation, xcm: Xcm<()> } 101 | ``` 102 | 103 | This instruction is used in this example instead of `DepositAsset`, because as well as depositing the assets to parachain 2's sovereign account, this instruction will send another XCM to parachain 2. 104 | This new XCM has the following instructions: 105 | 1. ReserveAssetDeposited 106 | 2. ClearOrigin 107 | 3. All instructions specified in the `xcm` operand, in this case, only `DepositAsset` 108 | 109 | ### ReserveAssetDeposited 110 | 111 | ```rust,noplayground 112 | ReserveAssetDeposited(MultiAssets) 113 | ``` 114 | 115 | Parachain 2 receives the XCM, mints new derivative tokens and deposit them locally to the beneficiary account. 116 | `ReserveAssetDeposited` is a *trusted indication*. 117 | As is the case with teleporting, you need to trust the reserve to have actually put the specified amount of assets in the sovereign account of this system. 118 | You can specify which systems you trust as reserves for which assets by configuring the [IsReserve](../../executor_config/index.md) type in the executor. 119 | In our example, both parachains trust the relay chain as a reserve for its own native token. 120 | 121 | ## Another example 122 | 123 | We now know this type of transfers requires 3 actors: the source, the reserve, and the destination. 124 | However, the source and reserve don't have to be different systems, they could be one and the same, as in the following diagram. 125 | 126 | ![Source is reserve](images/source_is_reserve.png) 127 | 128 | In this case the message is the following: 129 | 130 | ```rust,noplayground 131 | let message = Xcm(vec![ 132 | WithdrawAsset((Parent, amount).into()), 133 | DepositReserveAsset { 134 | assets: All.into(), 135 | dest: Parachain(2).into(), 136 | xcm: Xcm(vec![DepositAsset { 137 | assets: All.into(), 138 | beneficiary: AccountId32 { id: ALICE.into(), network: None }.into(), 139 | }]), 140 | }, 141 | ]); 142 | ``` 143 | 144 | This simplifies the reserve-backed transfer. 145 | However, the destination still needs to: 146 | - Recognize the source as the proper reserve for the tokens that are being sent over and 147 | - Support minting derivatives of the tokens being sent over 148 | 149 | It's also possible to skip the `WithdrawAsset` instruction. 150 | The `TransferReserveAsset` instruction handles the withdrawal already. 151 | It can be called like so: 152 | 153 | ```rust,noplayground 154 | let message = Xcm(vec![ 155 | TransferReserveAsset { 156 | assets: (Parent, amount).into(), 157 | dest: Parachain(2).into(), 158 | xcm: Xcm(vec![DepositAsset { 159 | assets: All.into(), 160 | beneficiary: AccountId32 { id: ALICE.into(), network: None }.into(), 161 | }]), 162 | }, 163 | ]); 164 | ``` 165 | 166 | ### Another note on trust 167 | 168 | In this model, where the sender is the reserve, the destination is trusting the sender entirely. 169 | It's the sender the one who doesn't need to trust the destination, since it'll ever only be minting derivatives anyway, the sender/reserve controls the real assets and issuance. 170 | 171 | ## Next steps 172 | 173 | Next, we'll talk about a very important topic we mentioned before but skipped in this chapter, [paying fees](../fees/index.html) for the effects our XCMs have. 174 | -------------------------------------------------------------------------------- /src/journey/transfers/teleports.md: -------------------------------------------------------------------------------- 1 | # Asset teleportation 2 | 3 | Asset teleportation is the simpler method of the two for sending assets from one chain to another. 4 | It has only two actors, the source and the destination. 5 | 6 | ## Process 7 | 8 | ![Asset Teleportation diagram](images/asset_teleportation.png) 9 | 10 | The way in which we transfer assets between the source and the destination are briefly summarized in the numbered labels on the diagram, and are explained in more detail below: 11 | 12 | ### 1. InitiateTeleport 13 | 14 | The source gathers the assets to be teleported from the sending account and *takes them out of the circulating supply*, taking note of the total amount of assets that were taken out. 15 | 16 | ### 2. ReceiveTeleportedAssets 17 | 18 | The source then creates an XCM instruction called `ReceiveTeleportedAssets` and puts the amount of assets taken out of circulation and the receiving account as parameters to this instruction. 19 | It then sends this instruction over to the destination, where it gets processed and new assets are *put back into the circulating supply* accordingly. 20 | 21 | ### 3. DepositAsset 22 | 23 | The destination then deposits the assets to the receiving account of the asset. 24 | 25 | ### Thoughts 26 | 27 | The phrases "taken out of circulating supply" and "put back into circulating supply" are highlighted above to give an indication of how much flexibility an XCM executor has in implementing the semantics of taking an asset out of and putting it back into its circulating supply. 28 | The straightforward answer is to burn the assets to take them out of circulation, but there are multiple methods of achieving the same goal, such as transferring the assets locally to an inaccessible account. 29 | Likewise for putting assets back to circulation, the receiving consensus system can freely choose to implement such semantics by releasing assets from a pre-filled and inaccessible treasury of the assets transferred, or perform a mint of the assets. 30 | 31 | The above also gives a hint on the disadvantages of this model, it requires both the source and destination to have a high level of mutual trust. 32 | The destination must trust that the source has appropriately removed the assets that were sent over from the circulating supply, and the source must also trust the destination to put the assets back into circulation. 33 | An asset teleportation should result in the same circulating supply of the asset. 34 | Failing to uphold either of these two conditions will result in a change in the asset's total issuance (in the case of fungible tokens) or a complete loss/duplication of an NFT. 35 | 36 | ## Example 37 | 38 | The following is an example XCM program that achieves the process described above. 39 | 40 | ```rust,noplayground 41 | let message = Xcm(vec![ 42 | WithdrawAsset((Here, teleport_amount).into()), 43 | InitiateTeleport { 44 | assets: All.into(), 45 | dest: Parachain(1).into(), 46 | xcm: Xcm(vec![DepositAsset { 47 | assets: All.into(), 48 | beneficiary: Junction::AccountId32 { 49 | network: None, 50 | id: ALICE.into(), 51 | } 52 | }]), 53 | }, 54 | ]); 55 | ``` 56 | 57 | Let's discuss how the new instructions work. 58 | 59 | ### InitiateTeleport 60 | 61 | ```rust,noplayground 62 | InitiateTeleport { assets: MultiAssetFilter, dest: MultiLocation, xcm: Xcm<()> } 63 | ``` 64 | 65 | This instruction is intended to be executed from the source system. 66 | It takes the assets to be teleported (that match the `MultiAssetFilter`) from the holding register, which needs to have been populated, usually with a `WithdrawAsset` instruction. 67 | It then sends an XCM to the destination system given by `dest` with the following instructions: 68 | 1. ReceiveTeleportedAsset 69 | 2. ClearOrigin 70 | 3. All the instructions from the `xcm` operand, in this case `DepositAsset` 71 | 72 | As we see in the example, instructions 1. and 2. are always added by the executor, no need to specify them. 73 | 74 | ### ReceiveTeleportedAsset 75 | 76 | ```rust,noplayground 77 | ReceiveTeleportedAssets(MultiAssets) 78 | ``` 79 | 80 | This instruction is a *trusted indication*. It should only be executed if the origin of the XCM is trusted for this purpose. 81 | This level of care must be taken because this instruction will *put assets into the circulating supply*, usually minting them. 82 | As specified earlier, this can result in an increase/decrease in circulating supply of an asset, or a duplication/loss of an NFT, if the source is not trusted for this purpose. 83 | 84 | You can set which origins are allowed to act as teleporters by configuring the [IsTeleporter](../../executor_config/index.md#isteleporter) type in the XCM executor. 85 | If the origin is not allowed to teleport assets to this system, an `UntrustedTeleportLocation` error is returned. 86 | 87 | This instruction will populate the holding register with the teleported assets, which can be used by further instructions. 88 | In our example, the `DepositAsset` instruction will deposit these assets to the receiving account. 89 | 90 | ### ClearOrigin 91 | 92 | ```rust,noplayground 93 | ClearOrigin 94 | ``` 95 | 96 | This instruction clears the origin register of the XCVM. 97 | It's mainly used to not allow further instructions to act on behalf of the previous origin. 98 | The `InitiateTeleport` instruction sends a XCM to the destination system with freshly minted assets and immediately clears the origin. 99 | 100 | ## Another example 101 | 102 | Let's say we want to teleport an NFT (Non-Fungible Token) this time, instead of a fungible token, to another system. 103 | We could do so with the following program: 104 | 105 | ```rust,noplayground 106 | let message = Xcm(vec![ 107 | WithdrawAsset((GeneralIndex(1), 42u32).into()), 108 | InitiateTeleport { 109 | assets: All.into(), 110 | dest: Parachain(1).into(), 111 | xcm: Xcm(vec![DepositAsset { 112 | assets: All.into(), 113 | beneficiary: Junction::AccountId32 { 114 | id: ALICE.into(), 115 | network: None, 116 | }.into() 117 | }]), 118 | }, 119 | ]); 120 | ``` 121 | 122 | Very little changes, in fact, only the `MultiAsset` we're referencing changes, like we would expect. 123 | All the teleportation logic stays the same. 124 | The example assumes an NFT with index 42 inside a collection with index 1. 125 | 126 | ## Next steps 127 | 128 | We'll look at reserve-backed transfers [next](reserve.md). 129 | -------------------------------------------------------------------------------- /src/journey/trap-and-claim.md: -------------------------------------------------------------------------------- 1 | # Trapping and Claiming assets. 2 | When we reach the end of the execution of the XCM there can still be assets in the Holding Register. We can do nothing with them (essentially burning the assets) or we can trap the assets. When we trap the assets, we keep track of the assets together with the origin of the XCM. The origin can claim the assets back in one of the next XCMs. We have two instructions related to trapping and claiming assets: 3 | 4 | - `Trap` 5 | - `ClaimAsset` 6 | 7 | ## Trap 8 | ```rust,noplayground 9 | Trap(#[codec(compact)] u64) 10 | ``` 11 | The `Trap` instruction throws an error of type `Trap`. Both the Trap instruction and Trap error take an `u64` that can be used to represent some value. The Trap instruction is useful for throwing custom errors. An important thing to note is that the Trap instruction does not directly trap assets. It can however forcefully halt the further execution of instructions and if there are still assets in the Holding Register, these assets can be trapped. 12 | 13 | ## ClaimAsset 14 | ```rust,noplayground 15 | ClaimAsset { assets: MultiAssets, ticket: MultiLocation } 16 | ``` 17 | 18 | Once assets are trapped, the `ClaimAsset` instruction can be used to claim the assets. The `ClaimAsset` instruction has two fields. 19 | 20 | The `assets` field tells which trapped assets should be claimed. 21 | This must match exactly with the assets claimable by the origin. 22 | 23 | The `ticket` field is an identifier that helps locating the asset. It is, for example, useful for distinguishing between Asset Versions. Lets say we have an XCM V2 trapped asset and send an XCM V3 `ClaimAsset` instruction, then the `ticket` field can be used to tell between the versions. In the xcm-pallet, `Here` is used to describe the same version as the `ClaimAsset` instruction, while the `GeneralIndex` Junction is used to describe other XCM versions. 24 | 25 | ## Example 26 | The full example can be found [here](https://github.com/paritytech/xcm-docs/tree/main/examples). 27 | 28 | The scenario of the example is this: 29 | Parachain A withdraws funds from its sovereign account on the relay chain. 30 | The assets are trapped because an error is thrown and the execution is halted. 31 | Parachain A claims the trapped assets and receives a report of the holding register. 32 | 33 | Parachain A sends the following message to the relay chain. 34 | The message errors because of the `Trap` instruction, so all assets in the Holding Register are trapped. 35 | ```rust, noplayground 36 | let message = Xcm(vec![ 37 | WithdrawAsset((Here, 10 * CENTS).into()), 38 | BuyExecution { fees: (Here, CENTS).into(), weight_limit: WeightLimit::Unlimited }, 39 | Trap(0), // <-- Errors 40 | DepositAsset { // <-- Not executed because of error. 41 | assets: All.into(), 42 | beneficiary: AccountId32 { 43 | network: Some(parachain::RelayNetwork::get()), 44 | id: ALICE.into() 45 | }.into() 46 | } 47 | ]); 48 | ``` 49 | 50 | Parachain A claims the assets, reports them to itself and deposits them in the Account of Alice. 51 | ```rust, noplayground 52 | let claim_message = Xcm(vec![ 53 | ClaimAsset { assets: (Here, 10 * CENTS).into(), ticket: Here.into() }, 54 | ReportHolding { 55 | response_info: QueryResponseInfo { 56 | destination: Parachain(1).into(), 57 | query_id: QUERY_ID, 58 | max_weight: Weight::from_parts(1_000_000_000, 64*64) }, 59 | assets: All.into() 60 | }, 61 | DepositAsset { 62 | assets: All.into(), 63 | beneficiary: AccountId32 { 64 | network: Some(parachain::RelayNetwork::get()), 65 | id: ALICE.into() 66 | }.into() 67 | }, 68 | ]); 69 | ``` -------------------------------------------------------------------------------- /src/journey/version.md: -------------------------------------------------------------------------------- 1 | # Version Subscription 2 | XCM is a versioned messaging format. One version may contain more or different instructions than another, so for parties to communicate via XCM, it is important to know which version the other party is using. XCM enables a version subscription model, where parties can subscribe to each other to get notified of version updates. XCM has two instructions to enable this: 3 | - `SubscribeVersion` 4 | - `UnsubscribeVersion` 5 | 6 | The version subscription model can differ per XCVM implementation. 7 | The `xcm-executor` has a `SubscriptionService` [config item](../executor_config/index.md#subscriptionservice). 8 | Any type specified as the `SubscriptionService` must implement the `VersionChangeNotifier` trait. 9 | The XCM pallet is one such implementor. 10 | When the `SubscribeVersion` instruction is sent to a consensus system that uses the XCM pallet as the `SubscriptionService` in the XCM executor, the system will send back its currently `AdvertisedVersion` and will keep the subscribed location up to date when the version changes. 11 | The subscribed location can unsubscribe to version changes by sending the `UnsubscribeVersion` instruction. 12 | 13 | ```rust,noplayground 14 | SubscribeVersion { 15 | #[codec(compact)] 16 | query_id: QueryId, 17 | max_response_weight: Weight, 18 | } 19 | 20 | UnsubscribeVersion 21 | ``` 22 | 23 | Check out the [example](https://github.com/paritytech/xcm-docs/tree/main/examples). 24 | -------------------------------------------------------------------------------- /src/overview/README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | XCM enables different consensus systems to communicate with each other. 4 | Common cross-consensus use-cases include: 5 | - Sending tokens between blockchains 6 | - Locking assets on one blockchain in order to gain some benefit on a smart contract on another blockchain 7 | - Calling specific functions on another blockchain 8 | 9 | These are just a few basic examples; once you can communicate with other consensus systems, you can create applications that can leverage multiple blockchains' capabilities. 10 | The potential it provides is especially evident in an ecosystem of highly specialized blockchains like Polkadot. 11 | 12 | Decentralized distributed systems are very complex, so it's easy to make errors when building interactions between them. 13 | XCM is meant to be used by developers to package these interactions into their runtime logic before exposing that functionality to end users. 14 | 15 | This chapter will cover what XCM is, what it isn't, and why it matters before exploring the different components that make up the XCM ecosystem. 16 | -------------------------------------------------------------------------------- /src/overview/architecture.md: -------------------------------------------------------------------------------- 1 | # Architecture 2 | 3 | XCM is a [format](https://github.com/paritytech/xcm-format). 4 | Anyone can create an implementation of the XCVM to interpret said format. 5 | 6 | Parity Technologies maintains a Rust implementation, primarily for [Substrate](https://substrate.io/)-based chains in the [Polkadot](https://polkadot.network/) ecosystem. 7 | It is this implementation that we use throughout this documentation. 8 | 9 | All the code lives in the [Polkadot repo](https://github.com/paritytech/polkadot/tree/master/xcm). 10 | The main structure is as follows: 11 | - [XCM](https://github.com/paritytech/polkadot/tree/master/xcm/src): Defines the fundamental constructs used in XCM and an enum with all the instructions available. 12 | - [Executor](https://github.com/paritytech/polkadot/tree/master/xcm/xcm-executor/src): Implements the XCVM, capable of executing XCMs. Highly configurable. 13 | - [Builder](https://github.com/paritytech/polkadot/tree/master/xcm/xcm-builder/src): Offers common configuration building blocks for the executor. 14 | - [Pallet](https://github.com/paritytech/polkadot/tree/master/xcm/pallet-xcm/src): FRAME pallet that provides extrinsics for interacting with the XCM executor, as well as specific XCM programs, such as teleports and reserve asset transfers. 15 | - [Simulator](https://github.com/paritytech/polkadot/tree/master/xcm/xcm-simulator/example/src): Allows for testing of XCM programs. 16 | 17 | ## Executor 18 | 19 | The XCM executor is responsible for interpreting and executing XCM messages. 20 | It is the core engine that processes and handles XCM instructions, ensuring that they are carried out accurately and in the correct order. 21 | The XCM executor follows the Cross-Consensus Virtual Machine (XCVM) specification and can be extended, customized, or even replaced with an alternative construct that adheres to the XCVM spec. 22 | 23 | ## Builder 24 | 25 | The XCM executor is highly configurable. 26 | XCM builder provides building blocks people can use to configure their executor according to their needs. 27 | Many of these building blocks will be explained in the [Config Deep Dive](../executor_config/index.md) chapter. 28 | They cover common use-cases but are not meant to be exhaustive. 29 | It's very easy to build your own building blocks for your specific configuration when needed, using these as examples. 30 | 31 | ## Pallet 32 | 33 | The XCM pallet is a [FRAME](https://docs.substrate.io/quick-start/substrate-at-a-glance/) pallet that can be used to execute XCMs locally or send them to a different system. 34 | It also has extrinsics for specific use cases such as teleporting assets or doing reserve asset transfers, which we'll talk about later. 35 | It's the glue between XCM and FRAME, which is highly used in the Polkadot ecosystem. 36 | 37 | ## Simulator 38 | 39 | The simulator allows for testing XCMs fast, without needing to boot up several different nodes in a network, or test in production. 40 | It's a very useful tool which we'll use throughout this document to build and test different XCMs. 41 | -------------------------------------------------------------------------------- /src/overview/format.md: -------------------------------------------------------------------------------- 1 | # A Format, Not a Protocol 2 | 3 | It's essential to understand that XCM is a format, not a protocol. 4 | It describes how messages should be structured and contains instructions that convey on-chain actions that the message intends to perform. 5 | However, XCM does not dictate how messages are delivered. 6 | That responsibility falls on [transport layer protocols](https://wiki.polkadot.network/docs/learn-xcm-transport) such as XCMP (Cross Chain Message Passing) and VMP (Vertical Message Passing) in the Polkadot ecosystem, or bridging protocols. 7 | 8 | This separation of concerns is useful, since it allows us to think of the interactions we want to build between systems without having to think about how the messages involved are actually routed. 9 | 10 | Not every system is expected to be able to interpret any possible XCM. 11 | Some messages will not have reasonable interpretations under some systems or will be intentionally unsupported. 12 | For example, some consensus systems won't deal with NFTs, and that's okay. 13 | Instructions that relate to NFTs will have valid interpretations on some systems but not on others. 14 | 15 | Furthermore, XCMs by themselves are not considered on-chain transactions: XCM describes how to change the state of the target consensus system, but the message by itself does not perform state changes. 16 | XCM communicates intentions; the actual interpretation and behaviour of each instruction in an XCM is defined by target's XCVM implementation. 17 | 18 | Both simple and more complex scenarios can be expressed, and developers are encouraged to design and implement diverse cross-consensus communication solutions. 19 | -------------------------------------------------------------------------------- /src/overview/interoperability.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | XCM is a **language** for communicating **intentions** between **consensus systems**. 4 | Concretely, XCM is a message format, it specifies how to craft messages that communicate intentions to other consensus systems. 5 | Some examples of consensus systems are blockchains and smart contracts. 6 | XCM comes from the [Polkadot](https://polkadot.network/) ecosystem, but is designed to be general enough to provide a common format for cross-consensus communication that can be used anywhere. 7 | 8 | Its goal is to let blockchain ecosystems thrive via specialization instead of generalization. 9 | If there's no interoperability, a chain is forced to host all services and support all functionalities on its own. 10 | With XCM, we are able to achieve an ecosystem-wide division of labour: a chain can specialize and focus on its own business logic, and leverage the benefits of depending on other specialized blockchain for services that it does not provide. 11 | 12 | XCM makes the following assumptions regarding the underlying environment: 13 | 1. Asynchronous: XCMs in no way assume that the sender will be blocking on its completion. 14 | 2. Absolute: XCMs are assumed to be delivered and interpreted accurately, in order and in a timely fashion. Once a message is sent, one can assume that it will be processed as intended. This guarantee has to be provided by the transport layer. 15 | 3. Asymmetric: XCMs, by default, do not have results that let the sender know that the message was executed correctly. If results are needed, a new message must be sent. 16 | 4. Agnostic: XCM makes no assumptions about the nature of the consensus systems between which the messages are being passed. XCM should be usable in any system that derives finality through consensus. 17 | 18 | XCM is constantly evolving; the format is expected to change over time. 19 | It has an RFC process to propose changes, which end up in newer versions, the current one being v3. 20 | To keep up with the development of the format, or to propose changes, go to [the XCM format repository](https://github.com/paritytech/xcm-format). 21 | -------------------------------------------------------------------------------- /src/overview/xcvm.md: -------------------------------------------------------------------------------- 1 | # The XCVM 2 | 3 | At the core of XCM lies the XCVM (Cross-Consensus Virtual Machine). 4 | A message in XCM (referred to as an XCM, cross-consensus message, or XCMs for more than one) is an XCVM program. 5 | The XCVM is a register-based state machine that executes every program by processing its instructions one at a time. 6 | During execution, state is tracked in domain-specific registers, and is constantly being used and updated. 7 | Most of the XCM format comprises these registers and the instructions used to compose XCVM programs. 8 | 9 | Like XCM, the XCVM is also a specification. 10 | The implementation that will be used in this documentation is the [xcm-executor](https://github.com/paritytech/polkadot/tree/master/xcm/xcm-executor), provided by Parity. 11 | The executor is highly configurable. 12 | For more information on the extensive configuration options available, see the [Config Deep Dive](../executor_config/index.md) chapter. 13 | 14 | Anyone can create an implementation of the XCVM. 15 | As long as they follow the standard, they'll be able to send XCMs to systems using other implementations. 16 | 17 | Typically, an XCM takes the following path through the XCVM: 18 | - Instructions within an XCM are read one-by-one. 19 | - The instruction is executed. This means that the current values of the XCVM registers, the instruction type, and the instruction operands are all used to execute some operation, which might result in some registers changing their value, or in an error being thrown, which would halt execution. 20 | - Each subsequent instruction within the XCM is read until the end of the message has been reached. 21 | 22 | An example of an XCVM register is the holding register. 23 | Any XCVM program that handles assets will be putting them in and taking them from this register. 24 | This register is used by several of the instructions we will look at later, including `DepositAsset` and `WithdrawAsset`. 25 | 26 | For more information on other registers, see the [All XCVM Registers](../reference/xcvm-registers.md) section. 27 | -------------------------------------------------------------------------------- /src/quickstart/README.md: -------------------------------------------------------------------------------- 1 | # Quickstart 2 | 3 | The XCM code can be found in [polkadot repository](https://github.com/paritytech/polkadot/tree/master/xcm). 4 | 5 | ## Rust & Cargo 6 | A pre-requisite for using XCM is to have a stable Rust version and Cargo installed. Here's an [installation guide](https://docs.substrate.io/install/). 7 | 8 | ## Running the Examples 9 | 10 | All examples in the documentation are located in the [repository](https://github.com/paritytech/xcm-docs/tree/main/examples). Follow these steps to run the `first-look` example. 11 | First clone the repository: 12 | 13 | ```shell 14 | git clone git@github.com:paritytech/xcm-docs.git 15 | cd xcm-docs/examples 16 | ``` 17 | 18 | To run the first-look example, run the following line: 19 | 20 | ```shell 21 | cargo test -p xcm-examples para_a_simple_transfer -- --nocapture 22 | ``` 23 | 24 | It should show you the following output: 25 | 26 | ```shell 27 | running 1 test 28 | test first_look::tests::para_a_simple_transfer ... ok 29 | 30 | test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out; finished in 0.01s 31 | ``` 32 | 33 | -------------------------------------------------------------------------------- /src/quickstart/first-look.md: -------------------------------------------------------------------------------- 1 | # First Look 2 | In this section, we take you through a simple example of an XCM. In this example, we withdraw the native token from the account of Alice and deposit this token in the account of Bob. This message simulates a transfer between two accounts in the same consensus system (`ParaA`). You can find the complete code example [in the repo](https://github.com/paritytech/xcm-docs/tree/main/examples). 3 | 4 | ## Message 5 | ```rust,noplayground 6 | let message = Xcm(vec![ 7 | WithdrawAsset((Here, amount).into()), 8 | BuyExecution{ fees: (Here, amount).into(), weight_limit: WeightLimit::Unlimited }, 9 | DepositAsset { 10 | assets: All.into(), 11 | beneficiary: MultiLocation { 12 | parents: 0, 13 | interior: Junction::AccountId32 { 14 | network: None, 15 | id: BOB.clone().into() 16 | }.into(), 17 | }.into() 18 | } 19 | ]); 20 | ``` 21 | 22 | The message consists of three instructions: `WithdrawAsset`, `BuyExecution`, and `DepositAsset`. 23 | In the following sections we will go over each instruction. 24 | 25 | ### WithdrawAsset 26 | ```rust 27 | WithdrawAsset((Here, amount).into()) 28 | ``` 29 | 30 | The first instruction takes as an input the [MultiAsset]() that should be withdrawn. The MultiAsset describes the native parachain token with the `Here` keyword. The `amount` parameter is the number of tokens that are transferred. The withdrawal account depends on the origin of the message. In this example the origin of the message is Alice. 31 | The WithdrawAsset instruction moves `amount` number of native tokens from Alice's account into the _holding register_. 32 | 33 | ### BuyExecution 34 | ```rust 35 | BuyExecution{fees: (Here, amount).into(), weight_limit: WeightLimit::Unlimited} 36 | ``` 37 | To execute XCM instructions, weight (some amount of resources) has to be bought. 38 | The amount of weight needed to execute an XCM depends on the number and type of instructions in the XCM. 39 | The `BuyExecution` instruction pays for the weight using the `fees`. 40 | The `fees` parameter describes the asset in the _holding register_ that should be used for paying for the weight. 41 | The `weight_limit` parameter defines the maximum amount of fees that can be used for buying weight. 42 | There are special occasions where it is not necessary to buy weight. 43 | See the chapter on [weight and fees](../fundamentals/weight_and_fees.md) for more information about the fees in XCM. 44 | 45 | ### DepositAsset 46 | ```rust 47 | DepositAsset { 48 | assets: All.into(), 49 | beneficiary: MultiLocation { 50 | parents: 0, 51 | interior: Junction::AccountId32 { 52 | network: None, 53 | id: BOB.clone().into() 54 | }.into(), 55 | }.into() 56 | } 57 | ``` 58 | The DepositAsset instruction is used to deposit funds from the holding register into the account of the _beneficiary_. 59 | We don’t actually know how much is remaining in the holding register after the `BuyExecution` instruction, but that doesn’t matter since we specify a wildcard for the asset(s) which should be deposited. 60 | In this case, the wildcard is `All`, meaning that all assets in the holding register at that point in the execution should be deposited. 61 | The _beneficiary_ in this case is the account of Bob in the current consensus system. 62 | 63 | When the three instructions are combined, we withdraw `amount` native tokens from the account of Alice, pay for the execution of these instructions, and deposit the remaining tokens in the account of Bob. 64 | 65 | ## What next? 66 | 67 | Now that we have taken a first look at an XCM, we can dive deeper into all the XCM instructions, to be able to build more complex XCVM programs. 68 | For an overview of the instructions check out the [xcm-format repo](https://github.com/paritytech/xcm-format#5-the-xcvm-instruction-set). 69 | We'll show examples for every instruction in the [journey through XCM](../journey/index.md) chapter. 70 | First, it's important to learn the fundamentals, `MultiLocation`, `MultiAsset`, and other concepts in XCM. 71 | We'll talk about those next. 72 | -------------------------------------------------------------------------------- /src/quickstart/xcm-simulator.md: -------------------------------------------------------------------------------- 1 | # XCM Simulator 2 | Setting up a live network with multiple connected parachains for testing XCM is not straight forward. 3 | The `xcm-simulator` was created as a solution to this problem. 4 | It's a network simulator specifically designed for testing and tinkering with XCM. 5 | It uses mock runtimes for a relay chain and parachains. 6 | 7 | Although it's a great tool to learn and test XCMs, it shouldn't be the only thing you use to actually test your XCM-powered solution. 8 | We'll get into tools and best practices for testing in the [testing](../testing/index.md) chapter. 9 | 10 | We'll use the simulator throughout the documentation to show different XCMs in action. 11 | In the next section we will take a first look at an XCM. 12 | -------------------------------------------------------------------------------- /src/reference/glossary.md: -------------------------------------------------------------------------------- 1 | # Glossary 2 | 3 | ## XCM (Cross-Consensus Messaging) 4 | 5 | A messaging format meant to communicate intentions between consensus systems. 6 | XCM could also refer to a single message. 7 | 8 | ## Instructions 9 | 10 | XCMs are composed of a sequence of instructions. 11 | Each instruction aims to convey a particular intention. 12 | There are instructions for transferring and locking assets, handling fees, calling arbitrary blobs, and more. 13 | 14 | ## Consensus system 15 | 16 | A system that can reach any kind of consensus. 17 | For example, relay chains, parachains, smart contracts. 18 | 19 | ## MultiLocation 20 | 21 | A way of addressing consensus systems. 22 | These could be relative or absolute. 23 | 24 | ## Junction 25 | 26 | The different ways of descending down a `MultiLocation` hierarchy. 27 | A junction can be a Parachain, an Account, or more. 28 | 29 | ## MultiAsset 30 | 31 | A way of identifying assets in the same or another consensus system, by using a `MultiLocation`. 32 | 33 | ## Sovereign account 34 | 35 | An account on a consensus system that is controlled by an account in another consensus system. 36 | 37 | ## Teleport 38 | 39 | A way of transferring assets between two consensus systems without the need of a third party. 40 | It consists of the sender system burning the asset that wants to be sent over and the recipient minting an equivalent amount of that asset. 41 | It requires a lot of trust between the two systems, since failure to mint or burn will reduce the total issuance of the token. 42 | 43 | ## Reserve asset transfer 44 | 45 | A way of transferring assets between two consensus systems that don't trust each other, by using a third system they both trust, called the reserve. 46 | The real asset only exists on the reserve, both sender and recipient only deal with derivatives. 47 | It consists of the sender burning a certain amount of derivatives, telling the reserve to move real assets from its sovereign account to the destination's sovereign account, and then telling the recipient to mint the right amount of derivatives. 48 | 49 | ## XCVM 50 | 51 | The virtual machine behind XCM. 52 | Every XCM is an XCVM programme. 53 | Holds state in registers. 54 | 55 | ## Holding register 56 | 57 | An XCVM register used to hold arbitrary `Asset`s during the execution of an XCVM programme. 58 | 59 | ## Barrier 60 | 61 | An XCM executor configuration item that works as a firewall for incoming XCMs. 62 | All XCMs have to pass the barrier to be executed, else they are dropped. 63 | It can be used for whitelisting only certain types or messages or messages from certain senders. 64 | 65 | ## UMP (Upward Message Passing) 66 | 67 | Transport-layer protocol that allows parachains to send messages upwards to their relay chain. 68 | 69 | ## DMP (Downward Message Passing) 70 | 71 | Transport-layer protocol that allows the relay chain to send messages downwards to one of their parachains. 72 | 73 | ## XCMP (Cross-Consensus Message Passing) 74 | 75 | Transport-layer protocol that allows parachains to send messages between themselves, without going through the relay chain. 76 | 77 | ## HRMP (Horizontal Message Passing) 78 | 79 | Transport-layer protocol that allows a parachain to send messages to a sibling parachain going through the relay chain. 80 | It's a precursor to XCMP, also known as XCMP-lite. 81 | It uses a mixture of UMP and VMP. 82 | -------------------------------------------------------------------------------- /src/reference/xcvm-registers.md: -------------------------------------------------------------------------------- 1 | # XCVM Registers 2 | 3 | Each implementation of an XCVM contains several registers which cannot generally be set at will, but rather begin with specific values and may only be mutated under certain circumstances and/or obeying certain rules. An XCVM has the following registers: 4 | 5 | - [Programme](https://github.com/paritytech/xcm-format#31-programme) 6 | - [Programme Counter](https://github.com/paritytech/xcm-format#32-programme-counter) 7 | - [Error](https://github.com/paritytech/xcm-format#33-error) 8 | - [Error Handler](https://github.com/paritytech/xcm-format#34-error-handler) 9 | - [Appendix](https://github.com/paritytech/xcm-format#35-appendix) 10 | - [Origin](https://github.com/paritytech/xcm-format#36-origin) 11 | - [Holding](https://github.com/paritytech/xcm-format#37-holding-register) 12 | - [Surplus Weight](https://github.com/paritytech/xcm-format#38-surplus-weight) 13 | - [Refunded Weight](https://github.com/paritytech/xcm-format#39-refunded-weight) 14 | - [Transact Status](https://github.com/paritytech/xcm-format#310-transact-status) 15 | - [Topic](https://github.com/paritytech/xcm-format#311-topic) -------------------------------------------------------------------------------- /src/testing/README.md: -------------------------------------------------------------------------------- 1 | # Testing 2 | 3 | Before deploying your XCM-powered solution to production, it's paramount to test it thoroughly. 4 | There are different levels for testing, which should be tackled sequentially: 5 | - Message: Making sure your message works properly, according to the XCVM spec. 6 | - Configuration: Making sure your executor's configuration is as expected. 7 | - End-to-end: Making sure the whole flow works, in an environment as similar to production as possible. 8 | 9 | We'll discuss tools and best practices for each of these levels. 10 | ## XCM Simulator 11 | The [xcm-simulator](https://github.com/paritytech/polkadot/tree/master/xcm/xcm-simulator) is a tool to quickly test the execution of various XCM instructions against the `xcm-executor`. 12 | The examples in this documentation use the xcm-simulator. 13 | The simulator mocks the Downward Message Passing pallet, enabling us to get the XCMs that a parachain receives from the relay chain using the `received_dmp` getter. 14 | The simulator should be used as a XCM playground. For testing the XCM configuration of your parachain and the integration with other chains, you can use the xcm-emulator. 15 | 16 | ## XCM Emulator 17 | The [xcm-emulator](https://github.com/paritytech/cumulus/tree/master/xcm/xcm-emulator) is a tool to emulate XCM program execution using pre-configured runtimes, including those used to run on live networks, such as Kusama, Polkadot, Statemine, etc. This allows for testing cross-chain message passing and verifying outcomes, weights, and side-effects. 18 | 19 | An example of how the emulator is used for testing common good parachains can be found [here](https://github.com/paritytech/cumulus/tree/master/parachains/integration-tests/emulated). 20 | 21 | The xcm-emulator uses the transport layer pallets. However, the messages do not utilize the same messaging infrastructure as live networks, as the transport mechanism is being mocked out. Also, consensus related events are not tested, like disputes, staking and iamonline events. To test for these events, parachains can use E2E tests. 22 | 23 | ## End-to-End testing 24 | There are two frameworks being used in the ecosystem to do e2e testing: 25 | - [Zombienet](https://github.com/paritytech/zombienet). 26 | - [Chopsticks](https://github.com/AcalaNetwork/chopsticks). -------------------------------------------------------------------------------- /src/xcm.md: -------------------------------------------------------------------------------- 1 | # XCM: Cross-Consensus Messaging 2 | 3 | Welcome to the Cross-Consensus Messaging (XCM) documentation! 4 | XCM is a **language** for communicating **intentions** between **consensus systems**. 5 | Whether you're a developer, a blockchain enthusiast, or just interested in Polkadot, this guide aims to provide you with an easy-to-understand and comprehensive introduction to XCM. 6 | 7 | ## Getting started 8 | 9 | Head over to the [overview](overview/README.md) to begin your journey with XCM. 10 | 11 | ## Configuration 12 | 13 | Head over to the [configuration section](executor_config/README.md) if you want to learn how to configure your project to use XCM. 14 | 15 | ## Glossary 16 | 17 | Go to the [glossary](reference/glossary.md) section for a quick explanation of all the terms used when dealing with XCM. 18 | 19 | ## Contribute 20 | 21 | Both the [format](https://github.com/paritytech/xcm-format) and this [documentation](https://github.com/paritytech/xcm-docs) are open for anyone to contribute. 22 | If there's anything you'd like to see in the documentation, feel free to [open an issue](https://github.com/paritytech/xcm-docs/issues). 23 | If you want to contribute to the format, check out the [RFC process](https://github.com/paritytech/xcm-format/blob/master/proposals/0001-process.md). 24 | --------------------------------------------------------------------------------