diff --git a/traits/src/xcm_transfer.rs b/traits/src/xcm_transfer.rs index 18913cb00..2a2e7104b 100644 --- a/traits/src/xcm_transfer.rs +++ b/traits/src/xcm_transfer.rs @@ -1,6 +1,7 @@ use frame_support::dispatch::DispatchResult; use frame_support::weights::Weight; -use xcm::latest::prelude::*; +use sp_std::vec::Vec; +use xcm::{latest::prelude::*, DoubleEncoded}; /// Abstraction over cross-chain token transfers. pub trait XcmTransfer { @@ -11,6 +12,9 @@ pub trait XcmTransfer { amount: Balance, dest: MultiLocation, dest_weight: Weight, + //maybe_call: Option>, + //maybe_transact_call: Option>, + //maybe_transact_fee: Balance, ) -> DispatchResult; /// Transfer `MultiAsset` diff --git a/xtokens/src/lib.rs b/xtokens/src/lib.rs index 1d52a63c6..88b2b351e 100644 --- a/xtokens/src/lib.rs +++ b/xtokens/src/lib.rs @@ -36,7 +36,7 @@ use sp_runtime::{ }; use sp_std::{prelude::*, result::Result}; -use xcm::prelude::*; +use xcm::{prelude::*, DoubleEncoded}; use xcm_executor::traits::{InvertLocation, WeightBounds}; pub use module::*; @@ -94,6 +94,8 @@ pub mod module { /// XCM executor. type XcmExecutor: ExecuteXcm; + type XcmSender: SendXcm; + /// MultiLocation filter type MultiLocationsFilter: Contains; @@ -195,6 +197,8 @@ pub mod module { /// received. Receiving depends on if the XCM message could be delivered /// by the network, and if the receiving chain would handle /// messages correctly. + //#[pallet::weight(Pallet::::weight_of_transfer(currency_id.clone(), *amount, dest, maybe_transact_call, + //#[pallet::weight(Pallet::::weight_of_transfer(currency_id.clone(), dest_weight))] #[pallet::weight(Pallet::::weight_of_transfer(currency_id.clone(), *amount, dest))] #[transactional] pub fn transfer( @@ -209,6 +213,31 @@ pub mod module { Self::do_transfer(who, currency_id, amount, dest, dest_weight) } + #[pallet::weight(0)] + #[transactional] + pub fn transfer_with_transact( + origin: OriginFor, + currency_id: T::CurrencyId, + amount: T::Balance, + dest_id: u32, + dest_weight: Weight, + encoded_call_data: Vec, + //maybe_transact_call: Option>, + transact_fee: T::Balance, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + Self::do_transfer_with_transact( + who, + currency_id, + amount, + dest_id, + dest_weight, + encoded_call_data, + transact_fee, + ) + } + /// Transfer `MultiAsset`. /// /// `dest_weight` is the weight for XCM execution on the dest chain, and @@ -371,7 +400,7 @@ pub mod module { // We first grab the fee let fee: &MultiAsset = assets.get(fee_item as usize).ok_or(Error::::AssetIndexNonExistent)?; - Self::do_transfer_multiassets(who, assets.clone(), fee.clone(), dest, dest_weight) + Self::do_transfer_multiassets(who, assets.clone(), fee.clone(), dest, dest_weight, None) } } @@ -392,8 +421,90 @@ pub mod module { Error::::NotSupportedMultiLocation ); - let asset: MultiAsset = (location, amount.into()).into(); - Self::do_transfer_multiassets(who, vec![asset.clone()].into(), asset, dest, dest_weight) + let asset: MultiAsset = (location.clone(), amount.into()).into(); + + Self::do_transfer_multiassets( + who.clone(), + vec![asset.clone()].into(), + asset, + dest.clone(), + dest_weight, + None, + ) + } + + fn do_transfer_with_transact( + who: T::AccountId, + currency_id: T::CurrencyId, + amount: T::Balance, + dest_id: u32, + dest_weight: Weight, + encoded_call_data: Vec, + transact_fee: T::Balance, + ) -> DispatchResult { + let location: MultiLocation = + T::CurrencyIdConvert::convert(currency_id).ok_or(Error::::NotCrossChainTransferableCurrency)?; + + let origin_location = T::AccountIdToMultiLocation::convert(who.clone()); + + let mut dest_chain_location: MultiLocation = (1, Parachain(dest_id)).into(); + let _ = dest_chain_location.append_with(origin_location.clone().interior); + + ensure!(!amount.is_zero(), Error::::ZeroAmount); + ensure!( + T::MultiLocationsFilter::contains(&dest_chain_location), + Error::::NotSupportedMultiLocation + ); + + let asset: MultiAsset = (location.clone(), amount.into()).into(); + let transact_fee_asset: MultiAsset = (location, transact_fee.into()).into(); + + let mut override_dest = T::SelfLocation::get(); + let _ = override_dest.append_with(origin_location.clone().interior); + + let _ = Self::do_transfer_multiassets( + who.clone(), + vec![asset.clone()].into(), + asset, + dest_chain_location.clone(), + dest_weight, + // TODO: not a todo, but this part is important + Some(override_dest), + ); + + let origin_location = T::AccountIdToMultiLocation::convert(who.clone()); + // TODO: is this the best way to get dest location + let target_chain_location = dest_chain_location.chain_part().ok_or(Error::::AssetHasNoReserve)?; + let ancestry = T::LocationInverter::ancestry(); + + let transact_fee_asset = transact_fee_asset + .clone() + .reanchored(&target_chain_location, &ancestry) + .map_err(|_| Error::::CannotReanchor)?; + + //let double_encoded: DoubleEncoded = call.into(); + let mut transact_fee_assets = MultiAssets::new(); + transact_fee_assets.push(transact_fee_asset.clone()); + let instructions = Xcm(vec![ + DescendOrigin(origin_location.clone().interior), + WithdrawAsset(transact_fee_assets), + BuyExecution { + fees: transact_fee_asset, + weight_limit: WeightLimit::Limited(dest_weight), + }, + Transact { + origin_type: OriginKind::SovereignAccount, + require_weight_at_most: dest_weight, + call: encoded_call_data.into(), + //call, + }, + // TODO: + // RefundSurplus + // DepositAsset(user_account or back to sovereign_account) + ]); + + let _res = T::XcmSender::send_xcm(target_chain_location.clone(), instructions); + Ok(()) } fn do_transfer_with_fee( @@ -422,7 +533,7 @@ pub mod module { assets.push(asset); assets.push(fee_asset.clone()); - Self::do_transfer_multiassets(who, assets, fee_asset, dest, dest_weight) + Self::do_transfer_multiassets(who, assets, fee_asset, dest, dest_weight, None) } fn do_transfer_multiasset( @@ -431,7 +542,7 @@ pub mod module { dest: MultiLocation, dest_weight: Weight, ) -> DispatchResult { - Self::do_transfer_multiassets(who, vec![asset.clone()].into(), asset, dest, dest_weight) + Self::do_transfer_multiassets(who, vec![asset.clone()].into(), asset, dest, dest_weight, None) } fn do_transfer_multiasset_with_fee( @@ -446,7 +557,7 @@ pub mod module { assets.push(asset); assets.push(fee.clone()); - Self::do_transfer_multiassets(who, assets, fee, dest, dest_weight)?; + Self::do_transfer_multiassets(who, assets, fee, dest, dest_weight, None)?; Ok(()) } @@ -490,7 +601,7 @@ pub mod module { let fee: MultiAsset = (fee_location, (*fee_amount).into()).into(); - Self::do_transfer_multiassets(who, assets, fee, dest, dest_weight) + Self::do_transfer_multiassets(who, assets, fee, dest, dest_weight, None) } fn do_transfer_multiassets( @@ -499,6 +610,10 @@ pub mod module { fee: MultiAsset, dest: MultiLocation, dest_weight: Weight, + //maybe_transact_call: Option>, + //maybe_transact_call: Option>, + //maybe_transact_fee_asset: Option, + maybe_recipient_override: Option, ) -> DispatchResult { ensure!( assets.len() <= T::MaxAssetsForTransfer::get(), @@ -571,22 +686,23 @@ pub mod module { // Second xcm send to dest chain. Self::execute_and_send_reserve_kind_xcm( - origin_location, + origin_location.clone(), assets_to_dest, fee_to_dest, non_fee_reserve, &dest, None, dest_weight, + // fee_reserve != non_fee_reserve )?; } else { Self::execute_and_send_reserve_kind_xcm( - origin_location, + origin_location.clone(), assets.clone(), fee.clone(), - non_fee_reserve, + non_fee_reserve.clone(), &dest, - None, + maybe_recipient_override, dest_weight, )?; } @@ -765,6 +881,7 @@ pub mod module { let (dest, recipient) = Self::ensure_valid_dest(dest)?; let self_location = T::SelfLocation::get(); + // TODO: skip check ensure!(dest != self_location, Error::::NotCrossChainTransfer); let reserve = reserve.ok_or(Error::::AssetHasNoReserve)?; let transfer_kind = if reserve == self_location { @@ -784,6 +901,9 @@ pub mod module { fn weight_of_transfer_multiasset(asset: &VersionedMultiAsset, dest: &VersionedMultiLocation) -> Weight { let asset: Result = asset.clone().try_into(); let dest = dest.clone().try_into(); + // TODO: + // if maybe_transact_call, include Transact instruction with + // required_weight_at_most if let (Ok(asset), Ok(dest)) = (asset, dest) { if let Ok((transfer_kind, dest, _, reserve)) = Self::transfer_kind(T::ReserveProvider::reserve(&asset), &dest) @@ -816,7 +936,13 @@ pub mod module { } /// Returns weight of `transfer` call. - fn weight_of_transfer(currency_id: T::CurrencyId, amount: T::Balance, dest: &VersionedMultiLocation) -> Weight { + fn weight_of_transfer( + currency_id: T::CurrencyId, + amount: T::Balance, + dest: &VersionedMultiLocation, + // maybe_transact_call: Option>, + // dest_weight: Weight, + ) -> Weight { if let Some(location) = T::CurrencyIdConvert::convert(currency_id) { let asset = (location, amount.into()).into(); Self::weight_of_transfer_multiasset(&asset, dest) @@ -908,6 +1034,9 @@ pub mod module { amount: T::Balance, dest: MultiLocation, dest_weight: Weight, + //maybe_transact_call: Option>, + //maybe_transact_call: Option>, + //maybe_transact_fee: T::Balance, ) -> DispatchResult { Self::do_transfer(who, currency_id, amount, dest, dest_weight) }