mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-31 07:31:02 +00:00
XCM Fee Payment Runtime API (#3607)
The PR provides API for obtaining: - the weight required to execute an XCM message, - a list of acceptable `AssetId`s for message execution payment, - the cost of the weight in the specified acceptable `AssetId`. It is meant to address an issue where one has to guess how much fee to pay for execution. Also, at the moment, a client has to guess which assets are acceptable for fee execution payment. See the related issue https://github.com/paritytech/polkadot-sdk/issues/690. With this API, a client is supposed to query the list of the supported asset IDs (in the XCM version format the client understands), weigh the XCM program the client wants to execute and convert the weight into one of the acceptable assets. Note that the client is supposed to know what program will be executed on what chains. However, having a small companion JS library for the pallet-xcm and xtokens should be enough to determine what XCM programs will be executed and where (since these pallets compose a known small set of programs). ```Rust pub trait XcmPaymentApi<Call> where Call: Codec, { /// Returns a list of acceptable payment assets. /// /// # Arguments /// /// * `xcm_version`: Version. fn query_acceptable_payment_assets(xcm_version: Version) -> Result<Vec<VersionedAssetId>, Error>; /// Returns a weight needed to execute a XCM. /// /// # Arguments /// /// * `message`: `VersionedXcm`. fn query_xcm_weight(message: VersionedXcm<Call>) -> Result<Weight, Error>; /// Converts a weight into a fee for the specified `AssetId`. /// /// # Arguments /// /// * `weight`: convertible `Weight`. /// * `asset`: `VersionedAssetId`. fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result<u128, Error>; /// Get delivery fees for sending a specific `message` to a `destination`. /// These always come in a specific asset, defined by the chain. /// /// # Arguments /// * `message`: The message that'll be sent, necessary because most delivery fees are based on the /// size of the message. /// * `destination`: The destination to send the message to. Different destinations may use /// different senders that charge different fees. fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result<VersionedAssets, Error>; } ``` An [example](https://gist.github.com/PraetorP/4bc323ff85401abe253897ba990ec29d) of a client side code. --------- Co-authored-by: Francisco Aguirre <franciscoaguirreperez@gmail.com> Co-authored-by: Adrian Catangiu <adrian@parity.io> Co-authored-by: Daniel Shiposha <mrshiposha@gmail.com>
This commit is contained in:
Generated
+19
@@ -11188,6 +11188,7 @@ dependencies = [
|
|||||||
"staging-xcm",
|
"staging-xcm",
|
||||||
"staging-xcm-builder",
|
"staging-xcm-builder",
|
||||||
"staging-xcm-executor",
|
"staging-xcm-executor",
|
||||||
|
"xcm-fee-payment-runtime-api",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -13540,12 +13541,14 @@ dependencies = [
|
|||||||
"sp-transaction-pool",
|
"sp-transaction-pool",
|
||||||
"sp-version",
|
"sp-version",
|
||||||
"sp-weights",
|
"sp-weights",
|
||||||
|
"staging-xcm",
|
||||||
"substrate-prometheus-endpoint",
|
"substrate-prometheus-endpoint",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tracing-gum",
|
"tracing-gum",
|
||||||
"westend-runtime",
|
"westend-runtime",
|
||||||
"westend-runtime-constants",
|
"westend-runtime-constants",
|
||||||
|
"xcm-fee-payment-runtime-api",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -15116,6 +15119,7 @@ dependencies = [
|
|||||||
"substrate-wasm-builder",
|
"substrate-wasm-builder",
|
||||||
"tiny-keccak",
|
"tiny-keccak",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"xcm-fee-payment-runtime-api",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -21970,6 +21974,7 @@ dependencies = [
|
|||||||
"tiny-keccak",
|
"tiny-keccak",
|
||||||
"tokio",
|
"tokio",
|
||||||
"westend-runtime-constants",
|
"westend-runtime-constants",
|
||||||
|
"xcm-fee-payment-runtime-api",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -22443,6 +22448,20 @@ dependencies = [
|
|||||||
"staging-xcm-executor",
|
"staging-xcm-executor",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xcm-fee-payment-runtime-api"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"frame-support",
|
||||||
|
"parity-scale-codec",
|
||||||
|
"scale-info",
|
||||||
|
"sp-api",
|
||||||
|
"sp-runtime",
|
||||||
|
"sp-std 14.0.0",
|
||||||
|
"sp-weights",
|
||||||
|
"staging-xcm",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xcm-procedural"
|
name = "xcm-procedural"
|
||||||
version = "7.0.0"
|
version = "7.0.0"
|
||||||
|
|||||||
@@ -214,6 +214,7 @@ members = [
|
|||||||
"polkadot/xcm/xcm-builder",
|
"polkadot/xcm/xcm-builder",
|
||||||
"polkadot/xcm/xcm-executor",
|
"polkadot/xcm/xcm-executor",
|
||||||
"polkadot/xcm/xcm-executor/integration-tests",
|
"polkadot/xcm/xcm-executor/integration-tests",
|
||||||
|
"polkadot/xcm/xcm-fee-payment-runtime-api",
|
||||||
"polkadot/xcm/xcm-simulator",
|
"polkadot/xcm/xcm-simulator",
|
||||||
"polkadot/xcm/xcm-simulator/example",
|
"polkadot/xcm/xcm-simulator/example",
|
||||||
"polkadot/xcm/xcm-simulator/fuzzer",
|
"polkadot/xcm/xcm-simulator/fuzzer",
|
||||||
|
|||||||
@@ -142,6 +142,9 @@ polkadot-node-core-pvf-checker = { path = "../core/pvf-checker", optional = true
|
|||||||
polkadot-node-core-runtime-api = { path = "../core/runtime-api", optional = true }
|
polkadot-node-core-runtime-api = { path = "../core/runtime-api", optional = true }
|
||||||
polkadot-statement-distribution = { path = "../network/statement-distribution", optional = true }
|
polkadot-statement-distribution = { path = "../network/statement-distribution", optional = true }
|
||||||
|
|
||||||
|
xcm = { package = "staging-xcm", path = "../../xcm" }
|
||||||
|
xcm-fee-payment-runtime-api = { path = "../../xcm/xcm-fee-payment-runtime-api" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
polkadot-test-client = { path = "../test/client" }
|
polkadot-test-client = { path = "../test/client" }
|
||||||
polkadot-node-subsystem-test-helpers = { path = "../subsystem-test-helpers" }
|
polkadot-node-subsystem-test-helpers = { path = "../subsystem-test-helpers" }
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ use polkadot_primitives::{
|
|||||||
ScrapedOnChainVotes, SessionIndex, SessionInfo, ValidationCode, ValidationCodeHash,
|
ScrapedOnChainVotes, SessionIndex, SessionInfo, ValidationCode, ValidationCodeHash,
|
||||||
ValidatorId, ValidatorIndex, ValidatorSignature,
|
ValidatorId, ValidatorIndex, ValidatorSignature,
|
||||||
};
|
};
|
||||||
|
|
||||||
use sp_core::OpaqueMetadata;
|
use sp_core::OpaqueMetadata;
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
traits::Block as BlockT,
|
traits::Block as BlockT,
|
||||||
@@ -39,7 +40,7 @@ use sp_runtime::{
|
|||||||
use sp_version::RuntimeVersion;
|
use sp_version::RuntimeVersion;
|
||||||
use sp_weights::Weight;
|
use sp_weights::Weight;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
use xcm::{VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm};
|
||||||
sp_api::decl_runtime_apis! {
|
sp_api::decl_runtime_apis! {
|
||||||
/// This runtime API is only implemented for the test runtime!
|
/// This runtime API is only implemented for the test runtime!
|
||||||
pub trait GetLastTimestamp {
|
pub trait GetLastTimestamp {
|
||||||
@@ -396,4 +397,22 @@ sp_api::impl_runtime_apis! {
|
|||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl xcm_fee_payment_runtime_api::XcmPaymentApi<Block> for Runtime {
|
||||||
|
fn query_acceptable_payment_assets(_: xcm::Version) -> Result<Vec<VersionedAssetId>, xcm_fee_payment_runtime_api::Error> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn query_weight_to_asset_fee(_: Weight, _: VersionedAssetId) -> Result<u128, xcm_fee_payment_runtime_api::Error> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn query_xcm_weight(_: VersionedXcm<()>) -> Result<Weight, xcm_fee_payment_runtime_api::Error> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn query_delivery_fees(_: VersionedLocation, _: VersionedXcm<()>) -> Result<VersionedAssets, xcm_fee_payment_runtime_api::Error> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ polkadot-parachain-primitives = { path = "../../parachain", default-features = f
|
|||||||
xcm = { package = "staging-xcm", path = "../../xcm", default-features = false }
|
xcm = { package = "staging-xcm", path = "../../xcm", default-features = false }
|
||||||
xcm-executor = { package = "staging-xcm-executor", path = "../../xcm/xcm-executor", default-features = false }
|
xcm-executor = { package = "staging-xcm-executor", path = "../../xcm/xcm-executor", default-features = false }
|
||||||
xcm-builder = { package = "staging-xcm-builder", path = "../../xcm/xcm-builder", default-features = false }
|
xcm-builder = { package = "staging-xcm-builder", path = "../../xcm/xcm-builder", default-features = false }
|
||||||
|
xcm-fee-payment-runtime-api = { path = "../../xcm/xcm-fee-payment-runtime-api", default-features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tiny-keccak = { version = "2.0.2", features = ["keccak"] }
|
tiny-keccak = { version = "2.0.2", features = ["keccak"] }
|
||||||
@@ -208,6 +209,7 @@ std = [
|
|||||||
"tx-pool-api/std",
|
"tx-pool-api/std",
|
||||||
"xcm-builder/std",
|
"xcm-builder/std",
|
||||||
"xcm-executor/std",
|
"xcm-executor/std",
|
||||||
|
"xcm-fee-payment-runtime-api/std",
|
||||||
"xcm/std",
|
"xcm/std",
|
||||||
]
|
]
|
||||||
runtime-benchmarks = [
|
runtime-benchmarks = [
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ use frame_support::{
|
|||||||
InstanceFilter, KeyOwnerProofSystem, LinearStoragePrice, PrivilegeCmp, ProcessMessage,
|
InstanceFilter, KeyOwnerProofSystem, LinearStoragePrice, PrivilegeCmp, ProcessMessage,
|
||||||
ProcessMessageError, StorageMapShim, WithdrawReasons,
|
ProcessMessageError, StorageMapShim, WithdrawReasons,
|
||||||
},
|
},
|
||||||
weights::{ConstantMultiplier, WeightMeter},
|
weights::{ConstantMultiplier, WeightMeter, WeightToFee as _},
|
||||||
PalletId,
|
PalletId,
|
||||||
};
|
};
|
||||||
use frame_system::{EnsureRoot, EnsureSigned};
|
use frame_system::{EnsureRoot, EnsureSigned};
|
||||||
@@ -98,7 +98,10 @@ use sp_staking::SessionIndex;
|
|||||||
#[cfg(any(feature = "std", test))]
|
#[cfg(any(feature = "std", test))]
|
||||||
use sp_version::NativeVersion;
|
use sp_version::NativeVersion;
|
||||||
use sp_version::RuntimeVersion;
|
use sp_version::RuntimeVersion;
|
||||||
use xcm::{latest::prelude::*, VersionedLocation};
|
use xcm::{
|
||||||
|
latest::prelude::*, IntoVersion, VersionedAssetId, VersionedAssets, VersionedLocation,
|
||||||
|
VersionedXcm,
|
||||||
|
};
|
||||||
use xcm_builder::PayOverXcm;
|
use xcm_builder::PayOverXcm;
|
||||||
|
|
||||||
pub use frame_system::Call as SystemCall;
|
pub use frame_system::Call as SystemCall;
|
||||||
@@ -123,6 +126,7 @@ use governance::{
|
|||||||
pallet_custom_origins, AuctionAdmin, Fellows, GeneralAdmin, LeaseAdmin, Treasurer,
|
pallet_custom_origins, AuctionAdmin, Fellows, GeneralAdmin, LeaseAdmin, Treasurer,
|
||||||
TreasurySpender,
|
TreasurySpender,
|
||||||
};
|
};
|
||||||
|
use xcm_fee_payment_runtime_api::Error as XcmPaymentApiError;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
@@ -216,7 +220,7 @@ pub struct OriginPrivilegeCmp;
|
|||||||
impl PrivilegeCmp<OriginCaller> for OriginPrivilegeCmp {
|
impl PrivilegeCmp<OriginCaller> for OriginPrivilegeCmp {
|
||||||
fn cmp_privilege(left: &OriginCaller, right: &OriginCaller) -> Option<Ordering> {
|
fn cmp_privilege(left: &OriginCaller, right: &OriginCaller) -> Option<Ordering> {
|
||||||
if left == right {
|
if left == right {
|
||||||
return Some(Ordering::Equal)
|
return Some(Ordering::Equal);
|
||||||
}
|
}
|
||||||
|
|
||||||
match (left, right) {
|
match (left, right) {
|
||||||
@@ -1493,11 +1497,11 @@ pub mod migrations {
|
|||||||
let now = frame_system::Pallet::<Runtime>::block_number();
|
let now = frame_system::Pallet::<Runtime>::block_number();
|
||||||
let lease = slots::Pallet::<Runtime>::lease(para);
|
let lease = slots::Pallet::<Runtime>::lease(para);
|
||||||
if lease.is_empty() {
|
if lease.is_empty() {
|
||||||
return None
|
return None;
|
||||||
}
|
}
|
||||||
// Lease not yet started, ignore:
|
// Lease not yet started, ignore:
|
||||||
if lease.iter().any(Option::is_none) {
|
if lease.iter().any(Option::is_none) {
|
||||||
return None
|
return None;
|
||||||
}
|
}
|
||||||
let (index, _) =
|
let (index, _) =
|
||||||
<slots::Pallet<Runtime> as Leaser<BlockNumber>>::lease_period_index(now)?;
|
<slots::Pallet<Runtime> as Leaser<BlockNumber>>::lease_period_index(now)?;
|
||||||
@@ -1559,7 +1563,7 @@ pub mod migrations {
|
|||||||
fn pre_upgrade() -> Result<sp_std::vec::Vec<u8>, sp_runtime::TryRuntimeError> {
|
fn pre_upgrade() -> Result<sp_std::vec::Vec<u8>, sp_runtime::TryRuntimeError> {
|
||||||
if System::last_runtime_upgrade_spec_version() > UPGRADE_SESSION_KEYS_FROM_SPEC {
|
if System::last_runtime_upgrade_spec_version() > UPGRADE_SESSION_KEYS_FROM_SPEC {
|
||||||
log::warn!(target: "runtime::session_keys", "Skipping session keys migration pre-upgrade check due to spec version (already applied?)");
|
log::warn!(target: "runtime::session_keys", "Skipping session keys migration pre-upgrade check due to spec version (already applied?)");
|
||||||
return Ok(Vec::new())
|
return Ok(Vec::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
log::info!(target: "runtime::session_keys", "Collecting pre-upgrade session keys state");
|
log::info!(target: "runtime::session_keys", "Collecting pre-upgrade session keys state");
|
||||||
@@ -1588,7 +1592,7 @@ pub mod migrations {
|
|||||||
fn on_runtime_upgrade() -> Weight {
|
fn on_runtime_upgrade() -> Weight {
|
||||||
if System::last_runtime_upgrade_spec_version() > UPGRADE_SESSION_KEYS_FROM_SPEC {
|
if System::last_runtime_upgrade_spec_version() > UPGRADE_SESSION_KEYS_FROM_SPEC {
|
||||||
log::info!("Skipping session keys upgrade: already applied");
|
log::info!("Skipping session keys upgrade: already applied");
|
||||||
return <Runtime as frame_system::Config>::DbWeight::get().reads(1)
|
return <Runtime as frame_system::Config>::DbWeight::get().reads(1);
|
||||||
}
|
}
|
||||||
log::trace!("Upgrading session keys");
|
log::trace!("Upgrading session keys");
|
||||||
Session::upgrade_keys::<OldSessionKeys, _>(transform_session_keys);
|
Session::upgrade_keys::<OldSessionKeys, _>(transform_session_keys);
|
||||||
@@ -1601,7 +1605,7 @@ pub mod migrations {
|
|||||||
) -> Result<(), sp_runtime::TryRuntimeError> {
|
) -> Result<(), sp_runtime::TryRuntimeError> {
|
||||||
if System::last_runtime_upgrade_spec_version() > UPGRADE_SESSION_KEYS_FROM_SPEC {
|
if System::last_runtime_upgrade_spec_version() > UPGRADE_SESSION_KEYS_FROM_SPEC {
|
||||||
log::warn!(target: "runtime::session_keys", "Skipping session keys migration post-upgrade check due to spec version (already applied?)");
|
log::warn!(target: "runtime::session_keys", "Skipping session keys migration post-upgrade check due to spec version (already applied?)");
|
||||||
return Ok(())
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let key_ids = SessionKeys::key_ids();
|
let key_ids = SessionKeys::key_ids();
|
||||||
@@ -1785,6 +1789,37 @@ sp_api::impl_runtime_apis! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl xcm_fee_payment_runtime_api::XcmPaymentApi<Block> for Runtime {
|
||||||
|
fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result<Vec<VersionedAssetId>, XcmPaymentApiError> {
|
||||||
|
if !matches!(xcm_version, 3 | 4) {
|
||||||
|
return Err(XcmPaymentApiError::UnhandledXcmVersion);
|
||||||
|
}
|
||||||
|
Ok([VersionedAssetId::V4(xcm_config::TokenLocation::get().into())]
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|asset| asset.into_version(xcm_version).ok())
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result<u128, XcmPaymentApiError> {
|
||||||
|
let local_asset = VersionedAssetId::V4(xcm_config::TokenLocation::get().into());
|
||||||
|
let asset = asset
|
||||||
|
.into_version(4)
|
||||||
|
.map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?;
|
||||||
|
|
||||||
|
if asset != local_asset { return Err(XcmPaymentApiError::AssetNotFound); }
|
||||||
|
|
||||||
|
Ok(WeightToFee::weight_to_fee(&weight))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn query_xcm_weight(message: VersionedXcm<()>) -> Result<Weight, XcmPaymentApiError> {
|
||||||
|
XcmPallet::query_xcm_weight(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result<VersionedAssets, XcmPaymentApiError> {
|
||||||
|
XcmPallet::query_delivery_fees(destination, message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl sp_api::Metadata<Block> for Runtime {
|
impl sp_api::Metadata<Block> for Runtime {
|
||||||
fn metadata() -> OpaqueMetadata {
|
fn metadata() -> OpaqueMetadata {
|
||||||
OpaqueMetadata::new(Runtime::metadata().into())
|
OpaqueMetadata::new(Runtime::metadata().into())
|
||||||
@@ -2493,7 +2528,7 @@ mod remote_tests {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn run_migrations() {
|
async fn run_migrations() {
|
||||||
if var("RUN_MIGRATION_TESTS").is_err() {
|
if var("RUN_MIGRATION_TESTS").is_err() {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sp_tracing::try_init_simple();
|
sp_tracing::try_init_simple();
|
||||||
|
|||||||
@@ -114,6 +114,7 @@ runtime-parachains = { package = "polkadot-runtime-parachains", path = "../parac
|
|||||||
xcm = { package = "staging-xcm", path = "../../xcm", default-features = false }
|
xcm = { package = "staging-xcm", path = "../../xcm", default-features = false }
|
||||||
xcm-executor = { package = "staging-xcm-executor", path = "../../xcm/xcm-executor", default-features = false }
|
xcm-executor = { package = "staging-xcm-executor", path = "../../xcm/xcm-executor", default-features = false }
|
||||||
xcm-builder = { package = "staging-xcm-builder", path = "../../xcm/xcm-builder", default-features = false }
|
xcm-builder = { package = "staging-xcm-builder", path = "../../xcm/xcm-builder", default-features = false }
|
||||||
|
xcm-fee-payment-runtime-api = { path = "../../xcm/xcm-fee-payment-runtime-api", default-features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
hex-literal = "0.4.1"
|
hex-literal = "0.4.1"
|
||||||
@@ -227,6 +228,7 @@ std = [
|
|||||||
"westend-runtime-constants/std",
|
"westend-runtime-constants/std",
|
||||||
"xcm-builder/std",
|
"xcm-builder/std",
|
||||||
"xcm-executor/std",
|
"xcm-executor/std",
|
||||||
|
"xcm-fee-payment-runtime-api/std",
|
||||||
"xcm/std",
|
"xcm/std",
|
||||||
]
|
]
|
||||||
runtime-benchmarks = [
|
runtime-benchmarks = [
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ use frame_support::{
|
|||||||
InstanceFilter, KeyOwnerProofSystem, LinearStoragePrice, ProcessMessage,
|
InstanceFilter, KeyOwnerProofSystem, LinearStoragePrice, ProcessMessage,
|
||||||
ProcessMessageError, WithdrawReasons,
|
ProcessMessageError, WithdrawReasons,
|
||||||
},
|
},
|
||||||
weights::{ConstantMultiplier, WeightMeter},
|
weights::{ConstantMultiplier, WeightMeter, WeightToFee as _},
|
||||||
PalletId,
|
PalletId,
|
||||||
};
|
};
|
||||||
use frame_system::{EnsureRoot, EnsureSigned};
|
use frame_system::{EnsureRoot, EnsureSigned};
|
||||||
@@ -101,11 +101,13 @@ use sp_std::{collections::btree_map::BTreeMap, prelude::*};
|
|||||||
use sp_version::NativeVersion;
|
use sp_version::NativeVersion;
|
||||||
use sp_version::RuntimeVersion;
|
use sp_version::RuntimeVersion;
|
||||||
use xcm::{
|
use xcm::{
|
||||||
latest::{InteriorLocation, Junction, Junction::PalletInstance},
|
latest::prelude::*, IntoVersion, VersionedAssetId, VersionedAssets, VersionedLocation,
|
||||||
VersionedLocation,
|
VersionedXcm,
|
||||||
};
|
};
|
||||||
use xcm_builder::PayOverXcm;
|
use xcm_builder::PayOverXcm;
|
||||||
|
|
||||||
|
use xcm_fee_payment_runtime_api::Error as XcmPaymentApiError;
|
||||||
|
|
||||||
pub use frame_system::Call as SystemCall;
|
pub use frame_system::Call as SystemCall;
|
||||||
pub use pallet_balances::Call as BalancesCall;
|
pub use pallet_balances::Call as BalancesCall;
|
||||||
pub use pallet_election_provider_multi_phase::{Call as EPMCall, GeometricDepositBase};
|
pub use pallet_election_provider_multi_phase::{Call as EPMCall, GeometricDepositBase};
|
||||||
@@ -1667,11 +1669,11 @@ pub mod migrations {
|
|||||||
let now = frame_system::Pallet::<Runtime>::block_number();
|
let now = frame_system::Pallet::<Runtime>::block_number();
|
||||||
let lease = slots::Pallet::<Runtime>::lease(para);
|
let lease = slots::Pallet::<Runtime>::lease(para);
|
||||||
if lease.is_empty() {
|
if lease.is_empty() {
|
||||||
return None
|
return None;
|
||||||
}
|
}
|
||||||
// Lease not yet started, ignore:
|
// Lease not yet started, ignore:
|
||||||
if lease.iter().any(Option::is_none) {
|
if lease.iter().any(Option::is_none) {
|
||||||
return None
|
return None;
|
||||||
}
|
}
|
||||||
let (index, _) =
|
let (index, _) =
|
||||||
<slots::Pallet<Runtime> as Leaser<BlockNumber>>::lease_period_index(now)?;
|
<slots::Pallet<Runtime> as Leaser<BlockNumber>>::lease_period_index(now)?;
|
||||||
@@ -1693,7 +1695,7 @@ pub mod migrations {
|
|||||||
fn pre_upgrade() -> Result<sp_std::vec::Vec<u8>, sp_runtime::TryRuntimeError> {
|
fn pre_upgrade() -> Result<sp_std::vec::Vec<u8>, sp_runtime::TryRuntimeError> {
|
||||||
if System::last_runtime_upgrade_spec_version() > UPGRADE_SESSION_KEYS_FROM_SPEC {
|
if System::last_runtime_upgrade_spec_version() > UPGRADE_SESSION_KEYS_FROM_SPEC {
|
||||||
log::warn!(target: "runtime::session_keys", "Skipping session keys migration pre-upgrade check due to spec version (already applied?)");
|
log::warn!(target: "runtime::session_keys", "Skipping session keys migration pre-upgrade check due to spec version (already applied?)");
|
||||||
return Ok(Vec::new())
|
return Ok(Vec::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
log::info!(target: "runtime::session_keys", "Collecting pre-upgrade session keys state");
|
log::info!(target: "runtime::session_keys", "Collecting pre-upgrade session keys state");
|
||||||
@@ -1722,7 +1724,7 @@ pub mod migrations {
|
|||||||
fn on_runtime_upgrade() -> Weight {
|
fn on_runtime_upgrade() -> Weight {
|
||||||
if System::last_runtime_upgrade_spec_version() > UPGRADE_SESSION_KEYS_FROM_SPEC {
|
if System::last_runtime_upgrade_spec_version() > UPGRADE_SESSION_KEYS_FROM_SPEC {
|
||||||
log::warn!("Skipping session keys upgrade: already applied");
|
log::warn!("Skipping session keys upgrade: already applied");
|
||||||
return <Runtime as frame_system::Config>::DbWeight::get().reads(1)
|
return <Runtime as frame_system::Config>::DbWeight::get().reads(1);
|
||||||
}
|
}
|
||||||
log::info!("Upgrading session keys");
|
log::info!("Upgrading session keys");
|
||||||
Session::upgrade_keys::<OldSessionKeys, _>(transform_session_keys);
|
Session::upgrade_keys::<OldSessionKeys, _>(transform_session_keys);
|
||||||
@@ -1735,7 +1737,7 @@ pub mod migrations {
|
|||||||
) -> Result<(), sp_runtime::TryRuntimeError> {
|
) -> Result<(), sp_runtime::TryRuntimeError> {
|
||||||
if System::last_runtime_upgrade_spec_version() > UPGRADE_SESSION_KEYS_FROM_SPEC {
|
if System::last_runtime_upgrade_spec_version() > UPGRADE_SESSION_KEYS_FROM_SPEC {
|
||||||
log::warn!(target: "runtime::session_keys", "Skipping session keys migration post-upgrade check due to spec version (already applied?)");
|
log::warn!(target: "runtime::session_keys", "Skipping session keys migration post-upgrade check due to spec version (already applied?)");
|
||||||
return Ok(())
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let key_ids = SessionKeys::key_ids();
|
let key_ids = SessionKeys::key_ids();
|
||||||
@@ -2332,6 +2334,37 @@ sp_api::impl_runtime_apis! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl xcm_fee_payment_runtime_api::XcmPaymentApi<Block> for Runtime {
|
||||||
|
fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result<Vec<VersionedAssetId>, XcmPaymentApiError> {
|
||||||
|
if !matches!(xcm_version, 3 | 4) {
|
||||||
|
return Err(XcmPaymentApiError::UnhandledXcmVersion);
|
||||||
|
}
|
||||||
|
Ok([VersionedAssetId::V4(xcm_config::TokenLocation::get().into())]
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|asset| asset.into_version(xcm_version).ok())
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result<u128, XcmPaymentApiError> {
|
||||||
|
let local_asset = VersionedAssetId::V4(xcm_config::TokenLocation::get().into());
|
||||||
|
let asset = asset
|
||||||
|
.into_version(4)
|
||||||
|
.map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?;
|
||||||
|
|
||||||
|
if asset != local_asset { return Err(XcmPaymentApiError::AssetNotFound); }
|
||||||
|
|
||||||
|
Ok(WeightToFee::weight_to_fee(&weight))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn query_xcm_weight(message: VersionedXcm<()>) -> Result<Weight, XcmPaymentApiError> {
|
||||||
|
XcmPallet::query_xcm_weight(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result<VersionedAssets, XcmPaymentApiError> {
|
||||||
|
XcmPallet::query_delivery_fees(destination, message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl pallet_nomination_pools_runtime_api::NominationPoolsApi<
|
impl pallet_nomination_pools_runtime_api::NominationPoolsApi<
|
||||||
Block,
|
Block,
|
||||||
AccountId,
|
AccountId,
|
||||||
@@ -2650,7 +2683,7 @@ mod remote_tests {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn run_migrations() {
|
async fn run_migrations() {
|
||||||
if var("RUN_MIGRATION_TESTS").is_err() {
|
if var("RUN_MIGRATION_TESTS").is_err() {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sp_tracing::try_init_simple();
|
sp_tracing::try_init_simple();
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ use std::collections::HashSet;
|
|||||||
use crate::*;
|
use crate::*;
|
||||||
use frame_support::traits::WhitelistedStorageKeys;
|
use frame_support::traits::WhitelistedStorageKeys;
|
||||||
use sp_core::hexdisplay::HexDisplay;
|
use sp_core::hexdisplay::HexDisplay;
|
||||||
use xcm::latest::prelude::*;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn remove_keys_weight_is_sensible() {
|
fn remove_keys_weight_is_sensible() {
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ sp-std = { path = "../../../substrate/primitives/std", default-features = false
|
|||||||
xcm = { package = "staging-xcm", path = "..", default-features = false }
|
xcm = { package = "staging-xcm", path = "..", default-features = false }
|
||||||
xcm-executor = { package = "staging-xcm-executor", path = "../xcm-executor", default-features = false }
|
xcm-executor = { package = "staging-xcm-executor", path = "../xcm-executor", default-features = false }
|
||||||
xcm-builder = { package = "staging-xcm-builder", path = "../xcm-builder", default-features = false }
|
xcm-builder = { package = "staging-xcm-builder", path = "../xcm-builder", default-features = false }
|
||||||
|
xcm-fee-payment-runtime-api = { path = "../xcm-fee-payment-runtime-api", default-features = false }
|
||||||
|
|
||||||
# marked optional, used in benchmarking
|
# marked optional, used in benchmarking
|
||||||
frame-benchmarking = { path = "../../../substrate/frame/benchmarking", default-features = false, optional = true }
|
frame-benchmarking = { path = "../../../substrate/frame/benchmarking", default-features = false, optional = true }
|
||||||
@@ -54,6 +55,7 @@ std = [
|
|||||||
"sp-std/std",
|
"sp-std/std",
|
||||||
"xcm-builder/std",
|
"xcm-builder/std",
|
||||||
"xcm-executor/std",
|
"xcm-executor/std",
|
||||||
|
"xcm-fee-payment-runtime-api/std",
|
||||||
"xcm/std",
|
"xcm/std",
|
||||||
]
|
]
|
||||||
runtime-benchmarks = [
|
runtime-benchmarks = [
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ use xcm_executor::{
|
|||||||
},
|
},
|
||||||
AssetsInHolding,
|
AssetsInHolding,
|
||||||
};
|
};
|
||||||
|
use xcm_fee_payment_runtime_api::Error as FeePaymentError;
|
||||||
|
|
||||||
#[cfg(any(feature = "try-runtime", test))]
|
#[cfg(any(feature = "try-runtime", test))]
|
||||||
use sp_runtime::TryRuntimeError;
|
use sp_runtime::TryRuntimeError;
|
||||||
@@ -2363,6 +2364,37 @@ impl<T: Config> Pallet<T> {
|
|||||||
AccountIdConversion::<T::AccountId>::into_account_truncating(&ID)
|
AccountIdConversion::<T::AccountId>::into_account_truncating(&ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn query_xcm_weight(message: VersionedXcm<()>) -> Result<Weight, FeePaymentError> {
|
||||||
|
let message =
|
||||||
|
Xcm::<()>::try_from(message).map_err(|_| FeePaymentError::VersionedConversionFailed)?;
|
||||||
|
|
||||||
|
T::Weigher::weight(&mut message.into()).map_err(|()| {
|
||||||
|
log::error!(target: "xcm::pallet_xcm::query_xcm_weight", "Error when querying XCM weight");
|
||||||
|
FeePaymentError::WeightNotComputable
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn query_delivery_fees(
|
||||||
|
destination: VersionedLocation,
|
||||||
|
message: VersionedXcm<()>,
|
||||||
|
) -> Result<VersionedAssets, FeePaymentError> {
|
||||||
|
let result_version = destination.identify_version().max(message.identify_version());
|
||||||
|
|
||||||
|
let destination =
|
||||||
|
destination.try_into().map_err(|_| FeePaymentError::VersionedConversionFailed)?;
|
||||||
|
|
||||||
|
let message = message.try_into().map_err(|_| FeePaymentError::VersionedConversionFailed)?;
|
||||||
|
|
||||||
|
let (_, fees) = validate_send::<T::XcmRouter>(destination, message).map_err(|error| {
|
||||||
|
log::error!(target: "xcm::pallet_xcm::query_delivery_fees", "Error when querying delivery fees: {:?}", error);
|
||||||
|
FeePaymentError::Unroutable
|
||||||
|
})?;
|
||||||
|
|
||||||
|
VersionedAssets::from(fees)
|
||||||
|
.into_version(result_version)
|
||||||
|
.map_err(|_| FeePaymentError::VersionedConversionFailed)
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new expectation of a query response with the querier being here.
|
/// Create a new expectation of a query response with the querier being here.
|
||||||
fn do_new_query(
|
fn do_new_query(
|
||||||
responder: impl Into<Location>,
|
responder: impl Into<Location>,
|
||||||
|
|||||||
@@ -443,6 +443,16 @@ impl<C> IntoVersion for VersionedXcm<C> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<C> IdentifyVersion for VersionedXcm<C> {
|
||||||
|
fn identify_version(&self) -> Version {
|
||||||
|
match self {
|
||||||
|
Self::V2(_) => v2::VERSION,
|
||||||
|
Self::V3(_) => v3::VERSION,
|
||||||
|
Self::V4(_) => v4::VERSION,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<RuntimeCall> From<v2::Xcm<RuntimeCall>> for VersionedXcm<RuntimeCall> {
|
impl<RuntimeCall> From<v2::Xcm<RuntimeCall>> for VersionedXcm<RuntimeCall> {
|
||||||
fn from(x: v2::Xcm<RuntimeCall>) -> Self {
|
fn from(x: v2::Xcm<RuntimeCall>) -> Self {
|
||||||
VersionedXcm::V2(x)
|
VersionedXcm::V2(x)
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
[package]
|
||||||
|
name = "xcm-fee-payment-runtime-api"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
license = "Apache-2.0"
|
||||||
|
repository.workspace = true
|
||||||
|
description = "XCM fee payment runtime API"
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
targets = ["x86_64-unknown-linux-gnu"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = [
|
||||||
|
"derive",
|
||||||
|
] }
|
||||||
|
|
||||||
|
sp-api = { path = "../../../substrate/primitives/api", default-features = false }
|
||||||
|
scale-info = { version = "2.10.0", default-features = false, features = [
|
||||||
|
"derive",
|
||||||
|
"serde",
|
||||||
|
] }
|
||||||
|
sp-std = { path = "../../../substrate/primitives/std", default-features = false }
|
||||||
|
sp-runtime = { path = "../../../substrate/primitives/runtime", default-features = false }
|
||||||
|
sp-weights = { path = "../../../substrate/primitives/weights", default-features = false }
|
||||||
|
xcm = { package = "staging-xcm", path = "../", default-features = false }
|
||||||
|
frame-support = { path = "../../../substrate/frame/support", default-features = false }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["std"]
|
||||||
|
std = [
|
||||||
|
"codec/std",
|
||||||
|
"frame-support/std",
|
||||||
|
"scale-info/std",
|
||||||
|
"sp-api/std",
|
||||||
|
"sp-runtime/std",
|
||||||
|
"sp-std/std",
|
||||||
|
"sp-weights/std",
|
||||||
|
"xcm/std",
|
||||||
|
]
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Polkadot.
|
||||||
|
|
||||||
|
// Substrate is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Substrate is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Runtime API definition for xcm transaction payment.
|
||||||
|
|
||||||
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
|
use codec::{Decode, Encode};
|
||||||
|
use frame_support::pallet_prelude::TypeInfo;
|
||||||
|
use sp_std::vec::Vec;
|
||||||
|
use sp_weights::Weight;
|
||||||
|
use xcm::{Version, VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm};
|
||||||
|
|
||||||
|
sp_api::decl_runtime_apis! {
|
||||||
|
/// A trait of XCM payment API.
|
||||||
|
///
|
||||||
|
/// API provides functionality for obtaining:
|
||||||
|
///
|
||||||
|
/// * the weight required to execute an XCM message,
|
||||||
|
/// * a list of acceptable `AssetId`s for message execution payment,
|
||||||
|
/// * the cost of the weight in the specified acceptable `AssetId`.
|
||||||
|
/// * the fees for an XCM message delivery.
|
||||||
|
///
|
||||||
|
/// To determine the execution weight of the calls required for
|
||||||
|
/// [`xcm::latest::Instruction::Transact`] instruction, `TransactionPaymentCallApi` can be used.
|
||||||
|
pub trait XcmPaymentApi {
|
||||||
|
/// Returns a list of acceptable payment assets.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `xcm_version`: Version.
|
||||||
|
fn query_acceptable_payment_assets(xcm_version: Version) -> Result<Vec<VersionedAssetId>, Error>;
|
||||||
|
|
||||||
|
/// Returns a weight needed to execute a XCM.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `message`: `VersionedXcm`.
|
||||||
|
fn query_xcm_weight(message: VersionedXcm<()>) -> Result<Weight, Error>;
|
||||||
|
|
||||||
|
/// Converts a weight into a fee for the specified `AssetId`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `weight`: convertible `Weight`.
|
||||||
|
/// * `asset`: `VersionedAssetId`.
|
||||||
|
fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result<u128, Error>;
|
||||||
|
|
||||||
|
/// Get delivery fees for sending a specific `message` to a `destination`.
|
||||||
|
/// These always come in a specific asset, defined by the chain.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `message`: The message that'll be sent, necessary because most delivery fees are based on the
|
||||||
|
/// size of the message.
|
||||||
|
/// * `destination`: The destination to send the message to. Different destinations may use
|
||||||
|
/// different senders that charge different fees.
|
||||||
|
fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result<VersionedAssets, Error>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo)]
|
||||||
|
pub enum Error {
|
||||||
|
/// An API part is unsupported.
|
||||||
|
#[codec(index = 0)]
|
||||||
|
Unimplemented,
|
||||||
|
|
||||||
|
/// Converting a versioned data structure from one version to another failed.
|
||||||
|
#[codec(index = 1)]
|
||||||
|
VersionedConversionFailed,
|
||||||
|
|
||||||
|
/// XCM message weight calculation failed.
|
||||||
|
#[codec(index = 2)]
|
||||||
|
WeightNotComputable,
|
||||||
|
|
||||||
|
/// XCM version not able to be handled.
|
||||||
|
#[codec(index = 3)]
|
||||||
|
UnhandledXcmVersion,
|
||||||
|
|
||||||
|
/// The given asset is not handled as a fee asset.
|
||||||
|
#[codec(index = 4)]
|
||||||
|
AssetNotFound,
|
||||||
|
|
||||||
|
/// Destination is known to be unroutable.
|
||||||
|
#[codec(index = 5)]
|
||||||
|
Unroutable,
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
|
||||||
|
# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json
|
||||||
|
|
||||||
|
title: "XCM fee payment API"
|
||||||
|
|
||||||
|
doc:
|
||||||
|
- audience: Runtime Dev
|
||||||
|
description: |
|
||||||
|
A runtime API was added for estimating the fees required for XCM execution and delivery.
|
||||||
|
This is the basic building block needed for UIs to accurately estimate fees.
|
||||||
|
An example implementation is shown in the PR. Ideally it's simple to implement, you only need to call existing parts of your XCM config.
|
||||||
|
The API looks like so:
|
||||||
|
```rust
|
||||||
|
fn query_acceptable_payment_assets(xcm_version: Version) -> Result<Vec<VersionedAssetId>, Error>;
|
||||||
|
fn query_xcm_weight(message: VersionedXcm<Call>) -> Result<Weight, Error>;
|
||||||
|
fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result<u128, Error>;
|
||||||
|
fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result<VersionedAssets, Error>;
|
||||||
|
```
|
||||||
|
The first three relate to XCM execution fees, given an XCM, you can query its weight, then which assets are acceptable for buying weight and convert weight to a number of those assets.
|
||||||
|
The last one takes in a destination and a message you want to send from the runtime you're executing this on, it will give you the delivery fees.
|
||||||
|
|
||||||
|
crates:
|
||||||
|
- name: xcm-fee-payment-runtime-api
|
||||||
|
- name: rococo-runtime
|
||||||
|
- name: westend-runtime
|
||||||
|
|
||||||
Reference in New Issue
Block a user