-
Notifications
You must be signed in to change notification settings - Fork 517
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support different assets for delivery fees #4375
base: master
Are you sure you want to change the base?
Conversation
98c7ded
to
da83c68
Compare
cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs
Outdated
Show resolved
Hide resolved
This pull request has been mentioned on Polkadot Forum. There might be relevant details there: https://forum.polkadot.network/t/xcm-user-and-developer-experience-improvements/4511/21 |
Here's a link to docs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if we disable the FeeManager
for our runtime setups and try to account a transport weights in Weigher
. Even if we cannot do it properly, may be some reasonable constants would be enough.
For local execution the transport fee will be included in extrinsics' weights, and for the remote execution a user will pay with BuyExecution
for transport fee as well.
To avoid a double payment with jit_withdraw
fee mode, we will need to disable it via config (here we also can provide some info for dependent teams).
After reading the weights payment model in the xcm pallet and the executor, I do not see a good way to make it work with various assets through the FeeManager without breaking the APIs. With the new xcm version we can have a better solution.
}, | ||
Err(error) => { | ||
// TODO: log::error is too noisy and this might get called a lot. | ||
log::error!( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's not really an error since we still trying
let actual_asset_to_use_for_fees = match Config::AssetConverter::convert_asset(&first, asset_id) { | ||
Ok(new_asset) => new_asset, | ||
Err(error) => { | ||
log::error!(target: "xcm::DepositReserveAsset", "What happened?"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this will error every time when we had no asset_for_fees
in registry
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah this case hasn't been done yet
polkadot/xcm/xcm-executor/src/lib.rs
Outdated
Config::AssetTransactor::withdraw_asset(&actual_asset_to_use, &origin, None)?; | ||
Config::FeeManager::handle_fee(fees, None, FeeReason::ChargeFees); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this looks dangerous, to withdraw only one and handle all
@@ -2422,7 +2422,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)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is this always here? even if we want it to be native always, it wont be here for system parachains
weight_limit, | ||
)?; | ||
Self::execute_xcm_transfer(origin, dest, local_xcm, remote_xcm) | ||
Self::execute_xcm_transfer(origin, dest, local_xcm, remote_xcm, fees.id) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the assumption is that a user always wanna pay local fees with the same asset as on a remote chain. this wont work for example when a user does a transfer all for fees.id
asset.
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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we could convert an asset from assets
into asset_for_fees
here, and avoid breaking the trait api at least for this case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
here I mean on the pallet
@@ -945,6 +1070,9 @@ impl<Config: config::Config> XcmExecutor<Config> { | |||
// should be executed. | |||
let Some(weight) = Option::<Weight>::from(weight_limit) else { return Ok(()) }; | |||
let old_holding = self.holding.clone(); | |||
// Store the asset being used for fees, so delivery fees can access it. | |||
self.asset_for_fees = Some(fees.id.clone()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is reasonable assumption that a user is fine to pay a transport fee with the same asset used for execution fee. The only problem is that local executions wont have the BuyExecution
instruction.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's my main concern, and the reason why I did the "looping through all assets in holding" trick, which I know is bad.
If local executions had a BuyExecution
instruction then this would be great.
My current thought is to create a new entrypoint prepare_and_execute_with_fee_asset
.
self.origin_ref(), | ||
self.fees_mode, | ||
reason, | ||
); | ||
let first = fees.get(0).ok_or(XcmError::AssetNotFound)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
valid for our implementations, but if someone have more than one here, there is a problem
// It's not empty because of jit withdrawal, it's empty because it's taken from holding before sending. | ||
// That's the problem jit withdrawal was solving, we need to "park" delivery fees in the same way we did | ||
// for `DepositReserveAsset`. | ||
for asset in self.holding.fungible_assets_iter() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This way we basically deciding for a user which asset to use for the payment, this can be a surprise.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, but some API needs to change or I have to add a new one to not have to do this.
I was thinking of creating a new entrypoint prepare_and_execute_with_fee_asset
that pallet-xcm
can use.
That'd keep the API the same for all other teams.
What do you think?
The CI pipeline was cancelled due to failure one of the required jobs. |
Context
Fees can already be paid in other assets locally thanks to the
Trader
implementations we have.This doesn't work when sending messages because delivery fees can only be paid in one asset.
The idea is to fix this by providing an
AssetConverter
that's able to turn the asset the user wants to pay fees in into the asset the router expects for delivery fees.Paying delivery fees in USDT instead of WND on Westend Asset Hub (uses min_balance ratio since it's sufficient):
Paying delivery fees in a custom asset in a pool with WND (notice how the custom asset is withdrawn but WND is deposited):
Main addition
A new xcm executor config item is introduced:
AssetConverter
, that can convert balances from one asset to another.It's used for paying delivery fees in different assets other than the native one (the one specified in the XcmRouter).
How it works
In order to pay delivery fees with different assets, you need to add some type as the
AssetConverter
in your XCM executor.Right now, the
AssetConverter
approach will only work for delivery fees, for execution fees, you need to copy the same logic onto aTrader
.The logic is mostly the same, only the interface changes. The Trader has to do a little bit more work.
This duplicated logic is planned to be removed in v5, when we have the same mechanism for both execution and delivery fees.
If there is no
AssetConverter
then nothing will break as long as you pay fees in the intended asset (the one in XcmRouter).If there is, then you can define which assets can be used for fees.
Tests
The following asset hub westend integration tests show scenarios where this is used:
Breaking changes
This PR breaks the following things:
AssetConverter
needs to be set in the executor config, even if it's just()
.XcmExecutor::charge_fees
signature changed.Dear reviewer
TODO