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

refactor weight trader #2178

Merged
merged 9 commits into from
Jun 14, 2022
Merged
Show file tree
Hide file tree
Changes from 6 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 Cargo.lock

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

118 changes: 0 additions & 118 deletions modules/asset-registry/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ use frame_support::{
pallet_prelude::*,
traits::{Currency, EnsureOrigin},
transactional,
weights::constants::WEIGHT_PER_SECOND,
};
use frame_system::pallet_prelude::*;
use module_support::{AssetIdMapping, BuyWeightRate, EVMBridge, Erc20InfoMapping, InvokeContext, Rate};
Expand All @@ -50,15 +49,10 @@ use scale_info::prelude::format;
use sp_runtime::{traits::One, ArithmeticError, FixedPointNumber, FixedU128};
use sp_std::{boxed::Box, vec::Vec};

// NOTE:v1::MultiLocation is used in storages, we would need to do migration if upgrade the
// MultiLocation in the future.
use xcm::opaque::latest::{prelude::XcmError, AssetId, Fungibility::Fungible, MultiAsset};
use xcm::{
v1::{Junction, Junctions::*, MultiLocation},
VersionedMultiLocation,
};
use xcm_builder::TakeRevenue;
use xcm_executor::{traits::WeightTrader, Assets};

mod mock;
mod tests;
Expand Down Expand Up @@ -613,118 +607,6 @@ where
}
}

/// 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 FixedRateOfAssetRegistry<FixedRate: Get<u128>, R: TakeRevenue, M: BuyWeightRate> {
weight: Weight,
amount: u128,
ed_ratio: FixedU128,
multi_location: Option<MultiLocation>,
_marker: PhantomData<(FixedRate, R, M)>,
}

impl<FixedRate: Get<u128>, R: TakeRevenue, M: BuyWeightRate> WeightTrader
for FixedRateOfAssetRegistry<FixedRate, R, M>
{
fn new() -> Self {
Self {
weight: 0,
amount: 0,
ed_ratio: Default::default(),
multi_location: None,
_marker: PhantomData,
}
}

fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result<Assets, XcmError> {
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))?;

if let AssetId::Concrete(ref multi_location) = asset_id {
log::debug!(target: "asset-registry::weight", "buy_weight multi_location: {:?}", multi_location);

if let Some(ed_ratio) = M::calculate_rate(multi_location.clone()) {
// The WEIGHT_PER_SECOND is non-zero.
let weight_ratio = FixedU128::saturating_from_rational(weight as u128, WEIGHT_PER_SECOND as u128);
let amount = ed_ratio.saturating_mul_int(weight_ratio.saturating_mul_int(FixedRate::get()));

let required = MultiAsset {
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);
self.amount = self.amount.saturating_add(amount);
self.ed_ratio = ed_ratio;
self.multi_location = Some(multi_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) -> Option<MultiAsset> {
log::trace!(
target: "asset-registry::weight", "refund_weight weight: {:?}, weight: {:?}, amount: {:?}, ed_ratio: {:?}, multi_location: {:?}",
weight, self.weight, self.amount, self.ed_ratio, self.multi_location
);
let weight = weight.min(self.weight);
let weight_ratio = FixedU128::saturating_from_rational(weight as u128, WEIGHT_PER_SECOND as u128);
let amount = self
.ed_ratio
.saturating_mul_int(weight_ratio.saturating_mul_int(FixedRate::get()));

self.weight = self.weight.saturating_sub(weight);
self.amount = self.amount.saturating_sub(amount);

log::trace!(target: "asset-registry::weight", "refund_weight amount: {:?}", amount);
if amount > 0 && self.multi_location.is_some() {
Some(
(
self.multi_location.as_ref().expect("checked is non-empty; qed").clone(),
amount,
)
.into(),
)
} else {
None
}
}
}

impl<FixedRate: Get<u128>, R: TakeRevenue, M: BuyWeightRate> Drop for FixedRateOfAssetRegistry<FixedRate, R, M> {
fn drop(&mut self) {
log::trace!(target: "asset-registry::weight", "take revenue, weight: {:?}, amount: {:?}, multi_location: {:?}", self.weight, self.amount, self.multi_location);
if self.amount > 0 && self.multi_location.is_some() {
R::take_revenue(
(
self.multi_location.as_ref().expect("checked is non-empty; qed").clone(),
self.amount,
)
.into(),
);
}
}
}

pub struct EvmErc20InfoMapping<T>(sp_std::marker::PhantomData<T>);

impl<T: Config> EvmErc20InfoMapping<T> {
Expand Down
94 changes: 8 additions & 86 deletions modules/transaction-payment/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,7 @@ use frame_support::{
WithdrawReasons,
},
transactional,
weights::{
constants::WEIGHT_PER_SECOND, DispatchInfo, GetDispatchInfo, Pays, PostDispatchInfo, WeightToFeeCoefficient,
WeightToFeePolynomial,
},
weights::{DispatchInfo, GetDispatchInfo, Pays, PostDispatchInfo, WeightToFeeCoefficient, WeightToFeePolynomial},
BoundedVec, PalletId,
};
use frame_system::pallet_prelude::*;
Expand All @@ -59,10 +56,8 @@ use sp_runtime::{
FixedPointNumber, FixedPointOperand, MultiSignature, Percent, Perquintill,
};
use sp_std::prelude::*;
use support::{DEXManager, PriceProvider, Ratio, SwapLimit, TransactionPayment};
use xcm::opaque::latest::{prelude::XcmError, AssetId, Fungibility::Fungible, MultiAsset, MultiLocation};
use xcm_builder::TakeRevenue;
use xcm_executor::{traits::WeightTrader, Assets};
use support::{BuyWeightRate, DEXManager, PriceProvider, Rate, Ratio, SwapLimit, TransactionPayment};
use xcm::opaque::latest::MultiLocation;

mod mock;
mod tests;
Expand Down Expand Up @@ -1074,94 +1069,21 @@ where
}
}

