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 all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions Cargo.lock

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

4 changes: 0 additions & 4 deletions modules/asset-registry/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ frame-system = { git = "https://github.com/paritytech/substrate", branch = "polk
primitives = { package = "acala-primitives", path = "../../primitives", default-features = false }

xcm = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.22", default-features = false }
xcm-builder = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.22", default-features = false }
xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.22", default-features = false }

module-support = { path = "../support", default-features = false }

Expand Down Expand Up @@ -46,8 +44,6 @@ std = [
"frame-system/std",
"primitives/std",
"xcm/std",
"xcm-builder/std",
"xcm-executor/std",
"module-support/std",
]
try-runtime = ["frame-support/try-runtime"]
124 changes: 3 additions & 121 deletions modules/asset-registry/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,9 @@ 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};
use module_support::{AssetIdMapping, BuyWeightRate, EVMBridge, Erc20InfoMapping, InvokeContext, Ratio};
use primitives::{
currency::{
AssetIds, AssetMetadata, CurrencyIdType, DexShare, DexShareType, Erc20Id, ForeignAssetId, Lease,
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 @@ -565,7 +559,7 @@ impl<T: Config> BuyWeightRate for BuyWeightRateOfForeignAsset<T>
where
BalanceOf<T>: Into<u128>,
{
fn calculate_rate(location: MultiLocation) -> Option<Rate> {
fn calculate_rate(location: MultiLocation) -> Option<Ratio> {
if let Some(CurrencyId::ForeignAsset(foreign_asset_id)) = Pallet::<T>::location_to_currency_ids(location) {
if let Some(asset_metadata) = Pallet::<T>::asset_metadatas(AssetIds::ForeignAssetId(foreign_asset_id)) {
let minimum_balance = asset_metadata.minimal_balance.into();
Expand All @@ -584,7 +578,7 @@ impl<T: Config> BuyWeightRate for BuyWeightRateOfErc20<T>
where
BalanceOf<T>: Into<u128>,
{
fn calculate_rate(location: MultiLocation) -> Option<Rate> {
fn calculate_rate(location: MultiLocation) -> Option<Ratio> {
match location {
MultiLocation {
parents: 0,
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
2 changes: 1 addition & 1 deletion modules/support/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,5 +190,5 @@ pub trait NomineesProvider<AccountId> {
}

pub trait BuyWeightRate {
fn calculate_rate(location: MultiLocation) -> Option<Rate>;
fn calculate_rate(location: MultiLocation) -> Option<Ratio>;
}
19 changes: 2 additions & 17 deletions modules/support/src/mocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.

#![allow(clippy::type_complexity)]
use crate::{AddressMapping, BuyWeightRate, CurrencyId, Erc20InfoMapping, Rate, TransactionPayment};
use crate::{AddressMapping, CurrencyId, Erc20InfoMapping, TransactionPayment};
use codec::Encode;
use frame_support::pallet_prelude::{DispatchClass, Pays, Weight};
use nutsfinance_stable_asset::{
Expand All @@ -30,12 +30,11 @@ use primitives::{
};
use sp_core::{crypto::AccountId32, H160};
use sp_io::hashing::blake2_256;
use sp_runtime::{traits::One, transaction_validity::TransactionValidityError, DispatchError, DispatchResult};
use sp_runtime::{transaction_validity::TransactionValidityError, DispatchError, DispatchResult};
use sp_std::{marker::PhantomData, vec::Vec};

#[cfg(feature = "std")]
use frame_support::traits::Imbalance;
use xcm::latest::MultiLocation;

pub struct MockAddressMapping;

Expand Down Expand Up @@ -409,17 +408,3 @@ impl<CurrencyId, Balance, AccountId, BlockNumber> StableAsset
unimplemented!()
}
}

pub struct MockNoneMinimumBalance;
impl BuyWeightRate for MockNoneMinimumBalance {
fn calculate_rate(_: MultiLocation) -> Option<Rate> {
None
}
}

pub struct MockFixedMinimumBalance;
impl BuyWeightRate for MockFixedMinimumBalance {
fn calculate_rate(_: MultiLocation) -> Option<Rate> {
Some(Rate::one())
}
}
4 changes: 0 additions & 4 deletions modules/transaction-payment/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ support = { package = "module-support", path = "../support", default-features =

orml-traits = { path = "../../orml/traits", default-features = false }
xcm = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.22", default-features = false }
xcm-builder = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.22", default-features = false }
xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.22", default-features = false }

[dev-dependencies]
sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.22" }
Expand All @@ -48,7 +46,5 @@ std = [
"support/std",
"orml-traits/std",
"xcm/std",
"xcm-builder/std",
"xcm-executor/std",
]
try-runtime = ["frame-support/try-runtime"]
98 changes: 8 additions & 90 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, Ratio, SwapLimit, TransactionPayment};
use xcm::opaque::latest::MultiLocation;

mod mock;
mod tests;
Expand Down Expand Up @@ -1074,94 +1069,17 @@ 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)>,
}

impl<T: Config, C, K: Get<u128>, R: TakeRevenue> WeightTrader for TransactionFeePoolTrader<T, C, K, R>
/// Calculate the exchange rate of token in transaction fee pool.
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> 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(),
)
} else {
None
}
fn calculate_rate(multi_location: MultiLocation) -> Option<Ratio> {
C::convert(multi_location).and_then(TokenExchangeRate::<T>::get)
}
}

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
Loading