#![allow(unused)] fn main() { //! # Fundamentals lesson 6: Pallet XCM //! //! Implement the core functionality of Pallet XCM use crate::xcm_executor::ExecuteXcm; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use xcm::{prelude::*, VersionedAssets, VersionedLocation, VersionedXcm}; pub use pallet::*; #[frame_support::pallet] pub mod pallet { use super::*; #[pallet::pallet] pub struct Pallet<T>(_); #[pallet::config] pub trait Config: frame_system::Config { /// Something to execute an XCM message. type XcmExecutor: ExecuteXcm; /// Required origin for executing XCM messages, including the teleport functionality. If /// successful, then it resolves to `Location` which exists as an interior location /// within this chain's XCM context. type ExecuteXcmOrigin: EnsureOrigin< <Self as frame_system::Config>::RuntimeOrigin, Success = Location, >; /// Required origin for sending XCM messages. If successful, it resolves to `Location` /// which exists as an interior location within this chain's XCM context. type SendXcmOrigin: EnsureOrigin< <Self as frame_system::Config>::RuntimeOrigin, Success = Location, >; /// The type used to actually dispatch an XCM to its destination. type XcmRouter: SendXcm; /// This chain's Universal Location. type UniversalLocation: Get<InteriorLocation>; } #[pallet::error] pub enum Error<T> { /// The version of the `Versioned` value used is not able to be interpreted. BadVersion, /// Origin is invalid for sending. InvalidOrigin, /// Could not re-anchor the assets to declare the fees for the destination chain. CannotReanchor, /// A general error indicating something went wrong with the XCM Executor. ExecutorError, /// A general error indicating something went wrong with the XCM Router. RouterError, } #[pallet::call] impl<T: Config> Pallet<T> { /// Execute an XCM from a local, signed, origin. #[pallet::call_index(0)] #[pallet::weight(Weight::default())] pub fn execute( origin: OriginFor<T>, message: Box<VersionedXcm<()>>, _max_weight: Weight, ) -> DispatchResult { let message = (*message).try_into().map_err(|()| Error::<T>::BadVersion)?; // Actually execute the XCM. Self::do_execute(origin, message) } /// Send an XCM to another consensus system. #[pallet::call_index(1)] #[pallet::weight(Weight::default())] pub fn send( origin: OriginFor<T>, dest: Box<VersionedLocation>, message: Box<VersionedXcm<()>>, ) -> DispatchResult { let dest = Location::try_from(*dest).map_err(|()| Error::<T>::BadVersion)?; let message: Xcm<()> = (*message).try_into().map_err(|()| Error::<T>::BadVersion)?; // Actually send the XCM. Self::do_send(origin, dest, message) } /// Teleport some assets from the local chain to some destination chain. #[pallet::call_index(2)] #[pallet::weight(Weight::default())] pub fn teleport_assets( origin: OriginFor<T>, dest: Box<VersionedLocation>, beneficiary: Box<VersionedLocation>, assets: Box<VersionedAssets>, fee_asset_item: u32, ) -> DispatchResult { let dest: Location = (*dest).try_into().map_err(|()| Error::<T>::BadVersion)?; let beneficiary: Location = (*beneficiary).try_into().map_err(|()| Error::<T>::BadVersion)?; let assets: Assets = (*assets).try_into().map_err(|()| Error::<T>::BadVersion)?; // Actually teleport the assets. Self::do_teleport_assets(origin, dest, beneficiary, assets, fee_asset_item) } /// Transfer some assets from the local chain to the destination chain through their local, /// destination or remote reserve. #[pallet::call_index(3)] #[pallet::weight(Weight::default())] pub fn reserve_transfer_assets( origin: OriginFor<T>, dest: Box<VersionedLocation>, beneficiary: Box<VersionedLocation>, assets: Box<VersionedAssets>, fee_asset_item: u32, ) -> DispatchResult { let dest: Location = (*dest).try_into().map_err(|()| Error::<T>::BadVersion)?; let beneficiary: Location = (*beneficiary).try_into().map_err(|()| Error::<T>::BadVersion)?; let assets: Assets = (*assets).try_into().map_err(|()| Error::<T>::BadVersion)?; // Actually reserve transfer the assets. Self::do_reserve_transfer_assets(origin, dest, beneficiary, assets, fee_asset_item) } } } impl<T: Config> Pallet<T> { /// Execute an XCM locally on this chain on behalf of `origin`. pub fn do_execute(origin: OriginFor<T>, message: Xcm<()>) -> DispatchResult { let execute_origin: Location = T::ExecuteXcmOrigin::ensure_origin(origin)?; T::XcmExecutor::execute(execute_origin, message).map_err(|_| Error::<T>::ExecutorError)?; Ok(()) } /// Relay an XCM `message` from a given `interior` location in this context to a given `dest` /// location. pub fn do_send(origin: OriginFor<T>, dest: Location, mut message: Xcm<()>) -> DispatchResult { let origin_location = T::SendXcmOrigin::ensure_origin(origin)?; let interior: Junctions = origin_location.try_into().map_err(|_| Error::<T>::InvalidOrigin)?; if interior != Junctions::Here { message.0.insert(0, DescendOrigin(interior)); } let (ticket, _) = T::XcmRouter::validate(&mut Some(dest), &mut Some(message)) .map_err(|_| Error::<T>::RouterError)?; let _message_id = T::XcmRouter::deliver(ticket).map_err(|_| Error::<T>::RouterError)?; Ok(()) } pub fn do_teleport_assets( origin: OriginFor<T>, dest: Location, beneficiary: Location, assets: Assets, // The index into `assets` of the item which should be used to pay fees. // We don't use this in our naive implementation. _fee_asset_item: u32, ) -> DispatchResult { /* The Teleport Instruction is broken up into a local and destination XCM. */ /* Create a `local_execute_xcm` which does: - `WithdrawAsset` - `BurnAsset` */ /* For the XCM on the destination: - We need to adjust the location of the assets to match the destination - For this, we need to `reanchor` the assets using the `dest` and local `context` - For `context`, you should use the `UniversalLocation`. */ /* Then prepare a `xcm_on_dest` which is composed of: - `ReceiveTeleportedAsset` - using `reanchored_assets` - `ClearOrigin` - `DepositAsset` - using the asset filter `Wild(All)` */ /* Finally, we just need to: - `do_execute` our `local_execute_xcm` - `do_send` our `xcm_on_dest` to `dest` */ todo!("{:?} {:?} {:?}", dest, beneficiary, assets) } pub fn do_reserve_transfer_assets( _origin: OriginFor<T>, _dest: Location, _beneficiary: Location, _assets: Assets, _fee_asset_item: u32, ) -> DispatchResult { // There are 3 different reserve transfer scenarios: // - A local reserve transfer: reserve-transfer `asset` to `dest`, using local chain as // reserve. // - A destination reserve transfer: reserve-transfer `asset` to `dest`, using `dest` as // reserve. // - A remote reserve transfer: reserve-transfer `asset` to `dest`, using remote chain // `Location` as reserve. // // This is a lot to do in this workshop, but a welcome challenge for the reader to // implement. unimplemented!() } } }
#![allow(unused)] fn main() { //! # Fundamentals lesson 6: Pallet XCM //! //! Implement the core functionality of Pallet XCM use crate::xcm_executor::ExecuteXcm; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use xcm::{prelude::*, VersionedAssets, VersionedLocation, VersionedXcm}; pub use pallet::*; #[frame_support::pallet] pub mod pallet { use super::*; #[pallet::pallet] pub struct Pallet<T>(_); #[pallet::config] pub trait Config: frame_system::Config { /// Something to execute an XCM message. type XcmExecutor: ExecuteXcm; /// Required origin for executing XCM messages, including the teleport functionality. If /// successful, then it resolves to `Location` which exists as an interior location /// within this chain's XCM context. type ExecuteXcmOrigin: EnsureOrigin< <Self as frame_system::Config>::RuntimeOrigin, Success = Location, >; /// Required origin for sending XCM messages. If successful, it resolves to `Location` /// which exists as an interior location within this chain's XCM context. type SendXcmOrigin: EnsureOrigin< <Self as frame_system::Config>::RuntimeOrigin, Success = Location, >; /// The type used to actually dispatch an XCM to its destination. type XcmRouter: SendXcm; /// This chain's Universal Location. type UniversalLocation: Get<InteriorLocation>; } #[pallet::error] pub enum Error<T> { /// The version of the `Versioned` value used is not able to be interpreted. BadVersion, /// Origin is invalid for sending. InvalidOrigin, /// Could not re-anchor the assets to declare the fees for the destination chain. CannotReanchor, /// A general error indicating something went wrong with the XCM Executor. ExecutorError, /// A general error indicating something went wrong with the XCM Router. RouterError, } #[pallet::call] impl<T: Config> Pallet<T> { /// Execute an XCM from a local, signed, origin. #[pallet::call_index(0)] #[pallet::weight(Weight::default())] pub fn execute( origin: OriginFor<T>, message: Box<VersionedXcm<()>>, _max_weight: Weight, ) -> DispatchResult { let message = (*message).try_into().map_err(|()| Error::<T>::BadVersion)?; // Actually execute the XCM. Self::do_execute(origin, message) } /// Send an XCM to another consensus system. #[pallet::call_index(1)] #[pallet::weight(Weight::default())] pub fn send( origin: OriginFor<T>, dest: Box<VersionedLocation>, message: Box<VersionedXcm<()>>, ) -> DispatchResult { let dest = Location::try_from(*dest).map_err(|()| Error::<T>::BadVersion)?; let message: Xcm<()> = (*message).try_into().map_err(|()| Error::<T>::BadVersion)?; // Actually send the XCM. Self::do_send(origin, dest, message) } /// Teleport some assets from the local chain to some destination chain. #[pallet::call_index(2)] #[pallet::weight(Weight::default())] pub fn teleport_assets( origin: OriginFor<T>, dest: Box<VersionedLocation>, beneficiary: Box<VersionedLocation>, assets: Box<VersionedAssets>, fee_asset_item: u32, ) -> DispatchResult { let dest: Location = (*dest).try_into().map_err(|()| Error::<T>::BadVersion)?; let beneficiary: Location = (*beneficiary).try_into().map_err(|()| Error::<T>::BadVersion)?; let assets: Assets = (*assets).try_into().map_err(|()| Error::<T>::BadVersion)?; // Actually teleport the assets. Self::do_teleport_assets(origin, dest, beneficiary, assets, fee_asset_item) } /// Transfer some assets from the local chain to the destination chain through their local, /// destination or remote reserve. #[pallet::call_index(3)] #[pallet::weight(Weight::default())] pub fn reserve_transfer_assets( origin: OriginFor<T>, dest: Box<VersionedLocation>, beneficiary: Box<VersionedLocation>, assets: Box<VersionedAssets>, fee_asset_item: u32, ) -> DispatchResult { let dest: Location = (*dest).try_into().map_err(|()| Error::<T>::BadVersion)?; let beneficiary: Location = (*beneficiary).try_into().map_err(|()| Error::<T>::BadVersion)?; let assets: Assets = (*assets).try_into().map_err(|()| Error::<T>::BadVersion)?; // Actually reserve transfer the assets. Self::do_reserve_transfer_assets(origin, dest, beneficiary, assets, fee_asset_item) } } } impl<T: Config> Pallet<T> { /// Execute an XCM locally on this chain on behalf of `origin`. pub fn do_execute(origin: OriginFor<T>, message: Xcm<()>) -> DispatchResult { let execute_origin: Location = T::ExecuteXcmOrigin::ensure_origin(origin)?; T::XcmExecutor::execute(execute_origin, message).map_err(|_| Error::<T>::ExecutorError)?; Ok(()) } /// Relay an XCM `message` from a given `interior` location in this context to a given `dest` /// location. pub fn do_send(origin: OriginFor<T>, dest: Location, mut message: Xcm<()>) -> DispatchResult { let origin_location = T::SendXcmOrigin::ensure_origin(origin)?; let interior: Junctions = origin_location.try_into().map_err(|_| Error::<T>::InvalidOrigin)?; if interior != Junctions::Here { message.0.insert(0, DescendOrigin(interior)); } let (ticket, _) = T::XcmRouter::validate(&mut Some(dest), &mut Some(message)) .map_err(|_| Error::<T>::RouterError)?; let _message_id = T::XcmRouter::deliver(ticket).map_err(|_| Error::<T>::RouterError)?; Ok(()) } pub fn do_teleport_assets( origin: OriginFor<T>, dest: Location, beneficiary: Location, assets: Assets, // The index into `assets` of the item which should be used to pay fees. // We don't use this in our naive implementation. _fee_asset_item: u32, ) -> DispatchResult { // XCM instructions to be executed on local chain let local_execute_xcm: Xcm<()> = Xcm(vec![ // withdraw assets to be teleported WithdrawAsset(assets.clone()), // burn assets on local chain BurnAsset(assets.clone()), ]); // Changing the asset location to be in the context of the destination chain. let context = T::UniversalLocation::get(); let mut reanchored_assets = assets; reanchored_assets .reanchor(&dest, &context) .map_err(|_| Error::<T>::CannotReanchor)?; // XCM instructions to be executed on destination chain let xcm_on_dest: Xcm<()> = Xcm(vec![ // teleport `assets` in from origin chain ReceiveTeleportedAsset(reanchored_assets), // following instructions are not exec'ed on behalf of origin chain anymore ClearOrigin, // deposit all remaining assets in holding to `beneficiary` location DepositAsset { assets: Wild(All), beneficiary }, ]); // Execute the local XCM instructions. Self::do_execute(origin.clone(), local_execute_xcm)?; // Send the destination XCM instructions. Self::do_send(origin, dest, xcm_on_dest)?; Ok(()) } pub fn do_reserve_transfer_assets( _origin: OriginFor<T>, _dest: Location, _beneficiary: Location, _assets: Assets, _fee_asset_item: u32, ) -> DispatchResult { // There are 3 different reserve transfer scenarios: // - A local reserve transfer: reserve-transfer `asset` to `dest`, using local chain as // reserve. // - A destination reserve transfer: reserve-transfer `asset` to `dest`, using `dest` as // reserve. // - A remote reserve transfer: reserve-transfer `asset` to `dest`, using remote chain // `Location` as reserve. // // This is a lot to do in this workshop, but a welcome challenge for the reader to // implement. unimplemented!() } } }
diff --git a/fundamentals/src/pallet_xcm.rs b/fundamentals/src/pallet_xcm.rs
index 2df57c9..ccf515a 100644
--- a/fundamentals/src/pallet_xcm.rs
+++ b/fundamentals/src/pallet_xcm.rs
@@ -153,6 +153,28 @@ impl<T: Config> Pallet<T> {
// We don't use this in our naive implementation.
_fee_asset_item: u32,
) -> DispatchResult {
+ /* The Teleport Instruction is broken up into a local and destination XCM. */
+ /* Create a `local_execute_xcm` which does:
+ - `WithdrawAsset`
+ - `BurnAsset`
+ */
+
+ /* For the XCM on the destination:
+ - We need to adjust the location of the assets to match the destination
+ - For this, we need to `reanchor` the assets using the `dest` and local `context`
+ - For `context`, you should use the `UniversalLocation`.
+ */
+
+ /* Then prepare a `xcm_on_dest` which is composed of:
+ - `ReceiveTeleportedAsset` - using `reanchored_assets`
+ - `ClearOrigin`
+ - `DepositAsset` - using the asset filter `Wild(All)`
+ */
+
+ /* Finally, we just need to:
+ - `do_execute` our `local_execute_xcm`
+ - `do_send` our `xcm_on_dest` to `dest`
+ */
todo!("{:?} {:?} {:?}", dest, beneficiary, assets)
}
diff --git a/fundamentals/src/pallet_xcm.rs b/fundamentals/src/pallet_xcm.rs
index ccf515a..1a88020 100644
--- a/fundamentals/src/pallet_xcm.rs
+++ b/fundamentals/src/pallet_xcm.rs
@@ -153,29 +153,37 @@ impl<T: Config> Pallet<T> {
// We don't use this in our naive implementation.
_fee_asset_item: u32,
) -> DispatchResult {
- /* The Teleport Instruction is broken up into a local and destination XCM. */
- /* Create a `local_execute_xcm` which does:
- - `WithdrawAsset`
- - `BurnAsset`
- */
-
- /* For the XCM on the destination:
- - We need to adjust the location of the assets to match the destination
- - For this, we need to `reanchor` the assets using the `dest` and local `context`
- - For `context`, you should use the `UniversalLocation`.
- */
-
- /* Then prepare a `xcm_on_dest` which is composed of:
- - `ReceiveTeleportedAsset` - using `reanchored_assets`
- - `ClearOrigin`
- - `DepositAsset` - using the asset filter `Wild(All)`
- */
-
- /* Finally, we just need to:
- - `do_execute` our `local_execute_xcm`
- - `do_send` our `xcm_on_dest` to `dest`
- */
- todo!("{:?} {:?} {:?}", dest, beneficiary, assets)
+ // XCM instructions to be executed on local chain
+ let local_execute_xcm: Xcm<()> = Xcm(vec![
+ // withdraw assets to be teleported
+ WithdrawAsset(assets.clone()),
+ // burn assets on local chain
+ BurnAsset(assets.clone()),
+ ]);
+
+ // Changing the asset location to be in the context of the destination chain.
+ let context = T::UniversalLocation::get();
+ let mut reanchored_assets = assets;
+ reanchored_assets
+ .reanchor(&dest, &context)
+ .map_err(|_| Error::<T>::CannotReanchor)?;
+
+ // XCM instructions to be executed on destination chain
+ let xcm_on_dest: Xcm<()> = Xcm(vec![
+ // teleport `assets` in from origin chain
+ ReceiveTeleportedAsset(reanchored_assets),
+ // following instructions are not exec'ed on behalf of origin chain anymore
+ ClearOrigin,
+ // deposit all remaining assets in holding to `beneficiary` location
+ DepositAsset { assets: Wild(All), beneficiary },
+ ]);
+
+ // Execute the local XCM instructions.
+ Self::do_execute(origin.clone(), local_execute_xcm)?;
+ // Send the destination XCM instructions.
+ Self::do_send(origin, dest, xcm_on_dest)?;
+
+ Ok(())
}
pub fn do_reserve_transfer_assets(