Skip to content

Commit db7a34b

Browse files
mrshiposhaacatangiu
authored andcommitted
XcmPaymentApi::query_weight_to_asset_fee simple common impl (#8281)
# Description Add a common implementation for `XcmPaymentApi::query_weight_to_asset_fee` to `pallet-xcm`. This PR is a simple alternative to #8202 (which could still be useful for other reasons). It uses a workaround instead of a big refactoring. The workaround is: Computes the weight cost using the provided `WeightTrader`. This function is supposed to be used ONLY in `XcmPaymentApi::query_weight_to_asset_fee` Runtime API implementation, as it can introduce a massive change to the total issuance. The provided `WeightTrader` must be the same as the one used in the XcmExecutor to ensure uniformity in the weight cost calculation. NOTE: Currently this function uses a workaround that should be good enough for all practical uses: passes `u128::MAX / 2 == 2^127` of the specified asset to the `WeightTrader` as payment and computes the weight cost as the difference between this and the unspent amount. Some weight traders could add the provided payment to some account's balance. However, it should practically never result in overflow because even currencies with a lot of decimal digits (say 18) usually have the total issuance of billions (`x * 10^9`) or trillions (`x * 10^12`) at max, much less than `2^127 / 10^18 =~ 1.7 * 10^20` (170 billion billion). Thus, any account's balance most likely holds less than `2^127`, so adding `2^127` won't result in `u128` overflow. ## Integration The Runtime builders can use the `query_weight_to_asset_fee` provided by `pallet-xcm` in their XcmPaymentApi implementation. --------- Co-authored-by: Adrian Catangiu <adrian@parity.io>
1 parent ceca6c5 commit db7a34b

27 files changed

Lines changed: 455 additions & 352 deletions

File tree

cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs

Lines changed: 6 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ use frame_support::{
6666
AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU32, ConstU64, ConstU8,
6767
ConstantStoragePrice, EitherOfDiverse, Equals, InstanceFilter, TransformOrigin,
6868
},
69-
weights::{ConstantMultiplier, Weight, WeightToFee as _},
69+
weights::{ConstantMultiplier, Weight},
7070
BoundedVec, PalletId,
7171
};
7272
use frame_system::{
@@ -1556,33 +1556,11 @@ impl_runtime_apis! {
15561556
}
15571557

15581558
fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result<u128, XcmPaymentApiError> {
1559-
let native_asset = xcm_config::TokenLocation::get();
1560-
let fee_in_native = WeightToFee::weight_to_fee(&weight);
1561-
let latest_asset_id: Result<AssetId, ()> = asset.clone().try_into();
1562-
match latest_asset_id {
1563-
Ok(asset_id) if asset_id.0 == native_asset => {
1564-
// for native token
1565-
Ok(fee_in_native)
1566-
},
1567-
Ok(asset_id) => {
1568-
// Try to get current price of `asset_id` in `native_asset`.
1569-
if let Ok(Some(swapped_in_native)) = assets_common::PoolAdapter::<Runtime>::quote_price_tokens_for_exact_tokens(
1570-
asset_id.0.clone(),
1571-
native_asset,
1572-
fee_in_native,
1573-
true, // We include the fee.
1574-
) {
1575-
Ok(swapped_in_native)
1576-
} else {
1577-
log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!");
1578-
Err(XcmPaymentApiError::AssetNotFound)
1579-
}
1580-
},
1581-
Err(_) => {
1582-
log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - failed to convert asset: {asset:?}!");
1583-
Err(XcmPaymentApiError::VersionedConversionFailed)
1584-
}
1585-
}
1559+
use crate::xcm_config::XcmConfig;
1560+
1561+
type Trader = <XcmConfig as xcm_executor::Config>::Trader;
1562+
1563+
PolkadotXcm::query_weight_to_asset_fee::<Trader>(weight, asset)
15861564
}
15871565

15881566
fn query_xcm_weight(message: VersionedXcm<()>) -> Result<Weight, XcmPaymentApiError> {

cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs

Lines changed: 13 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ use asset_test_utils::{
3434
ExtBuilder, GovernanceOrigin, SlotDurations,
3535
};
3636
use codec::{Decode, Encode};
37-
use core::ops::Mul;
3837
use frame_support::{
3938
assert_noop, assert_ok, parameter_types,
4039
traits::{
@@ -91,52 +90,6 @@ fn slot_durations() -> SlotDurations {
9190
}
9291
}
9392

94-
fn setup_pool_for_paying_fees_with_foreign_assets(
95-
(foreign_asset_owner, foreign_asset_id_location, foreign_asset_id_minimum_balance): (
96-
AccountId,
97-
Location,
98-
Balance,
99-
),
100-
) {
101-
let existential_deposit = ExistentialDeposit::get();
102-
103-
// setup a pool to pay fees with `foreign_asset_id_location` tokens
104-
let pool_owner: AccountId = [14u8; 32].into();
105-
let native_asset = Location::parent();
106-
let pool_liquidity: Balance =
107-
existential_deposit.max(foreign_asset_id_minimum_balance).mul(100_000);
108-
109-
let _ = Balances::force_set_balance(
110-
RuntimeOrigin::root(),
111-
pool_owner.clone().into(),
112-
(existential_deposit + pool_liquidity).mul(2).into(),
113-
);
114-
115-
assert_ok!(ForeignAssets::mint(
116-
RuntimeOrigin::signed(foreign_asset_owner),
117-
foreign_asset_id_location.clone().into(),
118-
pool_owner.clone().into(),
119-
(foreign_asset_id_minimum_balance + pool_liquidity).mul(2).into(),
120-
));
121-
122-
assert_ok!(AssetConversion::create_pool(
123-
RuntimeOrigin::signed(pool_owner.clone()),
124-
Box::new(native_asset.clone().into()),
125-
Box::new(foreign_asset_id_location.clone().into())
126-
));
127-
128-
assert_ok!(AssetConversion::add_liquidity(
129-
RuntimeOrigin::signed(pool_owner.clone()),
130-
Box::new(native_asset.into()),
131-
Box::new(foreign_asset_id_location.into()),
132-
pool_liquidity,
133-
pool_liquidity,
134-
1,
135-
1,
136-
pool_owner,
137-
));
138-
}
139-
14093
#[test]
14194
fn test_buy_and_refund_weight_in_native() {
14295
ExtBuilder::<Runtime>::default()
@@ -845,7 +798,7 @@ mod asset_hub_rococo_tests {
845798
1000000000000,
846799
|| {
847800
// setup pool for paying fees to touch `SwapFirstAssetTrader`
848-
setup_pool_for_paying_fees_with_foreign_assets(foreign_asset_create_params);
801+
asset_test_utils::test_cases::setup_pool_for_paying_fees_with_foreign_assets::<Runtime, RuntimeOrigin>(ExistentialDeposit::get(), foreign_asset_create_params);
849802
// staking pot account for collecting local native fees from `BuyExecution`
850803
let _ = Balances::force_set_balance(RuntimeOrigin::root(), StakingPot::get().into(), ExistentialDeposit::get());
851804
// prepare bridge configuration
@@ -920,7 +873,7 @@ mod asset_hub_rococo_tests {
920873
foreign_asset_create_params.clone(),
921874
1000000000000,
922875
|| {
923-
setup_pool_for_paying_fees_with_foreign_assets(foreign_asset_create_params);
876+
asset_test_utils::test_cases::setup_pool_for_paying_fees_with_foreign_assets::<Runtime, RuntimeOrigin>(ExistentialDeposit::get(), foreign_asset_create_params);
924877
bridging_to_asset_hub_westend()
925878
},
926879
(
@@ -1365,11 +1318,22 @@ fn xcm_payment_api_works() {
13651318
RuntimeCall,
13661319
RuntimeOrigin,
13671320
Block,
1321+
WeightToFee,
13681322
>();
13691323
asset_test_utils::test_cases::xcm_payment_api_with_pools_works::<
13701324
Runtime,
13711325
RuntimeCall,
13721326
RuntimeOrigin,
13731327
Block,
1328+
WeightToFee,
13741329
>();
1330+
1331+
asset_test_utils::test_cases::xcm_payment_api_foreign_asset_pool_works::<
1332+
Runtime,
1333+
RuntimeCall,
1334+
RuntimeOrigin,
1335+
LocationToAccountId,
1336+
Block,
1337+
WeightToFee,
1338+
>(ExistentialDeposit::get(), WESTEND_GENESIS_HASH);
13751339
}

cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs

Lines changed: 6 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ use frame_support::{
5353
AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU32, ConstU64, ConstU8,
5454
ConstantStoragePrice, Equals, InstanceFilter, Nothing, TransformOrigin,
5555
},
56-
weights::{ConstantMultiplier, Weight, WeightToFee as _},
56+
weights::{ConstantMultiplier, Weight},
5757
BoundedVec, PalletId,
5858
};
5959
use frame_system::{
@@ -1694,33 +1694,11 @@ impl_runtime_apis! {
16941694
}
16951695

16961696
fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result<u128, XcmPaymentApiError> {
1697-
let native_asset = xcm_config::WestendLocation::get();
1698-
let fee_in_native = WeightToFee::weight_to_fee(&weight);
1699-
let latest_asset_id: Result<AssetId, ()> = asset.clone().try_into();
1700-
match latest_asset_id {
1701-
Ok(asset_id) if asset_id.0 == native_asset => {
1702-
// for native asset
1703-
Ok(fee_in_native)
1704-
},
1705-
Ok(asset_id) => {
1706-
// Try to get current price of `asset_id` in `native_asset`.
1707-
if let Ok(Some(swapped_in_native)) = assets_common::PoolAdapter::<Runtime>::quote_price_tokens_for_exact_tokens(
1708-
asset_id.0.clone(),
1709-
native_asset,
1710-
fee_in_native,
1711-
true, // We include the fee.
1712-
) {
1713-
Ok(swapped_in_native)
1714-
} else {
1715-
log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!");
1716-
Err(XcmPaymentApiError::AssetNotFound)
1717-
}
1718-
},
1719-
Err(_) => {
1720-
log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - failed to convert asset: {asset:?}!");
1721-
Err(XcmPaymentApiError::VersionedConversionFailed)
1722-
}
1723-
}
1697+
use crate::xcm_config::XcmConfig;
1698+
1699+
type Trader = <XcmConfig as xcm_executor::Config>::Trader;
1700+
1701+
PolkadotXcm::query_weight_to_asset_fee::<Trader>(weight, asset)
17241702
}
17251703

17261704
fn query_xcm_weight(message: VersionedXcm<()>) -> Result<Weight, XcmPaymentApiError> {

cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs

Lines changed: 14 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ use parachains_common::{AccountId, AssetIdForTrustBackedAssets, AuraId, Balance}
5050
use sp_consensus_aura::SlotDuration;
5151
use sp_core::crypto::Ss58Codec;
5252
use sp_runtime::{traits::MaybeEquivalence, Either};
53-
use std::{convert::Into, ops::Mul};
53+
use std::convert::Into;
5454
use testnet_parachains_constants::westend::{consensus::*, currency::UNITS, fee::WeightToFee};
5555
use xcm::latest::{
5656
prelude::{Assets as XcmAssets, *},
@@ -91,52 +91,6 @@ fn slot_durations() -> SlotDurations {
9191
}
9292
}
9393

94-
fn setup_pool_for_paying_fees_with_foreign_assets(
95-
(foreign_asset_owner, foreign_asset_id_location, foreign_asset_id_minimum_balance): (
96-
AccountId,
97-
xcm::v5::Location,
98-
Balance,
99-
),
100-
) {
101-
let existential_deposit = ExistentialDeposit::get();
102-
103-
// setup a pool to pay fees with `foreign_asset_id_location` tokens
104-
let pool_owner: AccountId = [14u8; 32].into();
105-
let native_asset = xcm::v5::Location::parent();
106-
let pool_liquidity: Balance =
107-
existential_deposit.max(foreign_asset_id_minimum_balance).mul(100_000);
108-
109-
let _ = Balances::force_set_balance(
110-
RuntimeOrigin::root(),
111-
pool_owner.clone().into(),
112-
(existential_deposit + pool_liquidity).mul(2).into(),
113-
);
114-
115-
assert_ok!(ForeignAssets::mint(
116-
RuntimeOrigin::signed(foreign_asset_owner),
117-
foreign_asset_id_location.clone().into(),
118-
pool_owner.clone().into(),
119-
(foreign_asset_id_minimum_balance + pool_liquidity).mul(2).into(),
120-
));
121-
122-
assert_ok!(AssetConversion::create_pool(
123-
RuntimeOrigin::signed(pool_owner.clone()),
124-
Box::new(native_asset.clone().into()),
125-
Box::new(foreign_asset_id_location.clone().into())
126-
));
127-
128-
assert_ok!(AssetConversion::add_liquidity(
129-
RuntimeOrigin::signed(pool_owner.clone()),
130-
Box::new(native_asset.into()),
131-
Box::new(foreign_asset_id_location.into()),
132-
pool_liquidity,
133-
pool_liquidity,
134-
1,
135-
1,
136-
pool_owner,
137-
));
138-
}
139-
14094
#[test]
14195
fn test_buy_and_refund_weight_in_native() {
14296
ExtBuilder::<Runtime>::default()
@@ -944,7 +898,7 @@ fn receive_reserve_asset_deposited_roc_from_asset_hub_rococo_fees_paid_by_pool_s
944898
1000000000000,
945899
|| {
946900
// setup pool for paying fees to touch `SwapFirstAssetTrader`
947-
setup_pool_for_paying_fees_with_foreign_assets(foreign_asset_create_params);
901+
asset_test_utils::test_cases::setup_pool_for_paying_fees_with_foreign_assets::<Runtime, RuntimeOrigin>(ExistentialDeposit::get(), foreign_asset_create_params);
948902
// staking pot account for collecting local native fees from `BuyExecution`
949903
let _ = Balances::force_set_balance(RuntimeOrigin::root(), StakingPot::get().into(), ExistentialDeposit::get());
950904
// prepare bridge configuration
@@ -1014,7 +968,7 @@ fn receive_reserve_asset_deposited_roc_from_asset_hub_rococo_fees_paid_by_suffic
1014968
foreign_asset_create_params.clone(),
1015969
1000000000000,
1016970
|| {
1017-
setup_pool_for_paying_fees_with_foreign_assets(foreign_asset_create_params);
971+
asset_test_utils::test_cases::setup_pool_for_paying_fees_with_foreign_assets::<Runtime, RuntimeOrigin>(ExistentialDeposit::get(), foreign_asset_create_params);
1018972
bridging_to_asset_hub_rococo()
1019973
},
1020974
(
@@ -1423,13 +1377,24 @@ fn xcm_payment_api_works() {
14231377
RuntimeCall,
14241378
RuntimeOrigin,
14251379
Block,
1380+
WeightToFee,
14261381
>();
14271382
asset_test_utils::test_cases::xcm_payment_api_with_pools_works::<
14281383
Runtime,
14291384
RuntimeCall,
14301385
RuntimeOrigin,
14311386
Block,
1387+
WeightToFee,
14321388
>();
1389+
1390+
asset_test_utils::test_cases::xcm_payment_api_foreign_asset_pool_works::<
1391+
Runtime,
1392+
RuntimeCall,
1393+
RuntimeOrigin,
1394+
LocationToAccountId,
1395+
Block,
1396+
WeightToFee,
1397+
>(ExistentialDeposit::get(), ROCOCO_GENESIS_HASH);
14331398
}
14341399

14351400
#[test]

0 commit comments

Comments
 (0)