/// `WeightTrader` implementation used for `Trader`, the `rate` is read from storage,
/// and `token_per_second` is calculated by `rate` * `native_asset_per_second`.
pub struct TransactionFeePoolTrader<T, C, K: Get<u128>, R: TakeRevenue> {
weight: Weight,
amount: u128,
asset_location: Option<MultiLocation>,
asset_per_second: u128,
_marker: PhantomData<(T, C, K, R)>,
}
pub struct BuyWeightRateOfTransactionFeePool<T, C>(sp_std::marker::PhantomData<(T, C)>);
zqhxuyuan marked this conversation as resolved.
Show resolved Hide resolved

impl<T: Config, C, K: Get<u128>, R: TakeRevenue> WeightTrader for TransactionFeePoolTrader<T, C, K, R>
impl<T: Config, C> BuyWeightRate for BuyWeightRateOfTransactionFeePool<T, C>
where
C: Convert<MultiLocation, Option<CurrencyId>>,
{
fn new() -> Self {
Self {
weight: 0,
amount: 0,
asset_location: None,
asset_per_second: 0,
_marker: Default::default(),
}
}

fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result<Assets, XcmError> {
// only support first fungible assets now.
let asset_id = payment
.fungible
.iter()
.next()
.map_or(Err(XcmError::TooExpensive), |v| Ok(v.0))?;

if let AssetId::Concrete(ref multi_location) = asset_id.clone() {
if let Some(token_id) = C::convert(multi_location.clone()) {
if let Some(rate) = TokenExchangeRate::<T>::get(token_id) {
// calculate the amount of fungible asset.
let weight_ratio = Ratio::saturating_from_rational(weight as u128, WEIGHT_PER_SECOND as u128);
let asset_per_second = rate.saturating_mul_int(K::get());
let amount = weight_ratio.saturating_mul_int(asset_per_second);
let required = MultiAsset {
id: asset_id.clone(),
fun: Fungible(amount),
};
let unused = payment.checked_sub(required).map_err(|_| XcmError::TooExpensive)?;
self.weight = self.weight.saturating_add(weight);
self.amount = self.amount.saturating_add(amount);
self.asset_location = Some(multi_location.clone());
self.asset_per_second = asset_per_second;
return Ok(unused);
}
}
}
Err(XcmError::TooExpensive)
}

fn refund_weight(&mut self, weight: Weight) -> Option<MultiAsset> {
let weight = weight.min(self.weight);
let weight_ratio = Ratio::saturating_from_rational(weight as u128, WEIGHT_PER_SECOND as u128);
let amount = weight_ratio.saturating_mul_int(self.asset_per_second);
self.weight = self.weight.saturating_sub(weight);
self.amount = self.amount.saturating_sub(amount);
if amount > 0 && self.asset_location.is_some() {
Some(
(
self.asset_location.as_ref().expect("checked is non-empty; qed").clone(),
amount,
)
.into(),
)
fn calculate_rate(multi_location: MultiLocation) -> Option<Rate> {
if let Some(token_id) = C::convert(multi_location) {
zqhxuyuan marked this conversation as resolved.
Show resolved Hide resolved
TokenExchangeRate::<T>::get(token_id)
} else {
None
}
}
}

impl<T, C, K: Get<u128>, R: TakeRevenue> Drop for TransactionFeePoolTrader<T, C, K, R> {
fn drop(&mut self) {
if self.amount > 0 && self.asset_location.is_some() {
R::take_revenue(
(
self.asset_location.as_ref().expect("checked is non-empty; qed").clone(),
self.amount,
)
.into(),
);
}
}
}
impl<T> Convert<Weight, PalletBalanceOf<T>> for Pallet<T>
where
T: Config,
Expand Down
33 changes: 0 additions & 33 deletions modules/transaction-payment/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1362,39 +1362,6 @@ impl Convert<MultiLocation, Option<CurrencyId>> for CurrencyIdConvert {
}
}

#[test]
fn period_rate_buy_refund_weight_works() {
parameter_types! {
pub const NativePerSecond: u128 = 8_000_000_000_000;
}
builder_with_dex_and_fee_pool(true).execute_with(|| {
let mock_weight: Weight = 200_000_000;
let dot_rate = TokenExchangeRate::<Runtime>::get(DOT);
let usd_rate = TokenExchangeRate::<Runtime>::get(AUSD);
assert_eq!(dot_rate, Some(Ratio::saturating_from_rational(1, 10)));
assert_eq!(usd_rate, Some(Ratio::saturating_from_rational(10, 1)));

// 1DOT=10KAR, rate=DOT/KAR=1/10, rate=0.1, amount=rate*kar_per_second*weight,
// amount=8*weight*rate=0.8*weight=160_000_000
let asset: MultiAsset = ((0, X1(GeneralKey(DOT.encode()))), 170_000_000).into();
let assets: Assets = asset.into();
let mut trader = TransactionFeePoolTrader::<Runtime, CurrencyIdConvert, NativePerSecond, ()>::new();
let unused = trader.buy_weight(mock_weight, assets);
let expect_asset: MultiAsset = ((0, X1(GeneralKey(DOT.encode()))), 10_000_000).into();
assert_eq!(unused.unwrap(), expect_asset.into());
assert_eq!(trader.amount, 160_000_000);

// 1KAR=10AUSD, rate=AUSD/KAR=10, rate=10, amount=8*weight*rate=80*weight=16_000_000_000
let asset: MultiAsset = ((0, X1(GeneralKey(AUSD.encode()))), 17_000_000_000).into();
let assets: Assets = asset.into();
let mut trader = TransactionFeePoolTrader::<Runtime, CurrencyIdConvert, NativePerSecond, ()>::new();
let unused = trader.buy_weight(mock_weight, assets);
let expect_asset: MultiAsset = ((0, X1(GeneralKey(AUSD.encode()))), 1_000_000_000).into();
assert_eq!(unused.unwrap(), expect_asset.into());
assert_eq!(trader.amount, 16_000_000_000);
});
}

#[test]
fn swap_from_pool_not_enough_currency() {
builder_with_dex_and_fee_pool(true).execute_with(|| {
Expand Down
Loading