Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
franciscoaguirre committed May 3, 2024
1 parent c973fe8 commit 98c7ded
Show file tree
Hide file tree
Showing 14 changed files with 234 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1137,3 +1137,125 @@ fn reserve_transfer_native_asset_from_para_to_para_through_relay() {
// Receiver's balance is increased
assert!(receiver_assets_after > receiver_assets_before);
}

// =========================================================================
// ======= Reserve Transfers - Sufficient Asset - AssetHub<>Parachain ======
// =========================================================================
/// Reserve Transfers of a local asset and native asset from System Parachain to Parachain should
/// work
#[test]
fn reserve_transfer_sufficient_assets_from_system_para_to_para() {
use penpal_runtime::xcm_config::ASSET_HUB_ID;

let destination = AssetHubWestend::sibling_location_of(PenpalA::para_id());
let sov_penpal_on_ahr = AssetHubWestend::sovereign_account_id_of(destination.clone());
let sender = AssetHubWestendSender::get();
let fee_amount_to_send = ASSET_HUB_WESTEND_ED * 100;
let asset_amount_to_send = ASSET_HUB_WESTEND_ED * 100;
let asset_owner = AssetHubWestendAssetOwner::get();
let asset_owner_signer = <AssetHubWestend as Chain>::RuntimeOrigin::signed(asset_owner.clone());
let receiver = PenpalAReceiver::get();

let asset_id = 1984;
// Create sufficient asset.
AssetHubWestend::force_create_asset(
asset_id,
asset_owner,
true, // Mark it as sufficient.
70_000,
vec![(sender.clone(), ASSET_HUB_WESTEND_ED * 10000)],
);
let system_para_foreign_asset_location = Location::new(
1,
[
Parachain(ASSET_HUB_ID),
PalletInstance(ASSETS_PALLET_ID),
GeneralIndex(asset_id.into()),
],
);

// Create sufficient foreign asset.
PenpalA::force_create_foreign_asset(
system_para_foreign_asset_location.clone(),
PenpalAssetOwner::get(),
true, // Mark it as sufficient.
70_000,
vec![],
);

let assets: Assets = vec![
(
(PalletInstance(ASSETS_PALLET_ID), GeneralIndex(asset_id.into())),
asset_amount_to_send + fee_amount_to_send,
).into(),
].into();
let fee_asset_index = 0;

// Create SA-of-Penpal-on-AHR with ED.
// This ED isn't reflected in any derivative in a PenpalA account.
AssetHubWestend::fund_accounts(vec![(sov_penpal_on_ahr.into(), ASSET_HUB_WESTEND_ED)]);

// We want to make sure the sender doesn't have ANY native asset, so we can make
// sure we are paying delivery fees with the sufficient asset.
<AssetHubWestend as TestExt>::execute_with(|| {
assert_ok!(<AssetHubWestend as AssetHubWestendPallet>::Balances::force_set_balance(
<AssetHubWestend as Chain>::RuntimeOrigin::root(),
sender.clone().into(),
ASSET_HUB_WESTEND_ED,
));
});

let para_test_args = TestContext {
sender: sender.clone(),
receiver: receiver.clone(),
args: TestArgs::new_para(
destination,
receiver.clone(),
asset_amount_to_send,
assets,
None,
fee_asset_index,
),
};
let mut test = SystemParaToParaTest::new(para_test_args);

// Query initial balances.
let sender_balance_before = test.sender.balance;
dbg!(&sender_balance_before);
let sender_assets_before = AssetHubWestend::execute_with(|| {
type Assets = <AssetHubWestend as AssetHubWestendPallet>::Assets;
<Assets as Inspect<_>>::balance(asset_id, &sender)
});
dbg!(&sender_assets_before);
let receiver_foreign_assets_before = PenpalA::execute_with(|| {
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(
system_para_foreign_asset_location.clone(),
&receiver,
)
});
dbg!(&receiver_foreign_assets_before);

// Set assertions and dispatchables
// test.set_assertion::<AssetHubWestend>(system_para_to_para_assets_sender_assertions);
// test.set_assertion::<PenpalA>(system_para_to_para_assets_receiver_assertions);
test.set_dispatchable::<AssetHubWestend>(system_para_to_para_reserve_transfer_assets);
test.assert();

// Query initial balances.
let sender_balance_after = test.sender.balance;
dbg!(&sender_balance_after);
let sender_assets_after = AssetHubWestend::execute_with(|| {
type Assets = <AssetHubWestend as AssetHubWestendPallet>::Assets;
<Assets as Inspect<_>>::balance(asset_id, &sender)
});
dbg!(&sender_assets_after);
let receiver_foreign_assets_after = PenpalA::execute_with(|| {
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(
system_para_foreign_asset_location.clone(),
&receiver,
)
});
dbg!(&receiver_foreign_assets_after);
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use assets_common::{
matching::{FromSiblingParachain, IsForeignConcreteAsset},
TrustBackedAssetsAsLocation,
};
use core::marker::PhantomData;
use frame_support::{
parameter_types,
traits::{
Expand Down Expand Up @@ -349,6 +350,46 @@ pub type TrustedTeleporters = (
IsForeignConcreteAsset<FromSiblingParachain<parachain_info::Pallet<Runtime>>>,
);

use xcm_executor::traits::{MatchesFungibles, AssetConversion};
use frame_support::traits::fungibles;
use frame_support::traits::tokens::ConversionToAssetBalance;

pub struct IsSufficientAssetConverter<Runtime, Matcher, BalanceConverter, ConcreteAssets, AssetsInstance>(PhantomData<(Runtime, Matcher, BalanceConverter, ConcreteAssets, AssetsInstance)>);
impl<Runtime, Matcher, BalanceConverter, ConcreteAssets, AssetsInstance> AssetConversion for IsSufficientAssetConverter<Runtime, Matcher, BalanceConverter, ConcreteAssets, AssetsInstance>
where
Runtime: pallet_assets::Config<AssetsInstance>,
Matcher: MatchesFungibles<
<Runtime as pallet_assets::Config<AssetsInstance>>::AssetId,
<Runtime as pallet_assets::Config<AssetsInstance>>::Balance
>,
BalanceConverter: ConversionToAssetBalance<
u128,
<Runtime as pallet_assets::Config<AssetsInstance>>::AssetId,
<Runtime as pallet_assets::Config<AssetsInstance>>::Balance
>,
AssetsInstance: 'static,
{
fn convert_asset(asset: &Asset, asset_id: &AssetId) -> Asset {
let desired_asset: Asset = (asset_id.clone(), 0u128).into(); // To comply with the interface.
let local_asset_id = match Matcher::matches_fungibles(&desired_asset) {
Ok((local_asset_id, _)) => local_asset_id,
Err(_) => return asset.clone(), // We return the same asset that was passed in in case of an error.
};
log::trace!(target: "xcm::convert_asset", "local asset id: {:?}", local_asset_id);
let Fungibility::Fungible(old_asset_amount) = asset.fun else { todo!() };
let new_asset_amount = BalanceConverter::to_asset_balance(old_asset_amount, local_asset_id).map_err(|_| ()).unwrap(); // TODO
(asset_id.clone(), new_asset_amount).into()
}
}

pub type IsSufficientFeeAssetConverter = IsSufficientAssetConverter<
Runtime,
TrustBackedAssetsConvertedConcreteId,
pallet_assets::BalanceToAssetBalance<Balances, Runtime, ConvertInto, TrustBackedAssetsInstance>,
Assets,
TrustBackedAssetsInstance,
>;

pub struct XcmConfig;
impl xcm_executor::Config for XcmConfig {
type RuntimeCall = RuntimeCall;
Expand Down Expand Up @@ -419,6 +460,7 @@ impl xcm_executor::Config for XcmConfig {
>,
>,
);
type AssetConverter = IsSufficientFeeAssetConverter;
type ResponseHandler = PolkadotXcm;
type AssetTrap = PolkadotXcm;
type AssetClaims = PolkadotXcm;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ impl xcm_executor::Config for XcmConfig {
type HrmpNewChannelOpenRequestHandler = ();
type HrmpChannelAcceptedHandler = ();
type HrmpChannelClosingHandler = ();
type AssetConverter = ();
}

pub type PriceForParentDelivery =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ impl xcm_executor::Config for XcmConfig {
type HrmpNewChannelOpenRequestHandler = ();
type HrmpChannelAcceptedHandler = ();
type HrmpChannelClosingHandler = ();
type AssetConverter = ();
}

/// Converts a local signed origin into an XCM location.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ impl xcm_executor::Config for XcmConfig {
type HrmpNewChannelOpenRequestHandler = ();
type HrmpChannelAcceptedHandler = ();
type HrmpChannelClosingHandler = ();
type AssetConverter = ();
}

/// Converts a local signed origin into an XCM location. Forms the basis for local origins
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ impl xcm_executor::Config for XcmConfig {
type HrmpNewChannelOpenRequestHandler = ();
type HrmpChannelAcceptedHandler = ();
type HrmpChannelClosingHandler = ();
type AssetConverter = ();
}

/// Multiplier used for dedicated `TakeFirstAssetTrader` with `ForeignAssets` instance.
Expand Down
1 change: 1 addition & 0 deletions polkadot/runtime/rococo/src/xcm_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ impl xcm_executor::Config for XcmConfig {
type HrmpNewChannelOpenRequestHandler = ();
type HrmpChannelAcceptedHandler = ();
type HrmpChannelClosingHandler = ();
type AssetConverter = ();
}

parameter_types! {
Expand Down
1 change: 1 addition & 0 deletions polkadot/runtime/westend/src/xcm_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ impl xcm_executor::Config for XcmConfig {
type HrmpNewChannelOpenRequestHandler = ();
type HrmpChannelAcceptedHandler = ();
type HrmpChannelClosingHandler = ();
type AssetConverter = ();
}

parameter_types! {
Expand Down
23 changes: 12 additions & 11 deletions polkadot/xcm/pallet-xcm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1568,10 +1568,10 @@ impl<T: Config> Pallet<T> {
Either::Left(beneficiary),
assets,
assets_transfer_type,
FeesHandling::Batched { fees },
FeesHandling::Batched { fees: fees.clone() },
weight_limit,
)?;
Self::execute_xcm_transfer(origin, dest, local_xcm, remote_xcm)
Self::execute_xcm_transfer(origin, dest, local_xcm, remote_xcm, fees.id)
}

fn do_teleport_assets(
Expand Down Expand Up @@ -1610,10 +1610,10 @@ impl<T: Config> Pallet<T> {
Either::Left(beneficiary),
assets,
TransferType::Teleport,
FeesHandling::Batched { fees },
FeesHandling::Batched { fees: fees.clone() },
weight_limit,
)?;
Self::execute_xcm_transfer(origin_location, dest, local_xcm, remote_xcm)
Self::execute_xcm_transfer(origin_location, dest, local_xcm, remote_xcm, fees.id)
}

fn do_transfer_assets(
Expand All @@ -1626,11 +1626,11 @@ impl<T: Config> Pallet<T> {
fees_transfer_type: TransferType,
weight_limit: WeightLimit,
) -> DispatchResult {
let asset_for_fees = assets.get(fee_asset_index).ok_or(Error::<T>::Empty)?.clone();
// local and remote XCM programs to potentially handle fees separately
let fees = if fees_transfer_type == assets_transfer_type {
let fees = assets.get(fee_asset_index).ok_or(Error::<T>::Empty)?.clone();
// no need for custom fees instructions, fees are batched with assets
FeesHandling::Batched { fees }
FeesHandling::Batched { fees: asset_for_fees.clone() }
} else {
// Disallow _remote reserves_ unless assets & fees have same remote reserve (covered
// by branch above). The reason for this is that we'd need to send XCMs to separate
Expand Down Expand Up @@ -1679,7 +1679,7 @@ impl<T: Config> Pallet<T> {
fees,
weight_limit,
)?;
Self::execute_xcm_transfer(origin, dest, local_xcm, remote_xcm)
Self::execute_xcm_transfer(origin, dest, local_xcm, remote_xcm, asset_for_fees.id)
}

fn build_xcm_transfer_type(
Expand Down Expand Up @@ -1749,6 +1749,7 @@ impl<T: Config> Pallet<T> {
dest: Location,
mut local_xcm: Xcm<<T as Config>::RuntimeCall>,
remote_xcm: Option<Xcm<()>>,
asset_for_fees: AssetId,
) -> DispatchResult {
log::debug!(
target: "xcm::pallet_xcm::execute_xcm_transfer",
Expand Down Expand Up @@ -1779,7 +1780,7 @@ impl<T: Config> Pallet<T> {
let (ticket, price) = validate_send::<T::XcmRouter>(dest.clone(), remote_xcm.clone())
.map_err(Error::<T>::from)?;
if origin != Here.into_location() {
Self::charge_fees(origin.clone(), price).map_err(|error| {
Self::charge_fees(origin.clone(), price, &asset_for_fees).map_err(|error| {
log::error!(
target: "xcm::pallet_xcm::execute_xcm_transfer",
"Unable to charge fee with error {:?}", error
Expand Down Expand Up @@ -2403,7 +2404,7 @@ impl<T: Config> Pallet<T> {
log::debug!(target: "xcm::send_xcm", "dest: {:?}, message: {:?}", &dest, &message);
let (ticket, price) = validate_send::<T::XcmRouter>(dest, message)?;
if let Some(fee_payer) = maybe_fee_payer {
Self::charge_fees(fee_payer, price).map_err(|_| SendError::Fees)?;
Self::charge_fees(fee_payer, price, &AssetId(Here.into())).map_err(|_| SendError::Fees)?;
}
T::XcmRouter::deliver(ticket)
}
Expand Down Expand Up @@ -2546,8 +2547,8 @@ impl<T: Config> Pallet<T> {
/// Fails if:
/// - the `assets` are not known on this chain;
/// - the `assets` cannot be withdrawn with that location as the Origin.
fn charge_fees(location: Location, assets: Assets) -> DispatchResult {
T::XcmExecutor::charge_fees(location.clone(), assets.clone())
fn charge_fees(location: Location, assets: Assets, asset_for_fees: &AssetId) -> DispatchResult {
T::XcmExecutor::charge_fees(location.clone(), assets.clone(), asset_for_fees)
.map_err(|_| Error::<T>::FeesNotMet)?;
Self::deposit_event(Event::FeesPaid { paying: location, fees: assets });
Ok(())
Expand Down
4 changes: 2 additions & 2 deletions polkadot/xcm/src/v4/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ pub trait ExecuteXcm<Call> {

/// Deduct some `fees` to the sovereign account of the given `location` and place them as per
/// the convention for fees.
fn charge_fees(location: impl Into<Location>, fees: Assets) -> Result;
fn charge_fees(location: impl Into<Location>, fees: Assets, asset_for_fees: &AssetId) -> Result;
}

pub enum Weightless {}
Expand All @@ -120,7 +120,7 @@ impl<C> ExecuteXcm<C> for () {
fn execute(_: impl Into<Location>, _: Self::Prepared, _: &mut XcmHash, _: Weight) -> Outcome {
unreachable!()
}
fn charge_fees(_location: impl Into<Location>, _fees: Assets) -> Result {
fn charge_fees(_location: impl Into<Location>, _fees: Assets, _asset_for_fees: &AssetId) -> Result {
Err(Error::Unimplemented)
}
}
Expand Down
5 changes: 4 additions & 1 deletion polkadot/xcm/xcm-executor/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.

use crate::traits::{
AssetExchange, AssetLock, CallDispatcher, ClaimAssets, ConvertOrigin, DropAssets, ExportXcm,
AssetExchange, AssetConversion, AssetLock, CallDispatcher, ClaimAssets, ConvertOrigin, DropAssets, ExportXcm,
FeeManager, HandleHrmpChannelAccepted, HandleHrmpChannelClosing,
HandleHrmpNewChannelOpenRequest, OnResponse, ProcessTransaction, ShouldExecute, TransactAsset,
VersionChangeNotifier, WeightBounds, WeightTrader,
Expand Down Expand Up @@ -122,4 +122,7 @@ pub trait Config {
type HrmpChannelAcceptedHandler: HandleHrmpChannelAccepted;
/// Allows optional logic execution for the `HrmpChannelClosing` XCM notification.
type HrmpChannelClosingHandler: HandleHrmpChannelClosing;

/// Allows converting a balance of one asset into another.
type AssetConverter: AssetConversion;
}
Loading

0 comments on commit 98c7ded

Please sign in to comment.