Skip to content

Commit

Permalink
Asset hubs - Add all assets in pool with native to XcmPaymentApi (#523)
Browse files Browse the repository at this point in the history
The `XcmPaymentApi` has two functions, `query_acceptable_payment_assets`
and `query_weight_to_asset_fee`, which allow clients to know which
assets can be used to pay for fees.

On the asset hubs fees can be paid both with the native asset (DOT, KSM)
and with any asset in a liquidity pool with the native asset. However,
these functions were only returning the native asset.

This PR adds all other assets that can be used for fee payment to this
API.

Also fixed an issue where these APIs wouldn't accept any version other
than latest.

---------

Co-authored-by: Bastian Köcher <git@kchr.de>
  • Loading branch information
franciscoaguirre and bkchr authored Jan 24, 2025
1 parent 3c520e3 commit 359eec0
Show file tree
Hide file tree
Showing 28 changed files with 230 additions and 49 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Added

- Location conversion tests for relays and parachains ([polkadot-fellows/runtimes#487](https://github.com/polkadot-fellows/runtimes/pull/487))
- Asset Hubs: XcmPaymentApi now returns all assets in a pool with the native token as acceptable as fee payment ([polkadot-fellows/runtimes#523](https://github.com/polkadot-fellows/runtimes/pull/523))

- ParaRegistration proxy for Polkadot and Kusama ([polkadot-fellows/runtimes#520](https://github.com/polkadot-fellows/runtimes/pull/520))
- Encointer: Swap community currency for KSM from community treasuries subject to democratic decision on allowance ([polkadot-fellows/runtimes#541](https://github.com/polkadot-fellows/runtimes/pull/541))
Expand Down
7 changes: 5 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ asset-hub-kusama-runtime = { path = "system-parachains/asset-hubs/asset-hub-kusa
asset-hub-polkadot-emulated-chain = { path = "integration-tests/emulated/chains/parachains/assets/asset-hub-polkadot" }
asset-hub-polkadot-runtime = { path = "system-parachains/asset-hubs/asset-hub-polkadot" }
asset-test-utils = { version = "20.0.0" }
assets-common = { version = "0.18.0", default-features = false }
assets-common = { version = "0.18.3", default-features = false }
authority-discovery-primitives = { version = "34.0.0", default-features = false, package = "sp-authority-discovery" }
babe-primitives = { version = "0.40.0", default-features = false, package = "sp-consensus-babe" }
beefy-primitives = { version = "22.1.0", default-features = false, package = "sp-consensus-beefy" }
Expand Down
3 changes: 2 additions & 1 deletion relay/kusama/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2736,7 +2736,8 @@ sp_api::impl_runtime_apis! {
}

fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result<u128, XcmPaymentApiError> {
match asset.try_as::<AssetId>() {
let latest_asset_id: Result<AssetId, ()> = asset.clone().try_into();
match latest_asset_id {
Ok(asset_id) if asset_id.0 == xcm_config::TokenLocation::get() => {
// for native token
Ok(WeightToFee::weight_to_fee(&weight))
Expand Down
3 changes: 2 additions & 1 deletion relay/polkadot/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2709,7 +2709,8 @@ sp_api::impl_runtime_apis! {
}

fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result<u128, XcmPaymentApiError> {
match asset.try_as::<AssetId>() {
let latest_asset_id: Result<AssetId, ()> = asset.clone().try_into();
match latest_asset_id {
Ok(asset_id) if asset_id.0 == xcm_config::TokenLocation::get() => {
// for native token
Ok(WeightToFee::weight_to_fee(&weight))
Expand Down
34 changes: 27 additions & 7 deletions system-parachains/asset-hubs/asset-hub-kusama/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1265,19 +1265,39 @@ impl_runtime_apis! {

impl xcm_runtime_apis::fees::XcmPaymentApi<Block> for Runtime {
fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result<Vec<VersionedAssetId>, XcmPaymentApiError> {
let acceptable_assets = vec![AssetId(xcm_config::KsmLocation::get())];
let native_asset = xcm_config::KsmLocation::get();
// We accept the native asset to pay fees.
let mut acceptable_assets = vec![AssetId(native_asset.clone())];
// We also accept all assets in a pool with the native token.
acceptable_assets.extend(
assets_common::PoolAdapter::<Runtime>::get_assets_in_pool_with(native_asset)
.map_err(|()| XcmPaymentApiError::VersionedConversionFailed)?
);
PolkadotXcm::query_acceptable_payment_assets(xcm_version, acceptable_assets)
}

fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result<u128, XcmPaymentApiError> {
match asset.try_as::<AssetId>() {
Ok(asset_id) if asset_id.0 == xcm_config::KsmLocation::get() => {
// for native token
Ok(WeightToFee::weight_to_fee(&weight))
let native_asset = xcm_config::KsmLocation::get();
let fee_in_native = WeightToFee::weight_to_fee(&weight);
let latest_asset_id: Result<AssetId, ()> = asset.clone().try_into();
match latest_asset_id {
Ok(asset_id) if asset_id.0 == native_asset => {
// for native asset
Ok(fee_in_native)
},
Ok(asset_id) => {
log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!");
Err(XcmPaymentApiError::AssetNotFound)
// Try to get current price of `asset_id` in `native_asset`.
if let Ok(Some(swapped_in_native)) = assets_common::PoolAdapter::<Runtime>::quote_price_tokens_for_exact_tokens(
asset_id.0.clone(),
native_asset,
fee_in_native,
true, // We include the fee.
) {
Ok(swapped_in_native)
} else {
log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!");
Err(XcmPaymentApiError::AssetNotFound)
}
},
Err(_) => {
log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - failed to convert asset: {asset:?}!");
Expand Down
29 changes: 23 additions & 6 deletions system-parachains/asset-hubs/asset-hub-kusama/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,19 @@ use asset_hub_kusama_runtime::{
RelayTreasuryLocation, RelayTreasuryPalletAccount, StakingPot,
TrustBackedAssetsPalletLocation, XcmConfig,
},
AllPalletsWithoutSystem, AssetConversion, AssetDeposit, Assets, Balances, ExistentialDeposit,
ForeignAssets, ForeignAssetsInstance, MetadataDepositBase, MetadataDepositPerByte,
ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, SessionKeys,
ToPolkadotXcmRouterInstance, TrustBackedAssetsInstance, XcmpQueue, SLOT_DURATION,
AllPalletsWithoutSystem, AssetConversion, AssetDeposit, Assets, Balances, Block,
ExistentialDeposit, ForeignAssets, ForeignAssetsInstance, MetadataDepositBase,
MetadataDepositPerByte, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent,
RuntimeOrigin, SessionKeys, ToPolkadotXcmRouterInstance, TrustBackedAssetsInstance, XcmpQueue,
SLOT_DURATION,
};
use asset_test_utils::{
test_cases_over_bridge::TestBridgingConfig, CollatorSessionKey, CollatorSessionKeys, ExtBuilder,
test_cases_over_bridge::TestBridgingConfig, CollatorSessionKey, CollatorSessionKeys,
ExtBuilder, SlotDurations,
};
use codec::{Decode, Encode};
use frame_support::{assert_ok, traits::fungibles::InspectEnumerable};
use parachains_common::{AccountId, AssetIdForTrustBackedAssets, AuraId, Balance};
use parachains_runtimes_test_utils::SlotDurations;
use sp_consensus_aura::SlotDuration;
use sp_core::crypto::Ss58Codec;
use sp_runtime::traits::MaybeEquivalence;
Expand Down Expand Up @@ -1403,3 +1404,19 @@ fn location_conversion_works() {
assert_eq!(got, expected, "{}", tc.description);
}
}

#[test]
fn xcm_payment_api_works() {
parachains_runtimes_test_utils::test_cases::xcm_payment_api_with_native_token_works::<
Runtime,
RuntimeCall,
RuntimeOrigin,
Block,
>();
asset_test_utils::test_cases::xcm_payment_api_with_pools_works::<
Runtime,
RuntimeCall,
RuntimeOrigin,
Block,
>();
}
34 changes: 27 additions & 7 deletions system-parachains/asset-hubs/asset-hub-polkadot/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1231,19 +1231,39 @@ impl_runtime_apis! {

impl xcm_runtime_apis::fees::XcmPaymentApi<Block> for Runtime {
fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result<Vec<VersionedAssetId>, XcmPaymentApiError> {
let acceptable_assets = vec![AssetId(xcm_config::DotLocation::get())];
let native_asset = xcm_config::DotLocation::get();
// We accept the native asset to pay fees.
let mut acceptable_assets = vec![AssetId(native_asset.clone())];
// We also accept all assets in a pool with the native token.
acceptable_assets.extend(
assets_common::PoolAdapter::<Runtime>::get_assets_in_pool_with(native_asset)
.map_err(|()| XcmPaymentApiError::VersionedConversionFailed)?
);
PolkadotXcm::query_acceptable_payment_assets(xcm_version, acceptable_assets)
}

fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result<u128, XcmPaymentApiError> {
match asset.try_as::<AssetId>() {
Ok(asset_id) if asset_id.0 == xcm_config::DotLocation::get() => {
// for native token
Ok(WeightToFee::weight_to_fee(&weight))
let native_asset = xcm_config::DotLocation::get();
let fee_in_native = WeightToFee::weight_to_fee(&weight);
let latest_asset_id: Result<AssetId, ()> = asset.clone().try_into();
match latest_asset_id {
Ok(asset_id) if asset_id.0 == native_asset => {
// for native asset
Ok(fee_in_native)
},
Ok(asset_id) => {
log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!");
Err(XcmPaymentApiError::AssetNotFound)
// Try to get current price of `asset_id` in `native_asset`.
if let Ok(Some(swapped_in_native)) = assets_common::PoolAdapter::<Runtime>::quote_price_tokens_for_exact_tokens(
asset_id.0.clone(),
native_asset,
fee_in_native,
true, // We include the fee.
) {
Ok(swapped_in_native)
} else {
log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!");
Err(XcmPaymentApiError::AssetNotFound)
}
},
Err(_) => {
log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - failed to convert asset: {asset:?}!");
Expand Down
29 changes: 23 additions & 6 deletions system-parachains/asset-hubs/asset-hub-polkadot/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,21 @@ use asset_hub_polkadot_runtime::{
RelayTreasuryLocation, RelayTreasuryPalletAccount, StakingPot,
TrustBackedAssetsPalletLocation, XcmConfig,
},
AllPalletsWithoutSystem, AssetConversion, AssetDeposit, Assets, Balances, ExistentialDeposit,
ForeignAssets, ForeignAssetsInstance, MetadataDepositBase, MetadataDepositPerByte,
ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, SessionKeys,
ToKusamaXcmRouterInstance, TrustBackedAssetsInstance, XcmpQueue, SLOT_DURATION,
AllPalletsWithoutSystem, AssetConversion, AssetDeposit, Assets, Balances, Block,
ExistentialDeposit, ForeignAssets, ForeignAssetsInstance, MetadataDepositBase,
MetadataDepositPerByte, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent,
RuntimeOrigin, SessionKeys, ToKusamaXcmRouterInstance, TrustBackedAssetsInstance, XcmpQueue,
SLOT_DURATION,
};
use asset_test_utils::{
test_cases_over_bridge::TestBridgingConfig, CollatorSessionKey, CollatorSessionKeys, ExtBuilder,
test_cases_over_bridge::TestBridgingConfig, CollatorSessionKey, CollatorSessionKeys,
ExtBuilder, SlotDurations,
};
use codec::{Decode, Encode};
use frame_support::{assert_ok, traits::fungibles::InspectEnumerable};
use parachains_common::{
AccountId, AssetHubPolkadotAuraId as AuraId, AssetIdForTrustBackedAssets, Balance,
};
use parachains_runtimes_test_utils::SlotDurations;
use sp_consensus_aura::SlotDuration;
use sp_core::crypto::Ss58Codec;
use sp_runtime::traits::MaybeEquivalence;
Expand Down Expand Up @@ -1424,3 +1425,19 @@ fn location_conversion_works() {
assert_eq!(got, expected, "{}", tc.description);
}
}

#[test]
fn xcm_payment_api_works() {
parachains_runtimes_test_utils::test_cases::xcm_payment_api_with_native_token_works::<
Runtime,
RuntimeCall,
RuntimeOrigin,
Block,
>();
asset_test_utils::test_cases::xcm_payment_api_with_pools_works::<
Runtime,
RuntimeCall,
RuntimeOrigin,
Block,
>();
}
3 changes: 2 additions & 1 deletion system-parachains/bridge-hubs/bridge-hub-kusama/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -771,7 +771,8 @@ impl_runtime_apis! {
}

fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result<u128, XcmPaymentApiError> {
match asset.try_as::<AssetId>() {
let latest_asset_id: Result<AssetId, ()> = asset.clone().try_into();
match latest_asset_id {
Ok(asset_id) if asset_id.0 == xcm_config::KsmRelayLocation::get() => {
// for native token
Ok(WeightToFee::weight_to_fee(&weight))
Expand Down
16 changes: 13 additions & 3 deletions system-parachains/bridge-hubs/bridge-hub-kusama/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ use bridge_hub_kusama_runtime::{
KsmRelayLocation, LocationToAccountId, RelayNetwork, RelayTreasuryLocation,
RelayTreasuryPalletAccount, XcmConfig,
},
AllPalletsWithoutSystem, BridgeRejectObsoleteHeadersAndMessages, Executive, ExistentialDeposit,
ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, SessionKeys,
SignedExtra, TransactionPayment, UncheckedExtrinsic, SLOT_DURATION,
AllPalletsWithoutSystem, Block, BridgeRejectObsoleteHeadersAndMessages, Executive,
ExistentialDeposit, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent,
RuntimeOrigin, SessionKeys, SignedExtra, TransactionPayment, UncheckedExtrinsic, SLOT_DURATION,
};
use bridge_hub_test_utils::{test_cases::from_parachain, SlotDurations};
use codec::{Decode, Encode};
Expand Down Expand Up @@ -537,3 +537,13 @@ fn location_conversion_works() {
assert_eq!(got, expected, "{}", tc.description);
}
}

#[test]
fn xcm_payment_api_works() {
parachains_runtimes_test_utils::test_cases::xcm_payment_api_with_native_token_works::<
Runtime,
RuntimeCall,
RuntimeOrigin,
Block,
>();
}
3 changes: 2 additions & 1 deletion system-parachains/bridge-hubs/bridge-hub-polkadot/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -791,7 +791,8 @@ impl_runtime_apis! {
}

fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result<u128, XcmPaymentApiError> {
match asset.try_as::<AssetId>() {
let latest_asset_id: Result<AssetId, ()> = asset.clone().try_into();
match latest_asset_id {
Ok(asset_id) if asset_id.0 == xcm_config::DotRelayLocation::get() => {
// for native token
Ok(WeightToFee::weight_to_fee(&weight))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ parameter_types! {
type RuntimeHelper<Runtime, AllPalletsWithoutSystem = ()> =
parachains_runtimes_test_utils::RuntimeHelper<Runtime, AllPalletsWithoutSystem>;

fn collator_session_keys() -> bridge_hub_test_utils::CollatorSessionKeys<Runtime> {
bridge_hub_test_utils::CollatorSessionKeys::new(
fn collator_session_keys() -> CollatorSessionKeys<Runtime> {
CollatorSessionKeys::new(
AccountId::from(Alice),
AccountId::from(Alice),
SessionKeys { aura: AuraId::from(Alice.public()) },
Expand Down
16 changes: 13 additions & 3 deletions system-parachains/bridge-hubs/bridge-hub-polkadot/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ use bridge_hub_polkadot_runtime::{
DotRelayLocation, LocationToAccountId, RelayNetwork, RelayTreasuryLocation,
RelayTreasuryPalletAccount, XcmConfig,
},
AllPalletsWithoutSystem, BridgeRejectObsoleteHeadersAndMessages, Executive, ExistentialDeposit,
ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, SessionKeys,
SignedExtra, TransactionPayment, UncheckedExtrinsic, SLOT_DURATION,
AllPalletsWithoutSystem, Block, BridgeRejectObsoleteHeadersAndMessages, Executive,
ExistentialDeposit, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent,
RuntimeOrigin, SessionKeys, SignedExtra, TransactionPayment, UncheckedExtrinsic, SLOT_DURATION,
};
use bridge_hub_test_utils::{test_cases::from_parachain, SlotDurations};
use codec::{Decode, Encode};
Expand Down Expand Up @@ -538,3 +538,13 @@ fn location_conversion_works() {
assert_eq!(got, expected, "{}", tc.description);
}
}

#[test]
fn xcm_payment_api_works() {
parachains_runtimes_test_utils::test_cases::xcm_payment_api_with_native_token_works::<
Runtime,
RuntimeCall,
RuntimeOrigin,
Block,
>();
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ system-parachains-constants = { workspace = true }

[dev-dependencies]
collectives-polkadot-runtime-constants = { workspace = true }
parachains-runtimes-test-utils = { workspace = true }
sp-io = { workspace = true }

[build-dependencies]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -983,7 +983,8 @@ impl_runtime_apis! {
}

fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result<u128, XcmPaymentApiError> {
match asset.try_as::<AssetId>() {
let latest_asset_id: Result<AssetId, ()> = asset.clone().try_into();
match latest_asset_id {
Ok(asset_id) if asset_id.0 == xcm_config::DotLocation::get() => {
// for native token
Ok(WeightToFee::weight_to_fee(&weight))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use collectives_polkadot_runtime::{Block, Runtime, RuntimeCall, RuntimeOrigin};

#[test]
fn xcm_payment_api_works() {
parachains_runtimes_test_utils::test_cases::xcm_payment_api_with_native_token_works::<
Runtime,
RuntimeCall,
RuntimeOrigin,
Block,
>();
}
Loading

0 comments on commit 359eec0

Please sign in to comment.