Skip to content
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

Asset hubs - Add all assets in pool with native to XcmPaymentApi #523

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,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 @@ -2692,7 +2692,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 @@ -2830,7 +2830,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(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function has a pretty suboptimal signature. Way too easy to mix up arguments (not introduced here, just noticed that it makes it unnecessary hard to review correctness).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: Not even function docs nor naming of parameters helps here in any way. What is asset1? What is asset2? I need to read the code of the trait implementation to figure this out.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In what asset is the provided amount? Based on the implementation of the called get_amount_in, I am inferring that the amount is the amount we want to receive in the native token and that the first parameter is indeed the asset_in, but that seems to contradict the other code path. I am sure I am just holding it wrong, but the point is, I should not need to read a particular implementation of a trait function to check that the parameters have been passed correctly (because I see that they are too generic for the type checker to be able to catch that).

Note: This is by no means unique to this code, we have plenty of similar examples in the parachains/polkadot code base. I am just using this opportunity to raise awareness in a broader scope: If we improved here, our code quality and also our speed of reviews could be greatly enhanced as everything starts to become locally reasonable: I should not need to study implementation details of code that is not part of that PR to be able to infer that it is correct.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah the naming of the function is really not that great. IMO something like simulate_swap would have been better instead of the clunky name.

In what asset is the provided amount? Based on the implementation of the called get_amount_in, I am inferring that the amount is the amount we want to receive in the native token and that the first parameter is indeed the asset_in, but that seems to contradict the other code path.

We are passing in the amount in the native asset (weight to fee returns the native asset) and then we want to get the price in the target asset (asset_in). Given that it looks correct, but yeah hard to follow.

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 @@ -87,6 +87,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 @@ -980,7 +980,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
Loading