#![allow(unused)]fnmain() {
//! # Fundamentals lesson 6: Pallet XCM//!//! Implement the core functionality of Pallet XCMuse crate::xcm_executor::ExecuteXcm;
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
use xcm::{prelude::*, VersionedAssets, VersionedLocation, VersionedXcm};
pubuse pallet::*;
#[frame_support::pallet]pubmod pallet {
use super::*;
#[pallet::pallet]pubstructPallet<T>(_);
#[pallet::config]pubtraitConfig: frame_system::Config {
/// Something to execute an XCM message.typeXcmExecutor: 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.typeExecuteXcmOrigin: EnsureOrigin<
<Selfas 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.typeSendXcmOrigin: EnsureOrigin<
<Selfas frame_system::Config>::RuntimeOrigin,
Success = Location,
>;
/// The type used to actually dispatch an XCM to its destination.typeXcmRouter: SendXcm;
/// This chain's Universal Location.typeUniversalLocation: Get<InteriorLocation>;
}
#[pallet::error]pubenumError<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())]pubfnexecute(
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())]pubfnsend(
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())]pubfnteleport_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())]pubfnreserve_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`.pubfndo_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.pubfndo_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(&mutSome(dest), &mutSome(message))
.map_err(|_| Error::<T>::RouterError)?;
let _message_id = T::XcmRouter::deliver(ticket).map_err(|_| Error::<T>::RouterError)?;
Ok(())
}
pubfndo_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)
}
pubfndo_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)]fnmain() {
//! # Fundamentals lesson 6: Pallet XCM//!//! Implement the core functionality of Pallet XCMuse crate::xcm_executor::ExecuteXcm;
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
use xcm::{prelude::*, VersionedAssets, VersionedLocation, VersionedXcm};
pubuse pallet::*;
#[frame_support::pallet]pubmod pallet {
use super::*;
#[pallet::pallet]pubstructPallet<T>(_);
#[pallet::config]pubtraitConfig: frame_system::Config {
/// Something to execute an XCM message.typeXcmExecutor: 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.typeExecuteXcmOrigin: EnsureOrigin<
<Selfas 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.typeSendXcmOrigin: EnsureOrigin<
<Selfas frame_system::Config>::RuntimeOrigin,
Success = Location,
>;
/// The type used to actually dispatch an XCM to its destination.typeXcmRouter: SendXcm;
/// This chain's Universal Location.typeUniversalLocation: Get<InteriorLocation>;
}
#[pallet::error]pubenumError<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())]pubfnexecute(
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())]pubfnsend(
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())]pubfnteleport_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())]pubfnreserve_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`.pubfndo_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.pubfndo_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(&mutSome(dest), &mutSome(message))
.map_err(|_| Error::<T>::RouterError)?;
let _message_id = T::XcmRouter::deliver(ticket).map_err(|_| Error::<T>::RouterError)?;
Ok(())
}
pubfndo_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 chainlet 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();
letmut reanchored_assets = assets;
reanchored_assets
.reanchor(&dest, &context)
.map_err(|_| Error::<T>::CannotReanchor)?;
// XCM instructions to be executed on destination chainlet 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(())
}
pubfndo_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(