diff --git a/Cargo.lock b/Cargo.lock index 45e17f1f3..80ee29149 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1443,6 +1443,7 @@ dependencies = [ "strum 0.26.3", "strum_macros 0.26.4", "substrate-wasm-builder", + "xcm-fee-payment-runtime-api", "zenlink-protocol", "zenlink-protocol-runtime-api", "zenlink-stable-amm", @@ -1624,6 +1625,7 @@ dependencies = [ "strum 0.26.3", "strum_macros 0.26.4", "substrate-wasm-builder", + "xcm-fee-payment-runtime-api", "zenlink-protocol", "zenlink-protocol-runtime-api", ] @@ -3962,9 +3964,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.124" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "273dcfd3acd4e1e276af13ed2a43eea7001318823e7a726a6b3ed39b4acc0b82" +checksum = "28403c86fc49e3401fdf45499ba37fad6493d9329449d6449d7f0e10f4654d28" dependencies = [ "cc", "cxxbridge-flags", @@ -3974,9 +3976,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.124" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b2766fbd92be34e9ed143898fce6c572dc009de39506ed6903e5a05b68914e" +checksum = "78da94fef01786dc3e0c76eafcd187abcaa9972c78e05ff4041e24fdf059c285" dependencies = [ "cc", "codespan-reporting", @@ -3989,15 +3991,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.124" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "839fcd5e43464614ffaa989eaf1c139ef1f0c51672a1ed08023307fa1b909ccd" +checksum = "e2a6f5e1dfb4b34292ad4ea1facbfdaa1824705b231610087b00b17008641809" [[package]] name = "cxxbridge-macro" -version = "1.0.124" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2c1c1776b986979be68bb2285da855f8d8a35851a769fca8740df7c3d07877" +checksum = "50c49547d73ba8dcfd4ad7325d64c6d5391ff4224d498fc39a6f3f49825a530d" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index d5a5891c1..d8c8318ff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -253,6 +253,7 @@ substrate-fixed = { git = "https://github.com/encoint xcm = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.13.0", package = "staging-xcm", default-features = false } xcm-builder = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.13.0", package = "staging-xcm-builder", default-features = false } xcm-executor = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.13.0", package = "staging-xcm-executor", default-features = false } +xcm-fee-payment-runtime-api = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.13.0", default-features = false } # polkadot-sdk (client) cumulus-client-cli = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.13.0" } diff --git a/pallets/asset-registry/src/lib.rs b/pallets/asset-registry/src/lib.rs index edf1ca0e4..63e760815 100644 --- a/pallets/asset-registry/src/lib.rs +++ b/pallets/asset-registry/src/lib.rs @@ -33,23 +33,19 @@ use frame_support::{ ensure, pallet_prelude::*, traits::{Currency, EnsureOrigin}, - weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight}, + weights::Weight, }; use frame_system::pallet_prelude::*; use scale_info::{prelude::string::String, TypeInfo}; use sp_runtime::{ traits::{One, UniqueSaturatedFrom}, - ArithmeticError, FixedPointNumber, FixedU128, RuntimeDebug, + ArithmeticError, RuntimeDebug, }; use sp_std::{boxed::Box, vec::Vec}; use xcm::{ - opaque::lts::XcmContext, - v3::MultiLocation, - v4::{prelude::*, Asset, Location}, + v4::{prelude::*, Location}, VersionedLocation, }; -use xcm_builder::TakeRevenue; -use xcm_executor::{traits::WeightTrader, AssetsInHolding}; pub mod migrations; mod mock; @@ -423,13 +419,15 @@ impl Pallet { Ok(()) } + + pub fn asset_ids() -> Vec { + LocationToCurrencyIds::::iter_keys().map(|key| AssetId(key)).collect() + } } -pub struct AssetIdMaps(sp_std::marker::PhantomData); +pub struct AssetIdMaps(PhantomData); -impl CurrencyIdMapping>> - for AssetIdMaps -{ +impl CurrencyIdMapping>> for AssetIdMaps { fn get_asset_metadata(asset_ids: AssetIds) -> Option>> { AssetMetadatas::::get(asset_ids) } @@ -442,13 +440,12 @@ impl CurrencyIdMapping::iter_keys().collect() } - fn get_location(currency_id: CurrencyId) -> Option { - CurrencyIdToLocations::::get(currency_id).map(|location| location.try_into().ok())? + fn get_location(currency_id: &CurrencyId) -> Option { + CurrencyIdToLocations::::get(currency_id) } - fn get_currency_id(multi_location: Location) -> Option { - let v4_location = Location::try_from(multi_location).ok()?; - LocationToCurrencyIds::::get(v4_location) + fn get_currency_id(location: &Location) -> Option { + LocationToCurrencyIds::::get(location) } } @@ -586,129 +583,3 @@ impl CurrencyIdRegister for AssetIdMaps { ) } } - -/// Simple fee calculator that requires payment in a single fungible at a fixed rate. -/// -/// The constant `FixedRate` type parameter should be the concrete fungible ID and the amount of it -/// required for one second of weight. -pub struct FixedRateOfAsset, R: TakeRevenue> { - weight: u64, - amount: u128, - ed_ratio: FixedU128, - location: Option, - _marker: PhantomData<(T, FixedRate, R)>, -} - -impl, R: TakeRevenue> WeightTrader - for FixedRateOfAsset -where - BalanceOf: Into, -{ - fn new() -> Self { - Self { - weight: 0, - amount: 0, - ed_ratio: Default::default(), - location: None, - _marker: PhantomData, - } - } - - fn buy_weight( - &mut self, - weight: Weight, - payment: AssetsInHolding, - _context: &XcmContext, - ) -> Result { - log::trace!(target: "asset-registry::weight", "buy_weight weight: {:?}, payment: {:?}", weight, payment); - - // only support first fungible assets now. - let asset_id = payment - .fungible - .iter() - .next() - .map_or(Err(XcmError::TooExpensive), |v| Ok(v.0))?; - - let AssetId(ref location) = asset_id.clone(); - log::debug!(target: "asset-registry::weight", "buy_weight location: {:?}", location); - - let v4_location = - Location::try_from(location.clone()).map_err(|_| XcmError::InvalidLocation)?; - - if let Some(currency_id) = LocationToCurrencyIds::::get(v4_location) { - if let Some(currency_metadatas) = CurrencyMetadatas::::get(currency_id) { - // The integration tests can ensure the ed is non-zero. - let ed_ratio = FixedU128::saturating_from_rational( - currency_metadatas.minimal_balance.into(), - T::Currency::minimum_balance().into(), - ); - // The WEIGHT_REF_TIME_PER_SECOND is non-zero. - let weight_ratio = FixedU128::saturating_from_rational( - weight.ref_time(), - WEIGHT_REF_TIME_PER_SECOND, - ); - let amount = - ed_ratio.saturating_mul_int(weight_ratio.saturating_mul_int(FixedRate::get())); - - let required = Asset { id: asset_id.clone(), fun: Fungible(amount) }; - - log::trace!( - target: "asset-registry::weight", "buy_weight payment: {:?}, required: {:?}, fixed_rate: {:?}, ed_ratio: {:?}, weight_ratio: {:?}", - payment, required, FixedRate::get(), ed_ratio, weight_ratio - ); - let unused = - payment.clone().checked_sub(required).map_err(|_| XcmError::TooExpensive)?; - self.weight = self.weight.saturating_add(weight.ref_time()); - self.amount = self.amount.saturating_add(amount); - self.ed_ratio = ed_ratio; - self.location = Some(location.clone()); - return Ok(unused); - } - }; - - log::trace!(target: "asset-registry::weight", "no concrete fungible asset"); - Err(XcmError::TooExpensive) - } - - fn refund_weight(&mut self, weight: Weight, _context: &XcmContext) -> Option { - log::trace!( - target: "asset-registry::weight", "refund_weight weight: {:?}, weight: {:?}, amount: {:?}, ed_ratio: {:?}, location: {:?}", - weight, self.weight, self.amount, self.ed_ratio, self.location - ); - let weight = weight.min(Weight::from_parts(self.weight, 0)); - let weight_ratio = - FixedU128::saturating_from_rational(weight.ref_time(), WEIGHT_REF_TIME_PER_SECOND); - let amount = self - .ed_ratio - .saturating_mul_int(weight_ratio.saturating_mul_int(FixedRate::get())); - - self.weight = self.weight.saturating_sub(weight.ref_time()); - self.amount = self.amount.saturating_sub(amount); - - log::trace!(target: "asset-registry::weight", "refund_weight amount: {:?}", amount); - if amount > 0 && self.location.is_some() { - Some(Asset { - fun: Fungible(amount), - id: AssetId( - self.location.clone().expect("checked is non-empty; qed").try_into().unwrap(), - ), - }) - } else { - None - } - } -} - -impl, R: TakeRevenue> Drop for FixedRateOfAsset { - fn drop(&mut self) { - log::trace!(target: "asset-registry::weight", "take revenue, weight: {:?}, amount: {:?}, location: {:?}", self.weight, self.amount, self.location); - if self.amount > 0 && self.location.is_some() { - R::take_revenue(Asset { - fun: Fungible(self.amount), - id: AssetId( - self.location.clone().expect("checked is non-empty; qed").try_into().unwrap(), - ), - }); - } - } -} diff --git a/pallets/prices/src/lib.rs b/pallets/prices/src/lib.rs index a65516974..5ca2071f5 100644 --- a/pallets/prices/src/lib.rs +++ b/pallets/prices/src/lib.rs @@ -35,7 +35,6 @@ pub use pallet::*; use pallet_traits::*; use sp_runtime::{traits::CheckedDiv, FixedU128}; use sp_std::vec::Vec; -use xcm::v3::MultiLocation; #[cfg(test)] mod mock; @@ -79,11 +78,7 @@ pub mod pallet { type RelayCurrency: Get; /// Convert Location to `T::CurrencyId`. - type CurrencyIdConvert: CurrencyIdMapping< - CurrencyId, - MultiLocation, - AssetMetadata>, - >; + type CurrencyIdConvert: CurrencyIdMapping>>; /// Weight information type WeightInfo: WeightInfo; diff --git a/pallets/slp/src/lib.rs b/pallets/slp/src/lib.rs index 4ca0f422a..f6c62046a 100644 --- a/pallets/slp/src/lib.rs +++ b/pallets/slp/src/lib.rs @@ -167,11 +167,7 @@ pub mod pallet { >; // asset registry to get asset metadata - type AssetIdMaps: CurrencyIdMapping< - CurrencyId, - MultiLocation, - AssetMetadata>, - >; + type AssetIdMaps: CurrencyIdMapping>>; #[pallet::constant] type TreasuryAccount: Get; diff --git a/pallets/slpx/src/lib.rs b/pallets/slpx/src/lib.rs index 1e3fdb129..3d3f0d399 100644 --- a/pallets/slpx/src/lib.rs +++ b/pallets/slpx/src/lib.rs @@ -110,11 +110,7 @@ pub mod pallet { /// Send Xcm type XcmSender: SendXcm; /// Convert Location to `T::CurrencyId`. - type CurrencyIdConvert: CurrencyIdMapping< - CurrencyId, - xcm::v3::MultiLocation, - AssetMetadata>, - >; + type CurrencyIdConvert: CurrencyIdMapping>>; /// TreasuryAccount #[pallet::constant] type TreasuryAccount: Get>; diff --git a/pallets/slpx/src/mock.rs b/pallets/slpx/src/mock.rs index 12c3aa264..605c72a8c 100644 --- a/pallets/slpx/src/mock.rs +++ b/pallets/slpx/src/mock.rs @@ -248,13 +248,13 @@ parameter_types! { pub struct CurrencyIdConvert(sp_std::marker::PhantomData); impl> Convert> for CurrencyIdConvert { fn convert(id: CurrencyId) -> Option { - AssetIdMaps::::get_location(id) + AssetIdMaps::::get_location(&id) } } impl> Convert> for CurrencyIdConvert { fn convert(location: Location) -> Option { - AssetIdMaps::::get_currency_id(location) + AssetIdMaps::::get_currency_id(&location) } } diff --git a/pallets/xcm-interface/src/lib.rs b/pallets/xcm-interface/src/lib.rs index 1a9b609ca..1dbc6bba5 100644 --- a/pallets/xcm-interface/src/lib.rs +++ b/pallets/xcm-interface/src/lib.rs @@ -73,11 +73,7 @@ pub mod pallet { type AccountIdToLocation: Convert; /// Convert Location to `T::CurrencyId`. - type CurrencyIdConvert: CurrencyIdMapping< - CurrencyId, - xcm::v3::MultiLocation, - AssetMetadata>, - >; + type CurrencyIdConvert: CurrencyIdMapping>>; #[pallet::constant] type ParachainId: Get; @@ -170,8 +166,8 @@ pub mod pallet { to: H160, ) -> DispatchResult { let who = ensure_signed(origin.clone())?; - let asset_location = - T::CurrencyIdConvert::get_location(currency_id).ok_or(Error::::FailToConvert)?; + let asset_location = T::CurrencyIdConvert::get_location(¤cy_id) + .ok_or(Error::::FailToConvert)?; let asset: Asset = Asset { id: AssetId(asset_location), diff --git a/primitives/src/traits.rs b/primitives/src/traits.rs index e7d7e5d69..b29a588bf 100644 --- a/primitives/src/traits.rs +++ b/primitives/src/traits.rs @@ -34,6 +34,7 @@ use sp_runtime::{ BoundedVec, DispatchError, DispatchResult, TypeId, }; use sp_std::{cmp::Ordering, fmt::Debug, vec::Vec}; +use xcm::prelude::Location; pub trait TokenInfo { fn name(&self) -> Option<&str>; @@ -143,15 +144,15 @@ pub trait SlpxOperator { } /// A mapping between CurrencyId and AssetMetadata. -pub trait CurrencyIdMapping { +pub trait CurrencyIdMapping { /// Returns the AssetMetadata associated with a given `AssetIds`. fn get_asset_metadata(asset_ids: AssetIds) -> Option; /// Returns the AssetMetadata associated with a given `CurrencyId`. fn get_currency_metadata(currency_id: CurrencyId) -> Option; /// Returns the Location associated with a given CurrencyId. - fn get_location(currency_id: CurrencyId) -> Option; + fn get_location(currency_id: &CurrencyId) -> Option; /// Returns the CurrencyId associated with a given Location. - fn get_currency_id(multi_location: xcm::v4::Location) -> Option; + fn get_currency_id(location: &Location) -> Option; /// Returns all currencies in currencyMetadata. fn get_all_currency() -> Vec; } diff --git a/primitives/src/xcm.rs b/primitives/src/xcm.rs index efcb265a8..4bdfd8700 100644 --- a/primitives/src/xcm.rs +++ b/primitives/src/xcm.rs @@ -25,7 +25,7 @@ use sp_runtime::traits::Convert; use sp_std::marker::PhantomData; use xcm::{ latest::Asset, - prelude::{AccountId32, Ethereum, Fungible, GlobalConsensus, Parachain}, + prelude::{AccountId32, Ethereum, Fungible, GeneralKey, GlobalConsensus, Parachain}, v4::{AssetId, InteriorLocation, Location, NetworkId, Parent}, }; @@ -58,6 +58,13 @@ parameter_types! { pub SelfLocation: Location = Location::here(); pub AssetHubLocation: Location = Location::new(1, Parachain(AssetHubChainId::get())); pub EthereumLocation: Location = Location::new(2, [GlobalConsensus(Ethereum { chain_id: EthereumChainId::get() })]); + pub LocalBncLocation: Location = Location::new(0, [GeneralKey { + length: 2, + data: [ + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 + ], + }]); pub const KusamaNetwork: NetworkId = NetworkId::Kusama; pub const PolkadotNetwork: NetworkId = NetworkId::Polkadot; diff --git a/runtime/bifrost-kusama/Cargo.toml b/runtime/bifrost-kusama/Cargo.toml index 20bca7d5d..020f2abd5 100644 --- a/runtime/bifrost-kusama/Cargo.toml +++ b/runtime/bifrost-kusama/Cargo.toml @@ -92,6 +92,7 @@ polkadot-runtime-common = { workspace = true } xcm = { workspace = true } xcm-builder = { workspace = true } xcm-executor = { workspace = true } +xcm-fee-payment-runtime-api = { workspace = true } # orml orml-tokens = { workspace = true } @@ -223,6 +224,7 @@ std = [ "xcm/std", "xcm-builder/std", "xcm-executor/std", + "xcm-fee-payment-runtime-api/std", "orml-tokens/std", "orml-traits/std", diff --git a/runtime/bifrost-kusama/src/lib.rs b/runtime/bifrost-kusama/src/lib.rs index 22d139ec3..8db4a7d52 100644 --- a/runtime/bifrost-kusama/src/lib.rs +++ b/runtime/bifrost-kusama/src/lib.rs @@ -37,9 +37,9 @@ use bifrost_primitives::{ BifrostCrowdloanId, BifrostVsbondAccount, BuybackPalletId, CommissionPalletId, FarmingBoostPalletId, FarmingGaugeRewardIssuerPalletId, FarmingKeeperPalletId, FarmingRewardIssuerPalletId, FeeSharePalletId, FlexibleFeePalletId, IncentivePoolAccount, - LendMarketPalletId, MerkleDirtributorPalletId, OraclePalletId, ParachainStakingPalletId, - SlpEntrancePalletId, SlpExitPalletId, SystemMakerPalletId, SystemStakingPalletId, - TreasuryPalletId, VBNCConvertPalletId, + LendMarketPalletId, LocalBncLocation, MerkleDirtributorPalletId, OraclePalletId, + ParachainStakingPalletId, SlpEntrancePalletId, SlpExitPalletId, SystemMakerPalletId, + SystemStakingPalletId, TreasuryPalletId, VBNCConvertPalletId, }; pub use frame_support::{ construct_runtime, match_types, parameter_types, @@ -109,6 +109,7 @@ use frame_support::{ Currency, EitherOf, EitherOfDiverse, Get, Imbalance, InsideBoth, LinearStoragePrice, LockIdentifier, OnUnbalanced, }, + weights::WeightToFee as _, }; use frame_system::{EnsureRoot, EnsureRootWithSuccess, EnsureSigned}; use hex_literal::hex; @@ -133,16 +134,23 @@ use governance::{ // xcm config pub mod xcm_config; -use bifrost_primitives::MoonriverChainId; +use bifrost_primitives::{MoonriverChainId, OraclePriceProvider}; use bifrost_runtime_common::currency_converter::CurrencyIdConvert; use pallet_xcm::{EnsureResponse, QueryStatus}; use sp_runtime::traits::{IdentityLookup, Verify}; -use xcm::{v3::MultiLocation, v4::prelude::*}; +use xcm::{ + v3::MultiLocation, v4::prelude::*, IntoVersion, VersionedAssetId, VersionedAssets, + VersionedLocation, VersionedXcm, +}; pub use xcm_config::{ AccountId32Aliases, BifrostTreasuryAccount, ExistentialDeposits, MultiCurrency, Sibling, SiblingParachainConvertsVia, XcmConfig, XcmRouter, }; use xcm_executor::{traits::QueryHandler, XcmExecutor}; +use xcm_fee_payment_runtime_api::{ + dry_run::{CallDryRunEffects, Error as XcmDryRunApiError, XcmDryRunEffects}, + fees::Error as XcmPaymentApiError, +}; impl_opaque_keys! { pub struct SessionKeys { @@ -2197,6 +2205,49 @@ impl_runtime_apis! { } } + impl xcm_fee_payment_runtime_api::fees::XcmPaymentApi for Runtime { + fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result, XcmPaymentApiError> { + let acceptable_assets = AssetRegistry::asset_ids(); + PolkadotXcm::query_acceptable_payment_assets(xcm_version, acceptable_assets) + } + + fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { + let asset = asset + .into_version(4) + .map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?; + let bnc_asset = VersionedAssetId::V4(LocalBncLocation::get().into()); + + if asset == bnc_asset { + // for native token + Ok(WeightToFee::weight_to_fee(&weight)) + } else { + let native_fee = WeightToFee::weight_to_fee(&weight); + let asset_location = &asset.try_as::().map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?.0; + let asset_currency = AssetIdMaps::::get_currency_id(&asset_location).ok_or(XcmPaymentApiError::AssetNotFound)?; + let asset_fee = Prices::get_oracle_amount_by_currency_and_amount_in(&bifrost_primitives::BNC, native_fee, &asset_currency).ok_or(XcmPaymentApiError::AssetNotFound)?.0; + Ok(asset_fee) + } + } + + fn query_xcm_weight(message: VersionedXcm<()>) -> Result { + PolkadotXcm::query_xcm_weight(message) + } + + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { + PolkadotXcm::query_delivery_fees(destination, message) + } + } + + impl xcm_fee_payment_runtime_api::dry_run::DryRunApi for Runtime { + fn dry_run_call(origin: OriginCaller, call: RuntimeCall) -> Result, XcmDryRunApiError> { + PolkadotXcm::dry_run_call::(origin, call) + } + + fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm) -> Result, XcmDryRunApiError> { + PolkadotXcm::dry_run_xcm::(origin_location, xcm) + } + } + impl bifrost_flexible_fee_rpc_runtime_api::FlexibleFeeRuntimeApi for Runtime { fn get_fee_token_and_amount(who: AccountId, fee: Balance,utx: ::Extrinsic) -> (CurrencyId, Balance) { let call = utx.function; diff --git a/runtime/bifrost-kusama/src/xcm_config.rs b/runtime/bifrost-kusama/src/xcm_config.rs index 90b13211a..e4100406b 100644 --- a/runtime/bifrost-kusama/src/xcm_config.rs +++ b/runtime/bifrost-kusama/src/xcm_config.rs @@ -17,17 +17,15 @@ // along with this program. If not, see . use super::*; -use bifrost_asset_registry::{AssetIdMaps, FixedRateOfAsset}; +use bifrost_asset_registry::AssetIdMaps; use bifrost_primitives::{ - AccountId, AccountIdToLocation, AssetHubChainId, AssetHubLocation, AssetPrefixFrom, CurrencyId, - CurrencyIdMapping, EthereumLocation, KaruraChainId, KusamaNetwork, KusamaUniversalLocation, - NativeAssetFrom, PhalaChainId, SelfLocation, TokenSymbol, + AccountId, AccountIdToLocation, AssetHubLocation, AssetPrefixFrom, CurrencyId, + CurrencyIdMapping, EthereumLocation, KusamaNetwork, KusamaUniversalLocation, NativeAssetFrom, + SelfLocation, TokenSymbol, }; pub use cumulus_primitives_core::ParaId; use frame_support::{parameter_types, sp_runtime::traits::Convert, traits::Get}; -use parity_scale_codec::Encode; pub use polkadot_parachain_primitives::primitives::Sibling; -use sp_std::convert::TryFrom; pub use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, DescribeAllTerminal, DescribeFamily, EnsureXcmOrigin, @@ -42,6 +40,7 @@ use bifrost_currencies::BasicCurrencyAdapter; use bifrost_runtime_common::{ currency_adapter::{BifrostDropAssets, DepositToAlternative, MultiCurrencyAdapter}, currency_converter::CurrencyIdConvert, + xcm_weight_trader::XcmWeightTrader, }; use cumulus_primitives_core::AggregateMessageOrigin; use frame_support::traits::TransformOrigin; @@ -51,7 +50,6 @@ use orml_xcm_support::{IsNativeConcrete, MultiNativeAsset}; use pallet_xcm::XcmPassthrough; use parachains_common::message_queue::{NarrowOriginToSibling, ParaIdToSibling}; use polkadot_runtime_common::xcm_sender::NoPriceForMessageDelivery; -use sp_core::bounded::BoundedVec; use xcm::v4::{prelude::*, Location}; use xcm_builder::{FrameTransactionalProcessor, TrailingSetTopicAsId, WithComputedOrigin}; @@ -66,7 +64,7 @@ parameter_types! { // XTokens pallet supports maximum number of assets to be transferred at a time pub const MaxAssetsForTransfer: usize = 2; // One XCM operation is 200_000_000 weight, cross-chain transfer ~= 2x of transfer = 3_000_000_000 - pub const UnitWeightCost: Weight = Weight::from_parts(200_000_000, 0); + pub const UnitWeightCost: Weight = Weight::from_parts(50_000_000, 0); // Maximum number of instructions that can be executed in one XCM message pub const MaxInstructions: u32 = 100; } @@ -137,128 +135,6 @@ pub type BifrostAssetTransactor = MultiCurrencyAdapter< DepositToAlternative, >; -parameter_types! { - pub KsmPerSecond: (AssetId, u128, u128) = (Location::parent().into(), ksm_per_second::(),0); - pub VksmPerSecond: (AssetId, u128,u128) = ( - Location::new( - 0, - [Junction::from(BoundedVec::try_from(CurrencyId::VToken(TokenSymbol::KSM).encode()).unwrap())], - ).into(), - ksm_per_second::(), - 0 - ); - pub VsksmPerSecond: (AssetId, u128,u128) = ( - Location::new( - 1, - [Parachain(SelfParaId::get()), Junction::from(BoundedVec::try_from(CurrencyId::VSToken(TokenSymbol::KSM).encode()).unwrap())] - ).into(), - ksm_per_second::(), - 0 - ); - pub VsksmNewPerSecond: (AssetId, u128,u128) = ( - Location::new( - 0, - [Junction::from(BoundedVec::try_from(CurrencyId::VSToken(TokenSymbol::KSM).encode()).unwrap())] - ).into(), - ksm_per_second::(), - 0 - ); - pub BncPerSecond: (AssetId, u128,u128) = ( - Location::new( - 1, - [Parachain(SelfParaId::get()), Junction::from(BoundedVec::try_from(NativeCurrencyId::get().encode()).unwrap())] - ).into(), - // BNC:KSM = 80:1 - ksm_per_second::() * 80, - 0 - ); - pub BncNewPerSecond: (AssetId, u128,u128) = ( - Location::new( - 0, - [Junction::from(BoundedVec::try_from(NativeCurrencyId::get().encode()).unwrap())] - ).into(), - // BNC:KSM = 80:1 - ksm_per_second::() * 80, - 0 - ); - - pub ZlkPerSecond: (AssetId, u128,u128) = ( - Location::new( - 1, - [Parachain(SelfParaId::get()), Junction::from(BoundedVec::try_from(CurrencyId::Token(TokenSymbol::ZLK).encode()).unwrap())] - ).into(), - // ZLK:KSM = 150:1 - //ZLK has a decimal of 18, while KSM is 12. - ksm_per_second::() * 150 * 1_000_000, - 0 - ); - pub ZlkNewPerSecond: (AssetId, u128,u128) = ( - Location::new( - 0, - [Junction::from(BoundedVec::try_from(CurrencyId::Token(TokenSymbol::ZLK).encode()).unwrap())] - ).into(), - // ZLK:KSM = 150:1 - //ZLK has a decimal of 18, while KSM is 12. - ksm_per_second::() * 150 * 1_000_000, - 0 - ); - pub KarPerSecond: (AssetId, u128,u128) = ( - Location::new( - 1, - [Parachain(KaruraChainId::get()), Junction::from(BoundedVec::try_from(vec![0,128u8]).unwrap())] - ).into(), - // KAR:KSM = 100:1 - ksm_per_second::() * 100, - 0 - ); - pub KusdPerSecond: (AssetId, u128,u128) = ( - Location::new( - 1, - [Parachain(KaruraChainId::get()), Junction::from(BoundedVec::try_from(vec![0,129u8]).unwrap())] - ).into(), - // kUSD:KSM = 400:1 - ksm_per_second::() * 400, - 0 - ); - pub PhaPerSecond: (AssetId, u128,u128) = ( - Location::new( - 1, - [Parachain(PhalaChainId::get())], - ).into(), - // PHA:KSM = 400:1 - ksm_per_second::() * 400, - 0 - ); - pub RmrkPerSecond: (AssetId, u128,u128) = ( - Location::new( - 1, - [Parachain(AssetHubChainId::get()), GeneralIndex(50)] - ).into(), - // rmrk:KSM = 10:1 - ksm_per_second::() * 10 / 100, //rmrk currency decimal as 10 - 0 - ); - pub RmrkNewPerSecond: (AssetId, u128,u128) = ( - Location::new( - 1, - [Parachain(AssetHubChainId::get()), PalletInstance(50), GeneralIndex(8)] - ).into(), - // rmrk:KSM = 10:1 - ksm_per_second::() * 10 / 100, //rmrk currency decimal as 10 - 0 - ); - pub MovrPerSecond: (AssetId, u128,u128) = ( - Location::new( - 1, - [Parachain(MoonriverChainId::get()), PalletInstance(10)] - ).into(), - // MOVR:KSM = 2.67:1 - ksm_per_second::() * 267 * 10_000, //movr currency decimal as 18 - 0 - ); - pub BasePerSecond: u128 = ksm_per_second::(); -} - pub struct ToTreasury; impl TakeRevenue for ToTreasury { fn take_revenue(revenue: Asset) { @@ -272,24 +148,6 @@ impl TakeRevenue for ToTreasury { } } -pub type Trader = ( - FixedRateOfFungible, - FixedRateOfFungible, - FixedRateOfFungible, - FixedRateOfFungible, - FixedRateOfFungible, - FixedRateOfFungible, - FixedRateOfFungible, - FixedRateOfFungible, - FixedRateOfFungible, - FixedRateOfFungible, - FixedRateOfFungible, - FixedRateOfFungible, - FixedRateOfFungible, - FixedRateOfFungible, - FixedRateOfAsset, -); - /// A call filter for the XCM Transact instruction. This is a temporary measure until we properly /// account for proof size weights. /// @@ -398,7 +256,9 @@ impl xcm_executor::Config for XcmConfig { type OriginConverter = XcmOriginToTransactDispatchOrigin; type ResponseHandler = PolkadotXcm; type SubscriptionService = PolkadotXcm; - type Trader = Trader; + type Trader = XcmWeightTrader, ToTreasury>; + // TODO: Implement XcmWeigher, using real Weight, currently per instruction Weight = + // Weight::from_parts(50_000_000, 0) type Weigher = FixedWeightBounds; type XcmSender = XcmRouter; type PalletInstancesInfo = AllPalletsWithSystem; diff --git a/runtime/bifrost-polkadot/Cargo.toml b/runtime/bifrost-polkadot/Cargo.toml index 0888dda20..5ec0b47e6 100644 --- a/runtime/bifrost-polkadot/Cargo.toml +++ b/runtime/bifrost-polkadot/Cargo.toml @@ -92,6 +92,7 @@ polkadot-runtime-common = { workspace = true } xcm = { workspace = true } xcm-builder = { workspace = true } xcm-executor = { workspace = true } +xcm-fee-payment-runtime-api = { workspace = true } # orml orml-tokens = { workspace = true } @@ -241,6 +242,7 @@ std = [ "xcm-builder/std", "xcm-executor/std", "xcm/std", + "xcm-fee-payment-runtime-api/std", "orml-tokens/std", "orml-traits/std", diff --git a/runtime/bifrost-polkadot/src/lib.rs b/runtime/bifrost-polkadot/src/lib.rs index 52a4249a6..13fbc7767 100644 --- a/runtime/bifrost-polkadot/src/lib.rs +++ b/runtime/bifrost-polkadot/src/lib.rs @@ -35,8 +35,8 @@ use bifrost_primitives::{ CommissionPalletId, FarmingBoostPalletId, FarmingGaugeRewardIssuerPalletId, FarmingKeeperPalletId, FarmingRewardIssuerPalletId, FeeSharePalletId, FlexibleFeePalletId, IncentivePalletId, IncentivePoolAccount, LendMarketPalletId, LiquidityAccount, - MerkleDirtributorPalletId, OraclePalletId, SlpEntrancePalletId, SlpExitPalletId, - SystemMakerPalletId, SystemStakingPalletId, TreasuryPalletId, + LocalBncLocation, MerkleDirtributorPalletId, OraclePalletId, SlpEntrancePalletId, + SlpExitPalletId, SystemMakerPalletId, SystemStakingPalletId, TreasuryPalletId, }; use cumulus_pallet_parachain_system::{RelayNumberStrictlyIncreases, RelaychainDataProvider}; pub use frame_support::{ @@ -78,7 +78,7 @@ mod evm; mod migration; pub mod weights; use bb_bnc::traits::BbBNCInterface; -use bifrost_asset_registry::{AssetIdMaps, FixedRateOfAsset}; +use bifrost_asset_registry::AssetIdMaps; pub use bifrost_primitives::{ traits::{ CheckSubAccount, FarmingInfo, VtokenMintingInterface, VtokenMintingOperator, @@ -128,16 +128,26 @@ use sp_runtime::{ transaction_validity::TransactionValidityError, }; use static_assertions::const_assert; -use xcm::{v3::MultiLocation, v4::prelude::*}; +use xcm::{ + v3::MultiLocation, v4::prelude::*, VersionedAssetId, VersionedAssets, VersionedLocation, + VersionedXcm, +}; pub use xcm_config::{BifrostTreasuryAccount, MultiCurrency}; use xcm_executor::{traits::QueryHandler, XcmExecutor}; pub mod governance; use crate::xcm_config::XcmRouter; +use bifrost_primitives::OraclePriceProvider; +use frame_support::weights::WeightToFee as _; use governance::{ custom_origins, CoreAdminOrCouncil, LiquidStaking, SALPAdmin, Spender, TechAdmin, TechAdminOrCouncil, }; +use xcm::IntoVersion; +use xcm_fee_payment_runtime_api::{ + dry_run::{CallDryRunEffects, Error as XcmDryRunApiError, XcmDryRunEffects}, + fees::Error as XcmPaymentApiError, +}; use bifrost_primitives::MoonbeamChainId; #[cfg(feature = "runtime-benchmarks")] @@ -2300,6 +2310,49 @@ impl fp_rpc::EthereumRuntimeRPCApi for Runtime { } } + impl xcm_fee_payment_runtime_api::fees::XcmPaymentApi for Runtime { + fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result, XcmPaymentApiError> { + let acceptable_assets = AssetRegistry::asset_ids(); + PolkadotXcm::query_acceptable_payment_assets(xcm_version, acceptable_assets) + } + + fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { + let asset = asset + .into_version(4) + .map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?; + let bnc_asset = VersionedAssetId::V4(LocalBncLocation::get().into()); + + if asset == bnc_asset { + // for native token + Ok(WeightToFee::weight_to_fee(&weight)) + } else { + let native_fee = WeightToFee::weight_to_fee(&weight); + let asset_location = &asset.try_as::().map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?.0; + let asset_currency = AssetIdMaps::::get_currency_id(&asset_location).ok_or(XcmPaymentApiError::AssetNotFound)?; + let asset_fee = Prices::get_oracle_amount_by_currency_and_amount_in(&bifrost_primitives::BNC, native_fee, &asset_currency).ok_or(XcmPaymentApiError::AssetNotFound)?.0; + Ok(asset_fee) + } + } + + fn query_xcm_weight(message: VersionedXcm<()>) -> Result { + PolkadotXcm::query_xcm_weight(message) + } + + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { + PolkadotXcm::query_delivery_fees(destination, message) + } + } + + impl xcm_fee_payment_runtime_api::dry_run::DryRunApi for Runtime { + fn dry_run_call(origin: OriginCaller, call: RuntimeCall) -> Result, XcmDryRunApiError> { + PolkadotXcm::dry_run_call::(origin, call) + } + + fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm) -> Result, XcmDryRunApiError> { + PolkadotXcm::dry_run_xcm::(origin_location, xcm) + } + } + impl bifrost_flexible_fee_rpc_runtime_api::FlexibleFeeRuntimeApi for Runtime { fn get_fee_token_and_amount(who: AccountId, fee: Balance,utx: ::Extrinsic) -> (CurrencyId, Balance) { let call = utx.0.function; diff --git a/runtime/bifrost-polkadot/src/xcm_config.rs b/runtime/bifrost-polkadot/src/xcm_config.rs index 69ab3fa92..7bb1b2760 100644 --- a/runtime/bifrost-polkadot/src/xcm_config.rs +++ b/runtime/bifrost-polkadot/src/xcm_config.rs @@ -24,8 +24,9 @@ use bifrost_primitives::{ CurrencyId, CurrencyIdMapping, EthereumLocation, NativeAssetFrom, PolkadotNetwork, PolkadotUniversalLocation, SelfLocation, TokenSymbol, DOT_TOKEN_ID, }; -use bifrost_runtime_common::currency_adapter::{ - BifrostDropAssets, DepositToAlternative, MultiCurrencyAdapter, +use bifrost_runtime_common::{ + currency_adapter::{BifrostDropAssets, DepositToAlternative, MultiCurrencyAdapter}, + xcm_weight_trader::XcmWeightTrader, }; use cumulus_primitives_core::AggregateMessageOrigin; pub use cumulus_primitives_core::ParaId; @@ -37,11 +38,8 @@ pub use orml_traits::{location::AbsoluteReserveProvider, parameter_type_with_key use orml_xcm_support::{IsNativeConcrete, MultiNativeAsset}; use pallet_xcm::XcmPassthrough; use parachains_common::message_queue::{NarrowOriginToSibling, ParaIdToSibling}; -use parity_scale_codec::Encode; pub use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::xcm_sender::NoPriceForMessageDelivery; -use sp_core::bounded::BoundedVec; -use sp_std::convert::TryFrom; use xcm::v4::{Asset, AssetId, Location}; pub use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, @@ -66,7 +64,7 @@ parameter_types! { // XTokens pallet supports maximum number of assets to be transferred at a time pub const MaxAssetsForTransfer: usize = 2; // One XCM operation is 200_000_000 weight, cross-chain transfer ~= 2x of transfer = 3_000_000_000 - pub const UnitWeightCost: Weight = Weight::from_parts(200_000_000, 0); + pub const UnitWeightCost: Weight = Weight::from_parts(50_000_000, 0); // Maximum number of instructions that can be executed in one XCM message pub const MaxInstructions: u32 = 100; } @@ -139,47 +137,6 @@ pub type BifrostAssetTransactor = MultiCurrencyAdapter< DepositToAlternative, >; -parameter_types! { - pub DotPerSecond: (AssetId,u128, u128) = (Location::parent().into(), dot_per_second::(),0); - pub BncPerSecond: (AssetId,u128, u128) = ( - Location::new( - 1, - [xcm::v4::Junction::Parachain(SelfParaId::get()), xcm::v4::Junction::from(BoundedVec::try_from(NativeCurrencyId::get().encode()).unwrap())], - ).into(), - // BNC:DOT = 80:1 - dot_per_second::() * 80, - 0 - ); - pub BncNewPerSecond: (AssetId,u128, u128) = ( - Location::new( - 0, - [xcm::v4::Junction::from(BoundedVec::try_from(NativeCurrencyId::get().encode()).unwrap())] - ).into(), - // BNC:DOT = 80:1 - dot_per_second::() * 80, - 0 - ); - pub ZlkPerSecond: (AssetId, u128,u128) = ( - Location::new( - 1, - [xcm::v4::Junction::Parachain(SelfParaId::get()), xcm::v4::Junction::from(BoundedVec::try_from(CurrencyId::Token(TokenSymbol::ZLK).encode()).unwrap())] - ).into(), - // ZLK:KSM = 150:1 - dot_per_second::() * 150 * 1_000_000, - 0 - ); - pub ZlkNewPerSecond: (AssetId, u128,u128) = ( - Location::new( - 0, - [xcm::v4::Junction::from(BoundedVec::try_from(CurrencyId::Token(TokenSymbol::ZLK).encode()).unwrap())] - ).into(), - // ZLK:KSM = 150:1 - dot_per_second::() * 150 * 1_000_000, - 0 - ); - pub BasePerSecond: u128 = dot_per_second::(); -} - pub struct ToTreasury; impl TakeRevenue for ToTreasury { fn take_revenue(revenue: Asset) { @@ -193,13 +150,6 @@ impl TakeRevenue for ToTreasury { } } -pub type Trader = ( - FixedRateOfFungible, - FixedRateOfFungible, - FixedRateOfFungible, - FixedRateOfAsset, -); - /// A call filter for the XCM Transact instruction. This is a temporary measure until we properly /// account for proof size weights. /// @@ -305,7 +255,9 @@ impl xcm_executor::Config for XcmConfig { type OriginConverter = XcmOriginToTransactDispatchOrigin; type ResponseHandler = PolkadotXcm; type SubscriptionService = PolkadotXcm; - type Trader = Trader; + type Trader = XcmWeightTrader, ToTreasury>; + // TODO: Implement XcmWeigher, using real Weight, currently per instruction Weight = + // Weight::from_parts(50_000_000, 0) type Weigher = FixedWeightBounds; type XcmSender = XcmRouter; type PalletInstancesInfo = AllPalletsWithSystem; diff --git a/runtime/common/src/currency_converter.rs b/runtime/common/src/currency_converter.rs index fd2276e04..eb7190d41 100644 --- a/runtime/common/src/currency_converter.rs +++ b/runtime/common/src/currency_converter.rs @@ -21,7 +21,7 @@ use bifrost_primitives::{CurrencyId, CurrencyIdMapping}; use cumulus_primitives_core::ParaId; use frame_support::traits::Get; use sp_runtime::traits::Convert; -use sp_std::{marker::PhantomData, prelude::*}; +use sp_std::marker::PhantomData; use xcm::{ latest::{AssetId, Location}, prelude::Fungible, @@ -34,7 +34,7 @@ impl, R: bifrost_asset_registry::Config> Convert { fn convert(id: CurrencyId) -> Option { - AssetIdMaps::::get_location(id) + AssetIdMaps::::get_location(&id) } } /// Convert Location to CurrencyId @@ -42,7 +42,7 @@ impl, R: bifrost_asset_registry::Config> Convert { fn convert(location: Location) -> Option { - AssetIdMaps::::get_currency_id(location.clone()) + AssetIdMaps::::get_currency_id(&location) } } /// Convert Asset to CurrencyId diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index c7fccfce8..9181be760 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -32,6 +32,7 @@ use sp_runtime::{traits::Bounded, FixedPointNumber, Perquintill}; pub mod constants; pub mod currency_adapter; pub mod currency_converter; +pub mod xcm_weight_trader; #[cfg(test)] mod tests; diff --git a/runtime/common/src/xcm_weight_trader.rs b/runtime/common/src/xcm_weight_trader.rs new file mode 100644 index 000000000..42bc9ba6e --- /dev/null +++ b/runtime/common/src/xcm_weight_trader.rs @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use bifrost_asset_registry::AssetMetadata; +use bifrost_primitives::{ + Balance, CurrencyId, CurrencyIdMapping, LocalBncLocation, OraclePriceProvider, BNC, +}; +use frame_support::weights::Weight; +use sp_std::marker::PhantomData; +use xcm::{ + latest::{Asset, AssetId, Location, XcmContext}, + prelude::{Fungible, XcmError}, +}; +use xcm_builder::TakeRevenue; +use xcm_executor::{traits::WeightTrader, AssetsInHolding}; + +pub struct XcmWeightTrader< + WeightToFee: frame_support::weights::WeightToFee, + Price: OraclePriceProvider, + CM: CurrencyIdMapping>, + R: TakeRevenue, +>(Weight, Option, PhantomData<(WeightToFee, Price, CM, R)>); + +impl< + WeightToFee: frame_support::weights::WeightToFee, + Price: OraclePriceProvider, + CM: CurrencyIdMapping>, + R: TakeRevenue, + > XcmWeightTrader +{ + fn compute_amount_to_charge( + weight: &Weight, + asset_location: &Location, + ) -> Result { + if *asset_location == LocalBncLocation::get() { + Ok(WeightToFee::weight_to_fee(weight)) + } else { + let bnc_amount = WeightToFee::weight_to_fee(weight); + let asset_currency_id = + CM::get_currency_id(asset_location).ok_or(XcmError::AssetNotFound)?; + let asset_amount = Price::get_oracle_amount_by_currency_and_amount_in( + &BNC, + bnc_amount, + &asset_currency_id, + ) + .ok_or(XcmError::Overflow)? + .0; + Ok(asset_amount) + } + } +} + +impl< + WeightToFee: frame_support::weights::WeightToFee, + Price: OraclePriceProvider, + CM: CurrencyIdMapping>, + R: TakeRevenue, + > WeightTrader for XcmWeightTrader +{ + fn new() -> Self { + Self(Weight::zero(), None, PhantomData) + } + + fn buy_weight( + &mut self, + weight: Weight, + payment: AssetsInHolding, + _context: &XcmContext, + ) -> Result { + log::trace!(target: "xcm-weight-trader", "buy_weight weight: {:?}, payment: {:?}", weight, payment); + + // only support first fungible assets now. + let first_asset = + payment.clone().fungible_assets_iter().next().ok_or(XcmError::AssetNotFound)?; + + match (first_asset.id, first_asset.fun) { + (AssetId(location), Fungible(_)) => { + log::trace!(target: "xcm::weight", "buy_weight location: {:?}", location); + let amount = Self::compute_amount_to_charge(&weight, &location)?; + + // We don't need to proceed if the amount is 0 + // For cases (specially tests) where the asset is very cheap with respect + // to the weight needed + if amount == 0 { + return Ok(payment); + } + + let required = Asset { fun: Fungible(amount), id: AssetId(location) }; + let unused = + payment.checked_sub(required.clone()).map_err(|_| XcmError::TooExpensive)?; + + self.0 = weight; + self.1 = Some(required); + + Ok(unused) + }, + _ => Err(XcmError::AssetNotFound), + } + } + + fn refund_weight(&mut self, actual_weight: Weight, context: &XcmContext) -> Option { + log::trace!( + target: "xcm-weight-trader", + "refund_weight weight: {:?}, context: {:?}, available weight: {:?}, asset: {:?}", + actual_weight, + context, + self.0, + self.1 + ); + + if let Some(Asset { fun: Fungible(initial_amount), id: AssetId(location) }) = self.1.take() + { + if actual_weight == self.0 { + self.1 = Some(Asset { fun: Fungible(initial_amount), id: AssetId(location) }); + None + } else { + let weight = actual_weight.min(self.0); + let amount = + Self::compute_amount_to_charge(&weight, &location).unwrap_or(Balance::MAX); + let final_amount = amount.min(initial_amount); + let amount_to_refund = initial_amount.saturating_sub(final_amount); + self.0 -= weight; + self.1 = Some(Asset { fun: Fungible(final_amount), id: AssetId(location.clone()) }); + log::trace!( + target: "xcm-weight-trader", + "refund_weight amount to refund: {:?}", + amount_to_refund + ); + Some(Asset { fun: Fungible(amount_to_refund), id: AssetId(location) }) + } + } else { + None + } + } +} + +impl< + WeightToFee: frame_support::weights::WeightToFee, + Price: OraclePriceProvider, + CM: CurrencyIdMapping>, + R: TakeRevenue, + > Drop for XcmWeightTrader +{ + fn drop(&mut self) { + log::trace!(target: "xcm-weight-trader", "take revenue, weight: {:?}, asset: {:?}", self.0, self.1); + if let Some(asset) = self.1.take() { + R::take_revenue(asset); + } + } +}