From 39935e96c255657f2d41125cbe4328a944bd2807 Mon Sep 17 00:00:00 2001 From: muharem Date: Fri, 15 Sep 2023 12:28:22 +0200 Subject: [PATCH 01/98] move swap trait and impl into separate module --- substrate/frame/asset-conversion/src/lib.rs | 62 ++--------- substrate/frame/asset-conversion/src/swap.rs | 101 ++++++++++++++++++ substrate/frame/asset-conversion/src/types.rs | 37 ------- 3 files changed, 110 insertions(+), 90 deletions(-) create mode 100644 substrate/frame/asset-conversion/src/swap.rs diff --git a/substrate/frame/asset-conversion/src/lib.rs b/substrate/frame/asset-conversion/src/lib.rs index 8d811473e861..a3f5159743ec 100644 --- a/substrate/frame/asset-conversion/src/lib.rs +++ b/substrate/frame/asset-conversion/src/lib.rs @@ -57,15 +57,18 @@ use frame_support::traits::{DefensiveOption, Incrementable}; #[cfg(feature = "runtime-benchmarks")] mod benchmarking; - -mod types; -pub mod weights; - +#[cfg(test)] +mod mock; +mod swap; #[cfg(test)] mod tests; +mod types; +pub mod weights; -#[cfg(test)] -mod mock; +pub use pallet::*; +pub use swap::Swap; +pub use types::*; +pub use weights::WeightInfo; use codec::Codec; use frame_support::{ @@ -76,7 +79,6 @@ use frame_system::{ ensure_signed, pallet_prelude::{BlockNumberFor, OriginFor}, }; -pub use pallet::*; use sp_arithmetic::traits::Unsigned; use sp_runtime::{ traits::{ @@ -85,8 +87,6 @@ use sp_runtime::{ DispatchError, }; use sp_std::prelude::*; -pub use types::*; -pub use weights::WeightInfo; #[frame_support::pallet] pub mod pallet { @@ -1238,50 +1238,6 @@ pub mod pallet { } } -impl Swap for Pallet { - fn swap_exact_tokens_for_tokens( - sender: T::AccountId, - path: Vec, - amount_in: T::HigherPrecisionBalance, - amount_out_min: Option, - send_to: T::AccountId, - keep_alive: bool, - ) -> Result { - let path = path.try_into().map_err(|_| Error::::PathError)?; - let amount_out_min = amount_out_min.map(Self::convert_hpb_to_asset_balance).transpose()?; - let amount_out = Self::do_swap_exact_tokens_for_tokens( - sender, - path, - Self::convert_hpb_to_asset_balance(amount_in)?, - amount_out_min, - send_to, - keep_alive, - )?; - Ok(amount_out.into()) - } - - fn swap_tokens_for_exact_tokens( - sender: T::AccountId, - path: Vec, - amount_out: T::HigherPrecisionBalance, - amount_in_max: Option, - send_to: T::AccountId, - keep_alive: bool, - ) -> Result { - let path = path.try_into().map_err(|_| Error::::PathError)?; - let amount_in_max = amount_in_max.map(Self::convert_hpb_to_asset_balance).transpose()?; - let amount_in = Self::do_swap_tokens_for_exact_tokens( - sender, - path, - Self::convert_hpb_to_asset_balance(amount_out)?, - amount_in_max, - send_to, - keep_alive, - )?; - Ok(amount_in.into()) - } -} - sp_api::decl_runtime_apis! { /// This runtime api allows people to query the size of the liquidity pools /// and quote prices for swaps. diff --git a/substrate/frame/asset-conversion/src/swap.rs b/substrate/frame/asset-conversion/src/swap.rs new file mode 100644 index 000000000000..7c4468d24b7c --- /dev/null +++ b/substrate/frame/asset-conversion/src/swap.rs @@ -0,0 +1,101 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Traits and implementations for swap between the various asset classes. + +use super::*; + +/// Trait for providing methods to swap between the various asset classes. +pub trait Swap { + /// Swap exactly `amount_in` of asset `path[0]` for asset `path[1]`. + /// If an `amount_out_min` is specified, it will return an error if it is unable to acquire + /// the amount desired. + /// + /// Withdraws the `path[0]` asset from `sender`, deposits the `path[1]` asset to `send_to`, + /// respecting `keep_alive`. + /// + /// If successful, returns the amount of `path[1]` acquired for the `amount_in`. + fn swap_exact_tokens_for_tokens( + sender: AccountId, + path: Vec, + amount_in: Balance, + amount_out_min: Option, + send_to: AccountId, + keep_alive: bool, + ) -> Result; + + /// Take the `path[0]` asset and swap some amount for `amount_out` of the `path[1]`. If an + /// `amount_in_max` is specified, it will return an error if acquiring `amount_out` would be + /// too costly. + /// + /// Withdraws `path[0]` asset from `sender`, deposits `path[1]` asset to `send_to`, + /// respecting `keep_alive`. + /// + /// If successful returns the amount of the `path[0]` taken to provide `path[1]`. + fn swap_tokens_for_exact_tokens( + sender: AccountId, + path: Vec, + amount_out: Balance, + amount_in_max: Option, + send_to: AccountId, + keep_alive: bool, + ) -> Result; +} + +impl Swap for Pallet { + fn swap_exact_tokens_for_tokens( + sender: T::AccountId, + path: Vec, + amount_in: T::HigherPrecisionBalance, + amount_out_min: Option, + send_to: T::AccountId, + keep_alive: bool, + ) -> Result { + let path = path.try_into().map_err(|_| Error::::PathError)?; + let amount_out_min = amount_out_min.map(Self::convert_hpb_to_asset_balance).transpose()?; + let amount_out = Self::do_swap_exact_tokens_for_tokens( + sender, + path, + Self::convert_hpb_to_asset_balance(amount_in)?, + amount_out_min, + send_to, + keep_alive, + )?; + Ok(amount_out.into()) + } + + fn swap_tokens_for_exact_tokens( + sender: T::AccountId, + path: Vec, + amount_out: T::HigherPrecisionBalance, + amount_in_max: Option, + send_to: T::AccountId, + keep_alive: bool, + ) -> Result { + let path = path.try_into().map_err(|_| Error::::PathError)?; + let amount_in_max = amount_in_max.map(Self::convert_hpb_to_asset_balance).transpose()?; + let amount_in = Self::do_swap_tokens_for_exact_tokens( + sender, + path, + Self::convert_hpb_to_asset_balance(amount_out)?, + amount_in_max, + send_to, + keep_alive, + )?; + Ok(amount_in.into()) + } +} diff --git a/substrate/frame/asset-conversion/src/types.rs b/substrate/frame/asset-conversion/src/types.rs index ffdc63ce0ce7..0bec61705c11 100644 --- a/substrate/frame/asset-conversion/src/types.rs +++ b/substrate/frame/asset-conversion/src/types.rs @@ -83,43 +83,6 @@ where } } -/// Trait for providing methods to swap between the various asset classes. -pub trait Swap { - /// Swap exactly `amount_in` of asset `path[0]` for asset `path[1]`. - /// If an `amount_out_min` is specified, it will return an error if it is unable to acquire - /// the amount desired. - /// - /// Withdraws the `path[0]` asset from `sender`, deposits the `path[1]` asset to `send_to`, - /// respecting `keep_alive`. - /// - /// If successful, returns the amount of `path[1]` acquired for the `amount_in`. - fn swap_exact_tokens_for_tokens( - sender: AccountId, - path: Vec, - amount_in: Balance, - amount_out_min: Option, - send_to: AccountId, - keep_alive: bool, - ) -> Result; - - /// Take the `path[0]` asset and swap some amount for `amount_out` of the `path[1]`. If an - /// `amount_in_max` is specified, it will return an error if acquiring `amount_out` would be - /// too costly. - /// - /// Withdraws `path[0]` asset from `sender`, deposits `path[1]` asset to `send_to`, - /// respecting `keep_alive`. - /// - /// If successful returns the amount of the `path[0]` taken to provide `path[1]`. - fn swap_tokens_for_exact_tokens( - sender: AccountId, - path: Vec, - amount_out: Balance, - amount_in_max: Option, - send_to: AccountId, - keep_alive: bool, - ) -> Result; -} - /// An implementation of MultiAssetId that can be either Native or an asset. #[derive(Decode, Encode, Default, MaxEncodedLen, TypeInfo, Clone, Copy, Debug)] pub enum NativeOrAssetId From d8ab7c104fe02b0aca89c43f82df2245b4cabec7 Mon Sep 17 00:00:00 2001 From: muharem Date: Fri, 15 Sep 2023 12:51:45 +0200 Subject: [PATCH 02/98] restructure imports --- substrate/frame/asset-conversion/src/lib.rs | 47 ++++++++------------- 1 file changed, 18 insertions(+), 29 deletions(-) diff --git a/substrate/frame/asset-conversion/src/lib.rs b/substrate/frame/asset-conversion/src/lib.rs index a3f5159743ec..bdf25c175821 100644 --- a/substrate/frame/asset-conversion/src/lib.rs +++ b/substrate/frame/asset-conversion/src/lib.rs @@ -72,44 +72,33 @@ pub use weights::WeightInfo; use codec::Codec; use frame_support::{ - ensure, - traits::tokens::{AssetId, Balance}, -}; -use frame_system::{ - ensure_signed, - pallet_prelude::{BlockNumberFor, OriginFor}, + traits::{ + fungible::{Inspect as InspectFungible, Mutate as MutateFungible}, + fungibles::{Create, Inspect, Mutate}, + tokens::{ + AssetId, Balance, + Fortitude::Polite, + Precision::Exact, + Preservation::{Expendable, Preserve}, + }, + AccountTouch, ContainsPair, + }, + BoundedBTreeSet, PalletId, }; -use sp_arithmetic::traits::Unsigned; use sp_runtime::{ traits::{ - CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Ensure, MaybeDisplay, TrailingZeroInput, + CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Ensure, IntegerSquareRoot, MaybeDisplay, + One, TrailingZeroInput, Zero, }, - DispatchError, + DispatchError, Saturating, }; -use sp_std::prelude::*; #[frame_support::pallet] pub mod pallet { use super::*; - use frame_support::{ - pallet_prelude::*, - traits::{ - fungible::{Inspect as InspectFungible, Mutate as MutateFungible}, - fungibles::{Create, Inspect, Mutate}, - tokens::{ - Fortitude::Polite, - Precision::Exact, - Preservation::{Expendable, Preserve}, - }, - AccountTouch, ContainsPair, - }, - BoundedBTreeSet, PalletId, - }; - use sp_arithmetic::Permill; - use sp_runtime::{ - traits::{IntegerSquareRoot, One, Zero}, - Saturating, - }; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + use sp_arithmetic::{traits::Unsigned, Permill}; #[pallet::pallet] pub struct Pallet(_); From 6d6cc181d861624f008303c640ed01ea4b2e7fa2 Mon Sep 17 00:00:00 2001 From: muharem Date: Wed, 20 Sep 2023 17:46:07 +0200 Subject: [PATCH 03/98] transfer swap --- substrate/frame/asset-conversion/src/lib.rs | 280 +++++++++++------- substrate/frame/asset-conversion/src/types.rs | 79 +++++ 2 files changed, 260 insertions(+), 99 deletions(-) diff --git a/substrate/frame/asset-conversion/src/lib.rs b/substrate/frame/asset-conversion/src/lib.rs index bdf25c175821..926c1790e871 100644 --- a/substrate/frame/asset-conversion/src/lib.rs +++ b/substrate/frame/asset-conversion/src/lib.rs @@ -53,7 +53,6 @@ //! (This can be run against the kitchen sync node in the `node` folder of this repo.) #![deny(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] -use frame_support::traits::{DefensiveOption, Incrementable}; #[cfg(feature = "runtime-benchmarks")] mod benchmarking; @@ -73,15 +72,18 @@ pub use weights::WeightInfo; use codec::Codec; use frame_support::{ traits::{ - fungible::{Inspect as InspectFungible, Mutate as MutateFungible}, - fungibles::{Create, Inspect, Mutate}, + fungible::{ + Balanced as BalancedFungible, Credit as CreditFungible, Inspect as InspectFungible, + Mutate as MutateFungible, + }, + fungibles::{Balanced, Create, Credit as CreditFungibles, Inspect, Mutate}, tokens::{ AssetId, Balance, Fortitude::Polite, Precision::Exact, Preservation::{Expendable, Preserve}, }, - AccountTouch, ContainsPair, + AccountTouch, ContainsPair, Imbalance, Incrementable, }, BoundedBTreeSet, PalletId, }; @@ -90,7 +92,7 @@ use sp_runtime::{ CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Ensure, IntegerSquareRoot, MaybeDisplay, One, TrailingZeroInput, Zero, }, - DispatchError, Saturating, + BoundedVec, DispatchError, Saturating, }; #[frame_support::pallet] @@ -110,7 +112,8 @@ pub mod pallet { /// Currency type that this works on. type Currency: InspectFungible - + MutateFungible; + + MutateFungible + + BalancedFungible; /// The `Currency::Balance` type of the native currency. type Balance: Balance; @@ -151,7 +154,8 @@ pub mod pallet { type Assets: Inspect + Mutate + AccountTouch - + ContainsPair; + + ContainsPair + + Balanced; /// Registry for the lp tokens. Ideally only this pallet should have create permissions on /// the assets. @@ -350,10 +354,8 @@ pub mod pallet { NonUniquePath, /// It was not possible to get or increment the Id of the pool. IncorrectPoolAssetId, - /// Unable to find an element in an array/vec that should have one-to-one correspondence - /// with another. For example, an array of assets constituting a `path` should have a - /// corresponding array of `amounts` along the path. - CorrespondenceError, + /// Account cannot exist with the funds that would be given. + BelowMinimum, } #[pallet::hooks] @@ -702,6 +704,9 @@ pub mod pallet { /// respecting `keep_alive`. /// /// If successful, returns the amount of `path[1]` acquired for the `amount_in`. + /// + /// Note: + /// * this function might modify storage even if an error occurs. pub fn do_swap_exact_tokens_for_tokens( sender: T::AccountId, path: BoundedVec, @@ -716,11 +721,9 @@ pub mod pallet { } Self::validate_swap_path(&path)?; + let path = Self::balance_path_from_amount_in(amount_in, path)?; - let amounts = Self::get_amounts_out(&amount_in, &path)?; - let amount_out = - *amounts.last().defensive_ok_or("get_amounts_out() returned an empty result")?; - + let amount_out = path.last().map(|(_, a)| a.clone()).ok_or(Error::::InvalidPath)?; if let Some(amount_out_min) = amount_out_min { ensure!( amount_out >= amount_out_min, @@ -728,7 +731,15 @@ pub mod pallet { ); } - Self::do_swap(sender, &amounts, path, send_to, keep_alive)?; + Self::transfer_swap(&sender, &path, &send_to, keep_alive)?; + + Self::deposit_event(Event::SwapExecuted { + who: sender, + send_to, + amount_in, + amount_out, + path: BoundedVec::truncate_from(path.into_iter().map(|(a, _)| a).collect()), + }); Ok(amount_out) } @@ -740,6 +751,9 @@ pub mod pallet { /// respecting `keep_alive`. /// /// If successful returns the amount of the `path[0]` taken to provide `path[1]`. + /// + /// Note: + /// * this function might modify storage even if an error occurs. pub fn do_swap_tokens_for_exact_tokens( sender: T::AccountId, path: BoundedVec, @@ -754,11 +768,9 @@ pub mod pallet { } Self::validate_swap_path(&path)?; + let path = Self::balance_path_from_amount_out(amount_out, path)?; - let amounts = Self::get_amounts_in(&amount_out, &path)?; - let amount_in = - *amounts.first().defensive_ok_or("get_amounts_in() returned an empty result")?; - + let amount_in = path.first().map(|(_, a)| a.clone()).ok_or(Error::::InvalidPath)?; if let Some(amount_in_max) = amount_in_max { ensure!( amount_in <= amount_in_max, @@ -766,7 +778,16 @@ pub mod pallet { ); } - Self::do_swap(sender, &amounts, path, send_to, keep_alive)?; + Self::transfer_swap(&sender, &path, &send_to, keep_alive)?; + + Self::deposit_event(Event::SwapExecuted { + who: sender, + send_to, + amount_in, + amount_out, + path: BoundedVec::truncate_from(path.into_iter().map(|(a, _)| a).collect()), + }); + Ok(amount_in) } @@ -809,6 +830,40 @@ pub mod pallet { result } + /// The balance of `who` is increased in order to counter `credit`. If the whole of `credit` + /// cannot be countered, then nothing is changed and the original `credit` is returned in an + /// `Err`. + fn resolve(who: &T::AccountId, credit: Credit) -> Result<(), Credit> { + match credit { + Credit::Native(c) => T::Currency::resolve(who, c).map_err(|c| c.into()), + Credit::Asset(c) => T::Assets::resolve(who, c).map_err(|c| c.into()), + } + } + + /// Removes `value` balance of `asset` from `who` account if possible. + fn withdraw( + asset: &T::MultiAssetId, + who: &T::AccountId, + value: T::AssetBalance, + keep_alive: bool, + ) -> Result, DispatchError> { + let preservation = match keep_alive { + true => Preserve, + false => Expendable, + }; + match T::MultiAssetIdConverter::try_convert(asset) { + MultiAssetIdConversionResult::Converted(asset) => + T::Assets::withdraw(asset, who, value, Exact, preservation, Polite) + .map(|c| c.into()), + MultiAssetIdConversionResult::Native => { + let value = Self::convert_asset_balance_to_native_balance(value)?; + T::Currency::withdraw(who, value, Exact, preservation, Polite).map(|c| c.into()) + }, + MultiAssetIdConversionResult::Unsupported(_) => + Err(Error::::UnsupportedAsset.into()), + } + } + /// Convert a `Balance` type to an `AssetBalance`. pub(crate) fn convert_native_balance_to_asset_balance( amount: T::Balance, @@ -834,63 +889,80 @@ pub mod pallet { amount.try_into().map_err(|_| Error::::Overflow) } - /// Swap assets along a `path`, depositing in `send_to`. - pub(crate) fn do_swap( - sender: T::AccountId, - amounts: &Vec, - path: BoundedVec, - send_to: T::AccountId, + /// Swap assets along the `path`, withdrawing from `sender` and depositing in `send_to`. + /// + /// Note: + /// * this function might modify storage even if an error occurs. + /// * it's assumed that the provided `path` is valid. + fn transfer_swap( + sender: &T::AccountId, + path: &BalancePath, + send_to: &T::AccountId, keep_alive: bool, ) -> Result<(), DispatchError> { - ensure!(amounts.len() > 1, Error::::CorrespondenceError); - if let Some([asset1, asset2]) = &path.get(0..2) { - let pool_id = Self::get_pool_id(asset1.clone(), asset2.clone()); - let pool_account = Self::get_pool_account(&pool_id); - // amounts should always contain a corresponding element to path. - let first_amount = amounts.first().ok_or(Error::::CorrespondenceError)?; - - Self::transfer(asset1, &sender, &pool_account, *first_amount, keep_alive)?; - - let mut i = 0; - let path_len = path.len() as u32; - for assets_pair in path.windows(2) { - if let [asset1, asset2] = assets_pair { - let pool_id = Self::get_pool_id(asset1.clone(), asset2.clone()); - let pool_account = Self::get_pool_account(&pool_id); - - let amount_out = - amounts.get((i + 1) as usize).ok_or(Error::::CorrespondenceError)?; - - let to = if i < path_len - 2 { - let asset3 = path.get((i + 2) as usize).ok_or(Error::::PathError)?; - Self::get_pool_account(&Self::get_pool_id( - asset2.clone(), - asset3.clone(), - )) - } else { - send_to.clone() - }; + let (asset_in, amount_in) = path.first().ok_or(Error::::InvalidPath)?; + let credit_in = Self::withdraw(asset_in, sender, *amount_in, keep_alive)?; - let reserve = Self::get_balance(&pool_account, asset2)?; + let credit_out = Self::credit_swap(credit_in, path).map_err(|(_, e)| e)?; + Self::resolve(send_to, credit_out).map_err(|_| Error::::BelowMinimum)?; + + Ok(()) + } + + /// Swap assets along the specified `path`, consuming `credit_in` and producing + /// `credit_out`. + /// + /// Note: + /// * this function might modify storage even if an error occurs. + /// * if an error occurs, `credit_in` is returned back. + /// * it's assumed that the provided `path` is valid and `credit_in` corresponds to the + /// first asset in the `path`. + fn credit_swap( + credit_in: Credit, + path: &BalancePath, + ) -> Result, (Credit, DispatchError)> { + let resolve_path = || -> Result, DispatchError> { + for pos in 0..=path.len() { + if let Some([(asset1, _), (asset2, amount_out)]) = path.get(pos..=pos + 1) { + let pool_from = Self::get_pool_account(&Self::get_pool_id( + asset1.clone(), + asset2.clone(), + )); + + let reserve = Self::get_balance(&pool_from, asset2)?; let reserve_left = reserve.saturating_sub(*amount_out); Self::validate_minimal_amount(reserve_left, asset2) .map_err(|_| Error::::ReserveLeftLessThanMinimal)?; - Self::transfer(asset2, &pool_account, &to, *amount_out, true)?; + if let Some((asset3, _)) = path.get(pos + 2) { + let pool_to = Self::get_pool_account(&Self::get_pool_id( + asset2.clone(), + asset3.clone(), + )); + Self::transfer(asset2, &pool_from, &pool_to, *amount_out, true)?; + } else { + let credit_out = Self::withdraw(asset2, &pool_from, *amount_out, true)?; + return Ok(credit_out) + } } - i.saturating_inc(); } - Self::deposit_event(Event::SwapExecuted { - who: sender, - send_to, - path, - amount_in: *first_amount, - amount_out: *amounts.last().expect("Always has more than 1 element"), - }); - } else { return Err(Error::::InvalidPath.into()) - } - Ok(()) + }; + + let credit_out = match resolve_path() { + Ok(c) => c, + Err(e) => return Err((credit_in, e)), + }; + + let pool_to = if let Some([(asset1, _), (asset2, _)]) = path.get(0..2) { + Self::get_pool_account(&Self::get_pool_id(asset1.clone(), asset2.clone())) + } else { + return Err((credit_in, Error::::InvalidPath.into())) + }; + + Self::resolve(&pool_to, credit_in).map_err(|c| (c, Error::::BelowMinimum.into()))?; + + Ok(credit_out) } /// The account ID of the pool. @@ -967,42 +1039,52 @@ pub mod pallet { } /// Leading to an amount at the end of a `path`, get the required amounts in. - pub(crate) fn get_amounts_in( - amount_out: &T::AssetBalance, - path: &BoundedVec, - ) -> Result, DispatchError> { - let mut amounts: Vec = vec![*amount_out]; - - for assets_pair in path.windows(2).rev() { - if let [asset1, asset2] = assets_pair { - let (reserve_in, reserve_out) = Self::get_reserves(asset1, asset2)?; - let prev_amount = amounts.last().expect("Always has at least one element"); - let amount_in = Self::get_amount_in(prev_amount, &reserve_in, &reserve_out)?; - amounts.push(amount_in); - } + pub(crate) fn balance_path_from_amount_out( + amount_out: T::AssetBalance, + path: BoundedVec, + ) -> Result, DispatchError> { + let mut balance_path: BalancePath = Vec::with_capacity(path.len()); + let mut amount_out: T::AssetBalance = amount_out; + + let mut iter = path.into_iter().rev().peekable(); + while let Some(asset2) = iter.next() { + let asset1 = match iter.peek() { + Some(a) => a, + None => { + balance_path.push((asset2, amount_out)); + break + }, + }; + let (reserve_in, reserve_out) = Self::get_reserves(asset1, &asset2)?; + balance_path.push((asset2, amount_out)); + amount_out = Self::get_amount_in(&amount_out, &reserve_in, &reserve_out)?; } - - amounts.reverse(); - Ok(amounts) + balance_path.reverse(); + Ok(balance_path) } /// Following an amount into a `path`, get the corresponding amounts out. - pub(crate) fn get_amounts_out( - amount_in: &T::AssetBalance, - path: &BoundedVec, - ) -> Result, DispatchError> { - let mut amounts: Vec = vec![*amount_in]; - - for assets_pair in path.windows(2) { - if let [asset1, asset2] = assets_pair { - let (reserve_in, reserve_out) = Self::get_reserves(asset1, asset2)?; - let prev_amount = amounts.last().expect("Always has at least one element"); - let amount_out = Self::get_amount_out(prev_amount, &reserve_in, &reserve_out)?; - amounts.push(amount_out); - } + pub(crate) fn balance_path_from_amount_in( + amount_in: T::AssetBalance, + path: BoundedVec, + ) -> Result, DispatchError> { + let mut balance_path: BalancePath = Vec::with_capacity(path.len()); + let mut amount_out: T::AssetBalance = amount_in; + + let mut iter = path.into_iter().peekable(); + while let Some(asset1) = iter.next() { + let asset2 = match iter.peek() { + Some(a) => a, + None => { + balance_path.push((asset1, amount_out)); + break + }, + }; + let (reserve_in, reserve_out) = Self::get_reserves(&asset1, asset2)?; + balance_path.push((asset1, amount_out)); + amount_out = Self::get_amount_out(&amount_out, &reserve_in, &reserve_out)?; } - - Ok(amounts) + Ok(balance_path) } /// Used by the RPC service to provide current prices. diff --git a/substrate/frame/asset-conversion/src/types.rs b/substrate/frame/asset-conversion/src/types.rs index 0bec61705c11..175ac11e375f 100644 --- a/substrate/frame/asset-conversion/src/types.rs +++ b/substrate/frame/asset-conversion/src/types.rs @@ -27,6 +27,17 @@ use sp_std::{cmp::Ordering, marker::PhantomData}; /// migration. pub(super) type PoolIdOf = (::MultiAssetId, ::MultiAssetId); +/// Represents a swap path with associated amounts for input and output. +/// +/// Each pair of neighboring assets forms a pool. +/// +/// Example: +/// Given path [(asset1, amount_in), (asset2, amount_out2), (asset3, amount_out3)], can be resolved: +/// 1. `asset(asset1, amount_in)` take from `user` and move to the pool(asset1, asset2); +/// 2. `asset(asset2, amount_out2)` transfer from pool(asset1, asset2) to pool(asset2, asset3); +/// 3. `asset(asset3, amount_out3)` move from pool(asset2, asset3) to `user`. +pub(super) type BalancePath = Vec<(::MultiAssetId, ::AssetBalance)>; + /// Stores the lp_token asset id a particular pool has been assigned. #[derive(Decode, Encode, Default, PartialEq, Eq, MaxEncodedLen, TypeInfo)] pub struct PoolInfo { @@ -149,3 +160,71 @@ impl MultiAssetIdConverter, Asset } } } + +/// Credit of [Config::Currency]. +pub type NativeCredit = + CreditFungible<::AccountId, ::Currency>; + +/// Credit of [Config::Assets]. +pub type AssetCredit = + CreditFungibles<::AccountId, ::Assets>; + +/// Credit that can be either [`NativeCredit`] or [`AssetCredit`]. +pub enum Credit { + /// Native credit. + Native(NativeCredit), + /// Asset credit. + Asset(AssetCredit), +} + +impl From> for Credit { + fn from(value: NativeCredit) -> Self { + Credit::Native(value) + } +} + +impl From> for Credit { + fn from(value: AssetCredit) -> Self { + Credit::Asset(value) + } +} + +impl Credit { + /// Amount of `self`. + pub fn peek(&self) -> Result { + let amount = match self { + Credit::Native(c) => T::HigherPrecisionBalance::from(c.peek()) + .try_into() + .map_err(|_| Error::::Overflow)?, + Credit::Asset(c) => c.peek(), + }; + Ok(amount) + } + + /// Asset class of `self`. + pub fn asset(&self) -> T::MultiAssetId { + match self { + Credit::Native(_) => T::MultiAssetIdConverter::get_native(), + Credit::Asset(c) => c.asset().into(), + } + } + + /// Consume `self` and return two independent instances; the first is guaranteed to be at most + /// `amount` and the second will be the remainder. + pub fn split(self, amount: T::AssetBalance) -> Result<(Self, Self), (Self, DispatchError)> { + match self { + Credit::Native(c) => { + let amount = match T::HigherPrecisionBalance::from(amount).try_into() { + Ok(a) => a, + Err(_) => return Err((Self::Native(c), Error::::Overflow.into())), + }; + let (left, right) = c.split(amount); + Ok((left.into(), right.into())) + }, + Credit::Asset(c) => { + let (left, right) = c.split(amount); + Ok((left.into(), right.into())) + }, + } + } +} From a3552419b1c07689c524b3bc673990ea558a18c7 Mon Sep 17 00:00:00 2001 From: muharem Date: Fri, 22 Sep 2023 12:24:55 +0200 Subject: [PATCH 04/98] swap credit --- substrate/frame/asset-conversion/src/lib.rs | 205 ++++++++++++------ substrate/frame/asset-conversion/src/swap.rs | 67 ++++++ substrate/frame/asset-conversion/src/tests.rs | 6 +- substrate/frame/asset-conversion/src/types.rs | 13 +- 4 files changed, 220 insertions(+), 71 deletions(-) diff --git a/substrate/frame/asset-conversion/src/lib.rs b/substrate/frame/asset-conversion/src/lib.rs index 926c1790e871..80d2c6e09f46 100644 --- a/substrate/frame/asset-conversion/src/lib.rs +++ b/substrate/frame/asset-conversion/src/lib.rs @@ -65,7 +65,7 @@ mod types; pub mod weights; pub use pallet::*; -pub use swap::Swap; +pub use swap::*; pub use types::*; pub use weights::WeightInfo; @@ -276,24 +276,15 @@ pub mod pallet { who: T::AccountId, /// The account that the assets were transferred to. send_to: T::AccountId, - /// The route of asset ids that the swap went through. - /// E.g. A -> Dot -> B - path: BoundedVec, - /// The amount of the first asset that was swapped. - amount_in: T::AssetBalance, - /// The amount of the second asset that was received. - amount_out: T::AssetBalance, + /// The route of asset ids with amounts that the swap went through. + /// E.g. (A, amount_in) -> (Dot, amount_out) -> (B, amount_out) + path: BalancePath, }, - /// An amount has been transferred from one account to another. - Transfer { - /// The account that the assets were transferred from. - from: T::AccountId, - /// The account that the assets were transferred to. - to: T::AccountId, - /// The asset that was transferred. - asset: T::MultiAssetId, - /// The amount of the asset that was transferred. - amount: T::AssetBalance, + /// Assets have been converted from one to another. + CreditSwapExecuted { + /// The route of asset ids with amounts that the swap went through. + /// E.g. (A, amount_in) -> (Dot, amount_out) -> (B, amount_out) + path: BalancePath, }, } @@ -705,9 +696,10 @@ pub mod pallet { /// /// If successful, returns the amount of `path[1]` acquired for the `amount_in`. /// - /// Note: - /// * this function might modify storage even if an error occurs. - pub fn do_swap_exact_tokens_for_tokens( + /// WARNING: This may return an error after a partial storage mutation. It should be used + /// only inside a transactional storage context and an Err result must imply a storage + /// rollback. + pub(crate) fn do_swap_exact_tokens_for_tokens( sender: T::AccountId, path: BoundedVec, amount_in: T::AssetBalance, @@ -723,7 +715,7 @@ pub mod pallet { Self::validate_swap_path(&path)?; let path = Self::balance_path_from_amount_in(amount_in, path)?; - let amount_out = path.last().map(|(_, a)| a.clone()).ok_or(Error::::InvalidPath)?; + let amount_out = path.last().map(|(_, a)| *a).ok_or(Error::::InvalidPath)?; if let Some(amount_out_min) = amount_out_min { ensure!( amount_out >= amount_out_min, @@ -731,15 +723,9 @@ pub mod pallet { ); } - Self::transfer_swap(&sender, &path, &send_to, keep_alive)?; + Self::swap(&sender, &path, &send_to, keep_alive)?; - Self::deposit_event(Event::SwapExecuted { - who: sender, - send_to, - amount_in, - amount_out, - path: BoundedVec::truncate_from(path.into_iter().map(|(a, _)| a).collect()), - }); + Self::deposit_event(Event::SwapExecuted { who: sender, send_to, path }); Ok(amount_out) } @@ -752,9 +738,10 @@ pub mod pallet { /// /// If successful returns the amount of the `path[0]` taken to provide `path[1]`. /// - /// Note: - /// * this function might modify storage even if an error occurs. - pub fn do_swap_tokens_for_exact_tokens( + /// WARNING: This may return an error after a partial storage mutation. It should be used + /// only inside a transactional storage context and an Err result must imply a storage + /// rollback. + pub(crate) fn do_swap_tokens_for_exact_tokens( sender: T::AccountId, path: BoundedVec, amount_out: T::AssetBalance, @@ -770,7 +757,7 @@ pub mod pallet { Self::validate_swap_path(&path)?; let path = Self::balance_path_from_amount_out(amount_out, path)?; - let amount_in = path.first().map(|(_, a)| a.clone()).ok_or(Error::::InvalidPath)?; + let amount_in = path.first().map(|(_, a)| *a).ok_or(Error::::InvalidPath)?; if let Some(amount_in_max) = amount_in_max { ensure!( amount_in <= amount_in_max, @@ -778,19 +765,109 @@ pub mod pallet { ); } - Self::transfer_swap(&sender, &path, &send_to, keep_alive)?; + Self::swap(&sender, &path, &send_to, keep_alive)?; - Self::deposit_event(Event::SwapExecuted { - who: sender, - send_to, - amount_in, - amount_out, - path: BoundedVec::truncate_from(path.into_iter().map(|(a, _)| a).collect()), - }); + Self::deposit_event(Event::SwapExecuted { who: sender, send_to, path }); Ok(amount_in) } + /// Swap exactly `credit_in` of asset `path[0]` for asset `path[last]`. If `amount_out_min` + /// is provided and the swap can't achieve at least this amount, an error is returned. + /// + /// On a successful swap, the function returns the `credit_out` of `path[last]` obtained + /// from the `credit_in`. On failure, it returns an `Err` containing the original + /// `credit_in` and the associated error code. + /// + /// WARNING: This may return an error after a partial storage mutation. It should be used + /// only inside a transactional storage context and an Err result must imply a storage + /// rollback. + pub(crate) fn do_swap_exact_credit_tokens_for_tokens( + path: BoundedVec, + credit_in: Credit, + amount_out_min: Option, + ) -> Result, (Credit, DispatchError)> { + let amount_in = match credit_in.peek() { + Ok(a) => a, + Err(e) => return Err((credit_in, e)), + }; + let inspect_path = |credit_asset| { + ensure!(path.get(0).map_or(false, |a| *a == credit_asset), Error::::InvalidPath); + ensure!(!amount_in.is_zero(), Error::::ZeroAmount); + ensure!(amount_out_min.map_or(true, |a| !a.is_zero()), Error::::ZeroAmount); + + Self::validate_swap_path(&path)?; + let path = Self::balance_path_from_amount_in(amount_in, path)?; + + let amount_out = path.last().map(|(_, a)| *a).ok_or(Error::::InvalidPath)?; + ensure!( + amount_out_min.map_or(true, |a| amount_out >= a), + Error::::ProvidedMinimumNotSufficientForSwap + ); + Ok(path) + }; + let path = match inspect_path(credit_in.asset()) { + Ok(p) => p, + Err(e) => return Err((credit_in, e)), + }; + + let credit_out = Self::credit_swap(credit_in, &path)?; + + Self::deposit_event(Event::CreditSwapExecuted { path }); + + Ok(credit_out) + } + + /// Swaps a portion of `credit_in` of `path[0]` asset to obtain the desired `amount_out` of + /// the `path[last]` asset. The provided `credit_in` must be adequate to achieve the target + /// `amount_out`, or an error will occur. + /// + /// On success, the function returns a (`credit_out`, `credit_change`) tuple, where + /// `credit_out` represents the acquired amount of the `path[last]` asset, and + /// `credit_change` is the remaining portion from the `credit_in`. On failure, an `Err` with + /// the initial `credit_in` and error code is returned. + /// + /// WARNING: This may return an error after a partial storage mutation. It should be used + /// only inside a transactional storage context and an Err result must imply a storage + /// rollback. + pub(crate) fn do_swap_credit_tokens_for_exact_tokens( + path: BoundedVec, + credit_in: Credit, + amount_out: T::AssetBalance, + ) -> Result<(Credit, Credit), (Credit, DispatchError)> { + let amount_in_max = match credit_in.peek() { + Ok(a) => a, + Err(e) => return Err((credit_in, e)), + }; + let inspect_path = |credit_asset| { + ensure!(path.get(0).map_or(false, |a| a == &credit_asset), Error::::InvalidPath); + ensure!(amount_in_max > Zero::zero(), Error::::ZeroAmount); + ensure!(amount_out > Zero::zero(), Error::::ZeroAmount); + + Self::validate_swap_path(&path)?; + let path = Self::balance_path_from_amount_out(amount_out, path)?; + + let amount_in = path.first().map(|(_, a)| *a).ok_or(Error::::InvalidPath)?; + ensure!( + amount_in <= amount_in_max, + Error::::ProvidedMaximumNotSufficientForSwap + ); + + Ok((path, amount_in)) + }; + let (path, amount_in) = match inspect_path(credit_in.asset()) { + Ok((p, a)) => (p, a), + Err(e) => return Err((credit_in, e)), + }; + + let (credit_in, credit_change) = credit_in.split(amount_in)?; + let credit_out = Self::credit_swap(credit_in, &path)?; + + Self::deposit_event(Event::CreditSwapExecuted { path }); + + Ok((credit_out, credit_change)) + } + /// Transfer an `amount` of `asset_id`, respecting the `keep_alive` requirements. fn transfer( asset_id: &T::MultiAssetId, @@ -799,7 +876,7 @@ pub mod pallet { amount: T::AssetBalance, keep_alive: bool, ) -> Result { - let result = match T::MultiAssetIdConverter::try_convert(asset_id) { + match T::MultiAssetIdConverter::try_convert(asset_id) { MultiAssetIdConversionResult::Converted(asset_id) => T::Assets::transfer(asset_id, from, to, amount, Expendable), MultiAssetIdConversionResult::Native => { @@ -817,17 +894,7 @@ pub mod pallet { }, MultiAssetIdConversionResult::Unsupported(_) => Err(Error::::UnsupportedAsset.into()), - }; - - if result.is_ok() { - Self::deposit_event(Event::Transfer { - from: from.clone(), - to: to.clone(), - asset: (*asset_id).clone(), - amount, - }); } - result } /// The balance of `who` is increased in order to counter `credit`. If the whole of `credit` @@ -891,10 +958,12 @@ pub mod pallet { /// Swap assets along the `path`, withdrawing from `sender` and depositing in `send_to`. /// - /// Note: - /// * this function might modify storage even if an error occurs. - /// * it's assumed that the provided `path` is valid. - fn transfer_swap( + /// Note: It's assumed that the provided `path` is valid. + /// + /// WARNING: This may return an error after a partial storage mutation. It should be used + /// only inside a transactional storage context and an Err result must imply a storage + /// rollback. + fn swap( sender: &T::AccountId, path: &BalancePath, send_to: &T::AccountId, @@ -912,11 +981,14 @@ pub mod pallet { /// Swap assets along the specified `path`, consuming `credit_in` and producing /// `credit_out`. /// - /// Note: - /// * this function might modify storage even if an error occurs. - /// * if an error occurs, `credit_in` is returned back. - /// * it's assumed that the provided `path` is valid and `credit_in` corresponds to the - /// first asset in the `path`. + /// If an error occurs, `credit_in` is returned back. + /// + /// Note: It's assumed that the provided `path` is valid and `credit_in` corresponds to the + /// first asset in the `path`. + /// + /// WARNING: This may return an error after a partial storage mutation. It should be used + /// only inside a transactional storage context and an Err result must imply a storage + /// rollback. fn credit_swap( credit_in: Credit, path: &BalancePath, @@ -1044,22 +1116,23 @@ pub mod pallet { path: BoundedVec, ) -> Result, DispatchError> { let mut balance_path: BalancePath = Vec::with_capacity(path.len()); - let mut amount_out: T::AssetBalance = amount_out; + let mut amount_in: T::AssetBalance = amount_out; let mut iter = path.into_iter().rev().peekable(); while let Some(asset2) = iter.next() { let asset1 = match iter.peek() { Some(a) => a, None => { - balance_path.push((asset2, amount_out)); + balance_path.push((asset2, amount_in)); break }, }; let (reserve_in, reserve_out) = Self::get_reserves(asset1, &asset2)?; - balance_path.push((asset2, amount_out)); - amount_out = Self::get_amount_in(&amount_out, &reserve_in, &reserve_out)?; + balance_path.push((asset2, amount_in)); + amount_in = Self::get_amount_in(&amount_in, &reserve_in, &reserve_out)?; } balance_path.reverse(); + Ok(balance_path) } diff --git a/substrate/frame/asset-conversion/src/swap.rs b/substrate/frame/asset-conversion/src/swap.rs index 7c4468d24b7c..8724e99a1678 100644 --- a/substrate/frame/asset-conversion/src/swap.rs +++ b/substrate/frame/asset-conversion/src/swap.rs @@ -56,6 +56,43 @@ pub trait Swap { ) -> Result; } +/// Trait providing methods to swap between the various asset classes. +pub trait SwapCredit { + /// Measure units of the asset classes for swapping. + type Balance; + /// Kind of assets that are going to be swapped. + type MultiAssetId; + /// Credit implying a negative imbalance in the system that can be placed into an account or + /// alter the total supply. + type Credit; + + /// Swap exactly `credit_in` of asset `path[0]` for asset `path[last]`. If `amount_out_min` is + /// provided and the swap can't achieve at least this amount, an error is returned. + /// + /// On a successful swap, the function returns the `credit_out` of `path[last]` obtained from + /// the `credit_in`. On failure, it returns an `Err` containing the original `credit_in` and the + /// associated error code. + fn swap_exact_tokens_for_tokens( + path: Vec, + credit_in: Self::Credit, + amount_out_min: Option, + ) -> Result; + + /// Swaps a portion of `credit_in` of `path[0]` asset to obtain the desired `amount_out` of + /// the `path[last]` asset. The provided `credit_in` must be adequate to achieve the target + /// `amount_out`, or an error will occur. + /// + /// On success, the function returns a (`credit_out`, `credit_change`) tuple, where `credit_out` + /// represents the acquired amount of the `path[last]` asset, and `credit_change` is the + /// remaining portion from the `credit_in`. On failure, an `Err` with the initial `credit_in` + /// and error code is returned. + fn swap_tokens_for_exact_tokens( + path: Vec, + credit_in: Self::Credit, + amount_out: Self::Balance, + ) -> Result<(Self::Credit, Self::Credit), (Self::Credit, DispatchError)>; +} + impl Swap for Pallet { fn swap_exact_tokens_for_tokens( sender: T::AccountId, @@ -99,3 +136,33 @@ impl Swap f Ok(amount_in.into()) } } + +impl SwapCredit for Pallet { + type Balance = T::AssetBalance; + type MultiAssetId = T::MultiAssetId; + type Credit = Credit; + + fn swap_exact_tokens_for_tokens( + path: Vec, + credit_in: Self::Credit, + amount_out_min: Option, + ) -> Result { + let path = match path.try_into() { + Ok(p) => p, + Err(_) => return Err((credit_in, Error::::PathError.into())), + }; + Self::do_swap_exact_credit_tokens_for_tokens(path, credit_in, amount_out_min) + } + + fn swap_tokens_for_exact_tokens( + path: Vec, + credit_in: Self::Credit, + amount_out: Self::Balance, + ) -> Result<(Self::Credit, Self::Credit), (Self::Credit, DispatchError)> { + let path = match path.try_into() { + Ok(p) => p, + Err(_) => return Err((credit_in, Error::::PathError.into())), + }; + Self::do_swap_credit_tokens_for_exact_tokens(path, credit_in, amount_out) + } +} diff --git a/substrate/frame/asset-conversion/src/tests.rs b/substrate/frame/asset-conversion/src/tests.rs index 1c1267ab87b3..609d8b2316c1 100644 --- a/substrate/frame/asset-conversion/src/tests.rs +++ b/substrate/frame/asset-conversion/src/tests.rs @@ -948,9 +948,9 @@ fn can_swap_with_realistic_values() { assert!(events().contains(&Event::::SwapExecuted { who: user, send_to: user, - path: bvec![usd, dot], - amount_in: 10 * UNIT, // usd - amount_out: 1_993_980_120, // About 2 dot after div by UNIT. + path: bvec![(usd, 10 * UNIT), (dot, 1_993_980_120)], + // amount_in: 10 * UNIT, // usd + // amount_out: 1_993_980_120, // About 2 dot after div by UNIT. })); }); } diff --git a/substrate/frame/asset-conversion/src/types.rs b/substrate/frame/asset-conversion/src/types.rs index 175ac11e375f..7fe8e3d255db 100644 --- a/substrate/frame/asset-conversion/src/types.rs +++ b/substrate/frame/asset-conversion/src/types.rs @@ -27,7 +27,7 @@ use sp_std::{cmp::Ordering, marker::PhantomData}; /// migration. pub(super) type PoolIdOf = (::MultiAssetId, ::MultiAssetId); -/// Represents a swap path with associated amounts for input and output. +/// Represents a swap path with associated asset amounts for input and output. /// /// Each pair of neighboring assets forms a pool. /// @@ -162,14 +162,23 @@ impl MultiAssetIdConverter, Asset } /// Credit of [Config::Currency]. +/// +/// Implies a negative imbalance in the system that can be placed into an account or alter the total +/// supply. pub type NativeCredit = CreditFungible<::AccountId, ::Currency>; -/// Credit of [Config::Assets]. +/// Credit (aka negative imbalance) of [Config::Assets]. +/// +/// Implies a negative imbalance in the system that can be placed into an account or alter the total +/// supply. pub type AssetCredit = CreditFungibles<::AccountId, ::Assets>; /// Credit that can be either [`NativeCredit`] or [`AssetCredit`]. +/// +/// Implies a negative imbalance in the system that can be placed into an account or alter the total +/// supply. pub enum Credit { /// Native credit. Native(NativeCredit), From a78ac716d36ed075b4390b847cdafc106219c0bd Mon Sep 17 00:00:00 2001 From: muharem Date: Fri, 22 Sep 2023 13:15:24 +0200 Subject: [PATCH 05/98] with transaction --- substrate/frame/asset-conversion/src/lib.rs | 3 +- substrate/frame/asset-conversion/src/swap.rs | 65 ++++++++++++++----- substrate/frame/asset-conversion/src/tests.rs | 2 - substrate/frame/asset-conversion/src/types.rs | 5 ++ 4 files changed, 54 insertions(+), 21 deletions(-) diff --git a/substrate/frame/asset-conversion/src/lib.rs b/substrate/frame/asset-conversion/src/lib.rs index 80d2c6e09f46..bf2476d8293f 100644 --- a/substrate/frame/asset-conversion/src/lib.rs +++ b/substrate/frame/asset-conversion/src/lib.rs @@ -71,6 +71,7 @@ pub use weights::WeightInfo; use codec::Codec; use frame_support::{ + storage::{with_storage_layer, with_transaction}, traits::{ fungible::{ Balanced as BalancedFungible, Credit as CreditFungible, Inspect as InspectFungible, @@ -92,7 +93,7 @@ use sp_runtime::{ CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Ensure, IntegerSquareRoot, MaybeDisplay, One, TrailingZeroInput, Zero, }, - BoundedVec, DispatchError, Saturating, + BoundedVec, DispatchError, Saturating, TransactionOutcome, }; #[frame_support::pallet] diff --git a/substrate/frame/asset-conversion/src/swap.rs b/substrate/frame/asset-conversion/src/swap.rs index 8724e99a1678..e64fd39782d4 100644 --- a/substrate/frame/asset-conversion/src/swap.rs +++ b/substrate/frame/asset-conversion/src/swap.rs @@ -104,14 +104,16 @@ impl Swap f ) -> Result { let path = path.try_into().map_err(|_| Error::::PathError)?; let amount_out_min = amount_out_min.map(Self::convert_hpb_to_asset_balance).transpose()?; - let amount_out = Self::do_swap_exact_tokens_for_tokens( - sender, - path, - Self::convert_hpb_to_asset_balance(amount_in)?, - amount_out_min, - send_to, - keep_alive, - )?; + let amount_out = with_storage_layer(|| { + Self::do_swap_exact_tokens_for_tokens( + sender, + path, + Self::convert_hpb_to_asset_balance(amount_in)?, + amount_out_min, + send_to, + keep_alive, + ) + })?; Ok(amount_out.into()) } @@ -125,14 +127,16 @@ impl Swap f ) -> Result { let path = path.try_into().map_err(|_| Error::::PathError)?; let amount_in_max = amount_in_max.map(Self::convert_hpb_to_asset_balance).transpose()?; - let amount_in = Self::do_swap_tokens_for_exact_tokens( - sender, - path, - Self::convert_hpb_to_asset_balance(amount_out)?, - amount_in_max, - send_to, - keep_alive, - )?; + let amount_in = with_storage_layer(|| { + Self::do_swap_tokens_for_exact_tokens( + sender, + path, + Self::convert_hpb_to_asset_balance(amount_out)?, + amount_in_max, + send_to, + keep_alive, + ) + })?; Ok(amount_in.into()) } } @@ -151,7 +155,20 @@ impl SwapCredit for Pallet { Ok(p) => p, Err(_) => return Err((credit_in, Error::::PathError.into())), }; - Self::do_swap_exact_credit_tokens_for_tokens(path, credit_in, amount_out_min) + let transaction_res = + with_transaction(|| -> TransactionOutcome> { + let res = + Self::do_swap_exact_credit_tokens_for_tokens(path, credit_in, amount_out_min); + match &res { + Ok(_) => TransactionOutcome::Commit(Ok(res)), + Err(_) => TransactionOutcome::Rollback(Ok(res)), + } + }); + match transaction_res { + Ok(r) => r, + // should never happen, `with_transaction` above never returns an `Err` variant. + Err(_) => Err((Self::Credit::native_zero(), DispatchError::Corruption)), + } } fn swap_tokens_for_exact_tokens( @@ -163,6 +180,18 @@ impl SwapCredit for Pallet { Ok(p) => p, Err(_) => return Err((credit_in, Error::::PathError.into())), }; - Self::do_swap_credit_tokens_for_exact_tokens(path, credit_in, amount_out) + let transaction_res = + with_transaction(|| -> TransactionOutcome> { + let res = Self::do_swap_credit_tokens_for_exact_tokens(path, credit_in, amount_out); + match &res { + Ok(_) => TransactionOutcome::Commit(Ok(res)), + Err(_) => TransactionOutcome::Rollback(Ok(res)), + } + }); + match transaction_res { + Ok(r) => r, + // should never happen, `with_transaction` above never returns an `Err` variant. + Err(_) => Err((Self::Credit::native_zero(), DispatchError::Corruption)), + } } } diff --git a/substrate/frame/asset-conversion/src/tests.rs b/substrate/frame/asset-conversion/src/tests.rs index 609d8b2316c1..b5d56cbc7155 100644 --- a/substrate/frame/asset-conversion/src/tests.rs +++ b/substrate/frame/asset-conversion/src/tests.rs @@ -949,8 +949,6 @@ fn can_swap_with_realistic_values() { who: user, send_to: user, path: bvec![(usd, 10 * UNIT), (dot, 1_993_980_120)], - // amount_in: 10 * UNIT, // usd - // amount_out: 1_993_980_120, // About 2 dot after div by UNIT. })); }); } diff --git a/substrate/frame/asset-conversion/src/types.rs b/substrate/frame/asset-conversion/src/types.rs index 7fe8e3d255db..1b921a757a5a 100644 --- a/substrate/frame/asset-conversion/src/types.rs +++ b/substrate/frame/asset-conversion/src/types.rs @@ -199,6 +199,11 @@ impl From> for Credit { } impl Credit { + /// Create zero native credit. + pub fn native_zero() -> Self { + NativeCredit::::zero().into() + } + /// Amount of `self`. pub fn peek(&self) -> Result { let amount = match self { From 888c9fad09e2bacadf70dccd304c11c13691310b Mon Sep 17 00:00:00 2001 From: muharem Date: Fri, 22 Sep 2023 15:14:07 +0200 Subject: [PATCH 06/98] preservation --- substrate/frame/asset-conversion/src/lib.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/substrate/frame/asset-conversion/src/lib.rs b/substrate/frame/asset-conversion/src/lib.rs index bf2476d8293f..d549fe04512c 100644 --- a/substrate/frame/asset-conversion/src/lib.rs +++ b/substrate/frame/asset-conversion/src/lib.rs @@ -877,14 +877,14 @@ pub mod pallet { amount: T::AssetBalance, keep_alive: bool, ) -> Result { + let preservation = match keep_alive { + true => Preserve, + false => Expendable, + }; match T::MultiAssetIdConverter::try_convert(asset_id) { MultiAssetIdConversionResult::Converted(asset_id) => - T::Assets::transfer(asset_id, from, to, amount, Expendable), + T::Assets::transfer(asset_id, from, to, amount, preservation), MultiAssetIdConversionResult::Native => { - let preservation = match keep_alive { - true => Preserve, - false => Expendable, - }; let amount = Self::convert_asset_balance_to_native_balance(amount)?; Ok(Self::convert_native_balance_to_asset_balance(T::Currency::transfer( from, @@ -1213,7 +1213,7 @@ pub mod pallet { reserve1: &T::AssetBalance, reserve2: &T::AssetBalance, ) -> Result> { - // amount * reserve2 / reserve1 + // (amount * reserve2) / reserve1 Self::mul_div(amount, reserve2, reserve1) } From 793b61253d51402df5f64d704ac41df8241cb611 Mon Sep 17 00:00:00 2001 From: muharem Date: Fri, 22 Sep 2023 16:25:49 +0200 Subject: [PATCH 07/98] add amounts to the event --- substrate/frame/asset-conversion/src/lib.rs | 34 +++++++++++++++---- substrate/frame/asset-conversion/src/tests.rs | 2 ++ 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/substrate/frame/asset-conversion/src/lib.rs b/substrate/frame/asset-conversion/src/lib.rs index d549fe04512c..cbc2bb3b40ad 100644 --- a/substrate/frame/asset-conversion/src/lib.rs +++ b/substrate/frame/asset-conversion/src/lib.rs @@ -277,12 +277,20 @@ pub mod pallet { who: T::AccountId, /// The account that the assets were transferred to. send_to: T::AccountId, + /// The amount of the first asset that was swapped. + amount_in: T::AssetBalance, + /// The amount of the second asset that was received. + amount_out: T::AssetBalance, /// The route of asset ids with amounts that the swap went through. /// E.g. (A, amount_in) -> (Dot, amount_out) -> (B, amount_out) path: BalancePath, }, /// Assets have been converted from one to another. CreditSwapExecuted { + /// The amount of the first asset that was swapped. + amount_in: T::AssetBalance, + /// The amount of the second asset that was received. + amount_out: T::AssetBalance, /// The route of asset ids with amounts that the swap went through. /// E.g. (A, amount_in) -> (Dot, amount_out) -> (B, amount_out) path: BalancePath, @@ -726,7 +734,13 @@ pub mod pallet { Self::swap(&sender, &path, &send_to, keep_alive)?; - Self::deposit_event(Event::SwapExecuted { who: sender, send_to, path }); + Self::deposit_event(Event::SwapExecuted { + who: sender, + send_to, + amount_in, + amount_out, + path, + }); Ok(amount_out) } @@ -768,7 +782,13 @@ pub mod pallet { Self::swap(&sender, &path, &send_to, keep_alive)?; - Self::deposit_event(Event::SwapExecuted { who: sender, send_to, path }); + Self::deposit_event(Event::SwapExecuted { + who: sender, + send_to, + amount_in, + amount_out, + path, + }); Ok(amount_in) } @@ -805,16 +825,16 @@ pub mod pallet { amount_out_min.map_or(true, |a| amount_out >= a), Error::::ProvidedMinimumNotSufficientForSwap ); - Ok(path) + Ok((path, amount_out)) }; - let path = match inspect_path(credit_in.asset()) { - Ok(p) => p, + let (path, amount_out) = match inspect_path(credit_in.asset()) { + Ok((p, a)) => (p, a), Err(e) => return Err((credit_in, e)), }; let credit_out = Self::credit_swap(credit_in, &path)?; - Self::deposit_event(Event::CreditSwapExecuted { path }); + Self::deposit_event(Event::CreditSwapExecuted { amount_in, amount_out, path }); Ok(credit_out) } @@ -864,7 +884,7 @@ pub mod pallet { let (credit_in, credit_change) = credit_in.split(amount_in)?; let credit_out = Self::credit_swap(credit_in, &path)?; - Self::deposit_event(Event::CreditSwapExecuted { path }); + Self::deposit_event(Event::CreditSwapExecuted { amount_in, amount_out, path }); Ok((credit_out, credit_change)) } diff --git a/substrate/frame/asset-conversion/src/tests.rs b/substrate/frame/asset-conversion/src/tests.rs index b5d56cbc7155..639ecafb8ed7 100644 --- a/substrate/frame/asset-conversion/src/tests.rs +++ b/substrate/frame/asset-conversion/src/tests.rs @@ -948,6 +948,8 @@ fn can_swap_with_realistic_values() { assert!(events().contains(&Event::::SwapExecuted { who: user, send_to: user, + amount_in: 10 * UNIT, // usd + amount_out: 1_993_980_120, // About 2 dot after div by UNIT. path: bvec![(usd, 10 * UNIT), (dot, 1_993_980_120)], })); }); From cdaf50027ef83bb52b9b148584ef4eb941cd3c8a Mon Sep 17 00:00:00 2001 From: muharem Date: Mon, 25 Sep 2023 15:59:57 +0200 Subject: [PATCH 08/98] not expendable constraint --- substrate/frame/asset-conversion/src/lib.rs | 26 ++-- substrate/frame/asset-conversion/src/tests.rs | 144 +++++++++++++++++- 2 files changed, 154 insertions(+), 16 deletions(-) diff --git a/substrate/frame/asset-conversion/src/lib.rs b/substrate/frame/asset-conversion/src/lib.rs index cbc2bb3b40ad..37cdfbca5f89 100644 --- a/substrate/frame/asset-conversion/src/lib.rs +++ b/substrate/frame/asset-conversion/src/lib.rs @@ -93,7 +93,7 @@ use sp_runtime::{ CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Ensure, IntegerSquareRoot, MaybeDisplay, One, TrailingZeroInput, Zero, }, - BoundedVec, DispatchError, Saturating, TransactionOutcome, + BoundedVec, DispatchError, Saturating, TokenError, TransactionOutcome, }; #[frame_support::pallet] @@ -940,11 +940,25 @@ pub mod pallet { false => Expendable, }; match T::MultiAssetIdConverter::try_convert(asset) { - MultiAssetIdConversionResult::Converted(asset) => + MultiAssetIdConversionResult::Converted(asset) => { + if preservation == Preserve { + // TODO drop the ensure! when this issue addressed + // https://github.com/paritytech/polkadot-sdk/issues/1698 + let free = + T::Assets::reducible_balance(asset.clone(), who, preservation, Polite); + ensure!(free >= value, TokenError::NotExpendable); + } T::Assets::withdraw(asset, who, value, Exact, preservation, Polite) - .map(|c| c.into()), + .map(|c| c.into()) + }, MultiAssetIdConversionResult::Native => { let value = Self::convert_asset_balance_to_native_balance(value)?; + if preservation == Preserve { + // TODO drop the ensure! when this issue addressed + // https://github.com/paritytech/polkadot-sdk/issues/1698 + let free = T::Currency::reducible_balance(who, preservation, Polite); + ensure!(free >= value, TokenError::NotExpendable); + } T::Currency::withdraw(who, value, Exact, preservation, Polite).map(|c| c.into()) }, MultiAssetIdConversionResult::Unsupported(_) => @@ -1021,12 +1035,6 @@ pub mod pallet { asset1.clone(), asset2.clone(), )); - - let reserve = Self::get_balance(&pool_from, asset2)?; - let reserve_left = reserve.saturating_sub(*amount_out); - Self::validate_minimal_amount(reserve_left, asset2) - .map_err(|_| Error::::ReserveLeftLessThanMinimal)?; - if let Some((asset3, _)) = path.get(pos + 2) { let pool_to = Self::get_pool_account(&Self::get_pool_id( asset2.clone(), diff --git a/substrate/frame/asset-conversion/src/tests.rs b/substrate/frame/asset-conversion/src/tests.rs index 639ecafb8ed7..86fb61edcb77 100644 --- a/substrate/frame/asset-conversion/src/tests.rs +++ b/substrate/frame/asset-conversion/src/tests.rs @@ -19,7 +19,7 @@ use crate::{mock::*, *}; use frame_support::{ assert_noop, assert_ok, instances::Instance1, - traits::{fungible::Inspect, fungibles::InspectEnumerable, Get}, + traits::{fungible, fungibles, fungibles::InspectEnumerable, Get}, }; use sp_arithmetic::Permill; use sp_runtime::{DispatchError, TokenError}; @@ -65,13 +65,17 @@ fn pool_assets() -> Vec { } fn create_tokens(owner: u128, tokens: Vec>) { + create_tokens_with_ed(owner, tokens, 1) +} + +fn create_tokens_with_ed(owner: u128, tokens: Vec>, ed: u128) { for token_id in tokens { let MultiAssetIdConversionResult::Converted(asset_id) = NativeOrAssetIdConverter::try_convert(&token_id) else { unreachable!("invalid token") }; - assert_ok!(Assets::force_create(RuntimeOrigin::root(), asset_id, owner, false, 1)); + assert_ok!(Assets::force_create(RuntimeOrigin::root(), asset_id, owner, false, ed)); } } @@ -444,8 +448,14 @@ fn can_remove_liquidity() { let lp_token = AssetConversion::get_next_pool_asset_id(); assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), token_1, token_2)); - assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 10000000000)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user, 100000)); + let ed_token_1 = >::minimum_balance(); + let ed_token_2 = >::minimum_balance(2); + assert_ok!(Balances::force_set_balance( + RuntimeOrigin::root(), + user, + 10000000000 + ed_token_1 + )); + assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user, 100000 + ed_token_2)); assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), @@ -487,8 +497,8 @@ fn can_remove_liquidity() { assert_eq!(balance(pool_account, token_2), 10001); assert_eq!(pool_balance(pool_account, lp_token), 100); - assert_eq!(balance(user, token_1), 10000000000 - 1000000000 + 899991000); - assert_eq!(balance(user, token_2), 89999); + assert_eq!(balance(user, token_1), 10000000000 - 1000000000 + 899991000 + ed_token_1); + assert_eq!(balance(user, token_2), 89999 + ed_token_2); assert_eq!(pool_balance(user, lp_token), 0); }); } @@ -1050,7 +1060,7 @@ fn check_no_panic_when_try_swap_close_to_empty_pool() { user, false, ), - Error::::ReserveLeftLessThanMinimal + TokenError::NotExpendable, ); assert_ok!(AssetConversion::swap_tokens_for_exact_tokens( @@ -1308,6 +1318,7 @@ fn swap_when_existential_deposit_would_cause_reaping_but_keep_alive_set() { assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 101)); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user2, 10000 + ed)); assert_ok!(Assets::mint(RuntimeOrigin::signed(user2), 2, user2, 1000)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(user2), 2, user, 2)); assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user2), @@ -1343,6 +1354,125 @@ fn swap_when_existential_deposit_would_cause_reaping_but_keep_alive_set() { ), DispatchError::Token(TokenError::NotExpendable) ); + + assert_noop!( + AssetConversion::swap_tokens_for_exact_tokens( + RuntimeOrigin::signed(user), + bvec![token_2, token_1], + 51, // amount_out + 2, // amount_in_max + user, + true, + ), + DispatchError::Token(TokenError::NotExpendable) + ); + + assert_noop!( + AssetConversion::swap_exact_tokens_for_tokens( + RuntimeOrigin::signed(user), + bvec![token_2, token_1], + 2, // amount_in + 1, // amount_out_min + user, + true, + ), + DispatchError::Token(TokenError::NotExpendable) + ); + }); +} + +#[test] +fn swap_when_existential_deposit_would_cause_reaping_pool_account() { + new_test_ext().execute_with(|| { + let user = 1; + let user2 = 2; + let token_1 = NativeOrAssetId::Native; + let token_2 = NativeOrAssetId::Asset(2); + let token_3 = NativeOrAssetId::Asset(3); + + let ed_assets = 100; + create_tokens_with_ed(user2, vec![token_2, token_3], ed_assets); + assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user2), token_1, token_2)); + assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user2), token_1, token_3)); + + let ed = get_ed(); + assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 20000 + ed)); + assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user2, 20000 + ed)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(user2), 2, user2, 200 + ed_assets)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(user2), 3, user2, 10000 + ed_assets)); + + assert_ok!(Assets::mint(RuntimeOrigin::signed(user2), 2, user, 400 + ed_assets)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(user2), 3, user, 20000 + ed_assets)); + + assert_ok!(AssetConversion::add_liquidity( + RuntimeOrigin::signed(user2), + token_1, + token_2, + 10000, + 200, + 1, + 1, + user2, + )); + + assert_noop!( + AssetConversion::swap_tokens_for_exact_tokens( + RuntimeOrigin::signed(user), + bvec![token_1, token_2], + 110, // amount_out + 20000, // amount_in_max + user, + true, + ), + DispatchError::Token(TokenError::NotExpendable) + ); + + assert_noop!( + AssetConversion::swap_exact_tokens_for_tokens( + RuntimeOrigin::signed(user), + bvec![token_1, token_2], + 15000, // amount_in + 110, // amount_out_min + user, + true, + ), + DispatchError::Token(TokenError::NotExpendable) + ); + + assert_ok!(AssetConversion::add_liquidity( + RuntimeOrigin::signed(user2), + token_1, + token_3, + 200, + 10000, + 1, + 1, + user2, + )); + + assert_noop!( + AssetConversion::swap_tokens_for_exact_tokens( + RuntimeOrigin::signed(user), + bvec![token_3, token_1], + 110, // amount_out + 20000, // amount_in_max + user, + true, + ), + DispatchError::Token(TokenError::NotExpendable) + ); + + assert_noop!( + AssetConversion::swap_exact_tokens_for_tokens( + RuntimeOrigin::signed(user), + bvec![token_3, token_1], + 15000, // amount_in + 110, // amount_out_min + user, + true, + ), + DispatchError::Token(TokenError::NotExpendable) + ); }); } From e237dfc651de6d0a80977996b7657fe132df5123 Mon Sep 17 00:00:00 2001 From: muharem Date: Mon, 25 Sep 2023 17:15:17 +0200 Subject: [PATCH 09/98] vec import --- substrate/frame/asset-conversion/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/substrate/frame/asset-conversion/src/lib.rs b/substrate/frame/asset-conversion/src/lib.rs index 37cdfbca5f89..bb084f143e50 100644 --- a/substrate/frame/asset-conversion/src/lib.rs +++ b/substrate/frame/asset-conversion/src/lib.rs @@ -95,6 +95,7 @@ use sp_runtime::{ }, BoundedVec, DispatchError, Saturating, TokenError, TransactionOutcome, }; +use sp_std::vec::Vec; #[frame_support::pallet] pub mod pallet { From 0c13c85c4eeb58ced65a5a21f27d748b624ce62c Mon Sep 17 00:00:00 2001 From: muharem Date: Mon, 25 Sep 2023 18:14:09 +0200 Subject: [PATCH 10/98] fix tests, preserve account --- .../asset-conversion-tx-payment/src/tests.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/tests.rs b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/tests.rs index 9e9b74a0ddb2..cad301dc9599 100644 --- a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/tests.rs +++ b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/tests.rs @@ -19,7 +19,10 @@ use frame_support::{ assert_ok, dispatch::{DispatchInfo, PostDispatchInfo}, pallet_prelude::*, - traits::{fungible::Inspect, fungibles::Mutate}, + traits::{ + fungible::Inspect, + fungibles::{Inspect as FungiblesInspect, Mutate}, + }, weights::Weight, }; use frame_system as system; @@ -110,13 +113,19 @@ fn default_post_info() -> PostDispatchInfo { fn setup_lp(asset_id: u32, balance_factor: u64) { let lp_provider = 5; + let ed = Balances::minimum_balance(); + let ed_asset = Assets::minimum_balance(asset_id); assert_ok!(Balances::force_set_balance( RuntimeOrigin::root(), lp_provider, - 10_000 * balance_factor + 10_000 * balance_factor + ed, )); let lp_provider_account = ::Lookup::unlookup(lp_provider); - assert_ok!(Assets::mint_into(asset_id.into(), &lp_provider_account, 10_000 * balance_factor)); + assert_ok!(Assets::mint_into( + asset_id.into(), + &lp_provider_account, + 10_000 * balance_factor + ed_asset + )); let token_1 = NativeOrAssetId::Native; let token_2 = NativeOrAssetId::Asset(asset_id); From 24654b88c235dc0b9b7a4d150fbff83909785723 Mon Sep 17 00:00:00 2001 From: muharem Date: Tue, 26 Sep 2023 16:08:57 +0200 Subject: [PATCH 11/98] tests --- substrate/frame/asset-conversion/src/tests.rs | 86 ++++++++++++++++--- 1 file changed, 73 insertions(+), 13 deletions(-) diff --git a/substrate/frame/asset-conversion/src/tests.rs b/substrate/frame/asset-conversion/src/tests.rs index 86fb61edcb77..e3530ea3e206 100644 --- a/substrate/frame/asset-conversion/src/tests.rs +++ b/substrate/frame/asset-conversion/src/tests.rs @@ -1394,12 +1394,13 @@ fn swap_when_existential_deposit_would_cause_reaping_pool_account() { create_tokens_with_ed(user2, vec![token_2, token_3], ed_assets); assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user2), token_1, token_2)); assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user2), token_1, token_3)); + assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user2), token_2, token_3)); let ed = get_ed(); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 20000 + ed)); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user2, 20000 + ed)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(user2), 2, user2, 200 + ed_assets)); - assert_ok!(Assets::mint(RuntimeOrigin::signed(user2), 3, user2, 10000 + ed_assets)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(user2), 2, user2, 400 + ed_assets)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(user2), 3, user2, 20000 + ed_assets)); assert_ok!(Assets::mint(RuntimeOrigin::signed(user2), 2, user, 400 + ed_assets)); assert_ok!(Assets::mint(RuntimeOrigin::signed(user2), 3, user, 20000 + ed_assets)); @@ -1415,6 +1416,29 @@ fn swap_when_existential_deposit_would_cause_reaping_pool_account() { user2, )); + assert_ok!(AssetConversion::add_liquidity( + RuntimeOrigin::signed(user2), + token_1, + token_3, + 200, + 10000, + 1, + 1, + user2, + )); + + assert_ok!(AssetConversion::add_liquidity( + RuntimeOrigin::signed(user2), + token_2, + token_3, + 200, + 10000, + 1, + 1, + user2, + )); + + // causes an account removal for asset token 2 assert_noop!( AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user), @@ -1427,6 +1451,7 @@ fn swap_when_existential_deposit_would_cause_reaping_pool_account() { DispatchError::Token(TokenError::NotExpendable) ); + // causes an account removal for asset token 2 assert_noop!( AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), @@ -1439,17 +1464,7 @@ fn swap_when_existential_deposit_would_cause_reaping_pool_account() { DispatchError::Token(TokenError::NotExpendable) ); - assert_ok!(AssetConversion::add_liquidity( - RuntimeOrigin::signed(user2), - token_1, - token_3, - 200, - 10000, - 1, - 1, - user2, - )); - + // causes an account removal for native token 1 assert_noop!( AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user), @@ -1462,6 +1477,7 @@ fn swap_when_existential_deposit_would_cause_reaping_pool_account() { DispatchError::Token(TokenError::NotExpendable) ); + // causes an account removal for native token 1 assert_noop!( AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), @@ -1473,6 +1489,50 @@ fn swap_when_existential_deposit_would_cause_reaping_pool_account() { ), DispatchError::Token(TokenError::NotExpendable) ); + + // causes an account removal for native token 1 locate in the middle of a swap path + let amount_in = AssetConversion::balance_path_from_amount_out( + 110, + vec![token_3, token_1].try_into().unwrap(), + ) + .unwrap() + .first() + .map(|(_, a)| a.clone()) + .unwrap(); + + assert_noop!( + AssetConversion::swap_exact_tokens_for_tokens( + RuntimeOrigin::signed(user), + bvec![token_3, token_1, token_2], + amount_in, // amount_in + 1, // amount_out_min + user, + true, + ), + DispatchError::Token(TokenError::NotExpendable) + ); + + // causes an account removal for asset token 2 locate in the middle of a swap path + let amount_in = AssetConversion::balance_path_from_amount_out( + 110, + vec![token_1, token_2].try_into().unwrap(), + ) + .unwrap() + .first() + .map(|(_, a)| a.clone()) + .unwrap(); + + assert_noop!( + AssetConversion::swap_exact_tokens_for_tokens( + RuntimeOrigin::signed(user), + bvec![token_1, token_2, token_3], + amount_in, // amount_in + 1, // amount_out_min + user, + true, + ), + DispatchError::Token(TokenError::NotExpendable) + ); }); } From e0bd8b71cbe2607c96f331d90216a2cc2eb96ebb Mon Sep 17 00:00:00 2001 From: muharem Date: Tue, 26 Sep 2023 16:15:33 +0200 Subject: [PATCH 12/98] max lenght of the swap path in swap trait --- substrate/frame/asset-conversion/src/lib.rs | 1 + substrate/frame/asset-conversion/src/swap.rs | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/substrate/frame/asset-conversion/src/lib.rs b/substrate/frame/asset-conversion/src/lib.rs index bb084f143e50..a3fa1464dd52 100644 --- a/substrate/frame/asset-conversion/src/lib.rs +++ b/substrate/frame/asset-conversion/src/lib.rs @@ -88,6 +88,7 @@ use frame_support::{ }, BoundedBTreeSet, PalletId, }; +use sp_core::Get; use sp_runtime::{ traits::{ CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Ensure, IntegerSquareRoot, MaybeDisplay, diff --git a/substrate/frame/asset-conversion/src/swap.rs b/substrate/frame/asset-conversion/src/swap.rs index e64fd39782d4..2bff86392b38 100644 --- a/substrate/frame/asset-conversion/src/swap.rs +++ b/substrate/frame/asset-conversion/src/swap.rs @@ -21,6 +21,9 @@ use super::*; /// Trait for providing methods to swap between the various asset classes. pub trait Swap { + /// Returns the upper limit on the length of the swap path. + fn max_path_len() -> u32; + /// Swap exactly `amount_in` of asset `path[0]` for asset `path[1]`. /// If an `amount_out_min` is specified, it will return an error if it is unable to acquire /// the amount desired. @@ -66,6 +69,9 @@ pub trait SwapCredit { /// alter the total supply. type Credit; + /// Returns the upper limit on the length of the swap path. + fn max_path_len() -> u32; + /// Swap exactly `credit_in` of asset `path[0]` for asset `path[last]`. If `amount_out_min` is /// provided and the swap can't achieve at least this amount, an error is returned. /// @@ -94,6 +100,10 @@ pub trait SwapCredit { } impl Swap for Pallet { + fn max_path_len() -> u32 { + T::MaxSwapPathLength::get() + } + fn swap_exact_tokens_for_tokens( sender: T::AccountId, path: Vec, @@ -146,6 +156,10 @@ impl SwapCredit for Pallet { type MultiAssetId = T::MultiAssetId; type Credit = Credit; + fn max_path_len() -> u32 { + T::MaxSwapPathLength::get() + } + fn swap_exact_tokens_for_tokens( path: Vec, credit_in: Self::Credit, From 80dbcab83f26e29ec6801c7d30d15f35a726e780 Mon Sep 17 00:00:00 2001 From: muharem Date: Tue, 26 Sep 2023 16:39:45 +0200 Subject: [PATCH 13/98] fix clippy --- substrate/frame/asset-conversion/src/tests.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/substrate/frame/asset-conversion/src/tests.rs b/substrate/frame/asset-conversion/src/tests.rs index e3530ea3e206..6503c4b7d8c0 100644 --- a/substrate/frame/asset-conversion/src/tests.rs +++ b/substrate/frame/asset-conversion/src/tests.rs @@ -1497,7 +1497,7 @@ fn swap_when_existential_deposit_would_cause_reaping_pool_account() { ) .unwrap() .first() - .map(|(_, a)| a.clone()) + .map(|(_, a)| *a) .unwrap(); assert_noop!( @@ -1519,7 +1519,7 @@ fn swap_when_existential_deposit_would_cause_reaping_pool_account() { ) .unwrap() .first() - .map(|(_, a)| a.clone()) + .map(|(_, a)| *a) .unwrap(); assert_noop!( From 847207be9dc286137dd68601f325debb343cb459 Mon Sep 17 00:00:00 2001 From: muharem Date: Tue, 26 Sep 2023 18:24:38 +0200 Subject: [PATCH 14/98] debug impls for imbalances --- .../src/traits/tokens/fungible/imbalance.rs | 22 +++++++++++-- .../src/traits/tokens/fungibles/imbalance.rs | 33 +++++++++++++++++-- 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/substrate/frame/support/src/traits/tokens/fungible/imbalance.rs b/substrate/frame/support/src/traits/tokens/fungible/imbalance.rs index de85924a4de7..1357056d3a07 100644 --- a/substrate/frame/support/src/traits/tokens/fungible/imbalance.rs +++ b/substrate/frame/support/src/traits/tokens/fungible/imbalance.rs @@ -23,7 +23,7 @@ use crate::traits::{ misc::{SameOrOther, TryDrop}, tokens::Balance, }; -use sp_runtime::{traits::Zero, RuntimeDebug}; +use sp_runtime::traits::Zero; use sp_std::marker::PhantomData; /// Handler for when an imbalance gets dropped. This could handle either a credit (negative) or @@ -43,7 +43,7 @@ impl HandleImbalanceDrop for () { /// /// Importantly, it has a special `Drop` impl, and cannot be created outside of this module. #[must_use] -#[derive(RuntimeDebug, Eq, PartialEq)] +#[derive(Eq, PartialEq)] pub struct Imbalance< B: Balance, OnDrop: HandleImbalanceDrop, @@ -141,6 +141,24 @@ impl, OppositeOnDrop: HandleImbalance } } +#[cfg(any(feature = "std", feature = "force-debug"))] +impl, OppositeOnDrop: HandleImbalanceDrop> + sp_std::fmt::Debug for Imbalance +{ + fn fmt(&self, fmt: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { + fmt.debug_struct("Imbalance").field("amount", &self.amount).finish() + } +} + +#[cfg(all(not(feature = "std"), not(feature = "force-debug")))] +impl, OppositeOnDrop: HandleImbalanceDrop> + sp_std::fmt::Debug for Imbalance +{ + fn fmt(&self, fmt: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { + fmt.write_str("") + } +} + /// Imbalance implying that the total_issuance value is less than the sum of all account balances. pub type Debt = Imbalance< >::Balance, diff --git a/substrate/frame/support/src/traits/tokens/fungibles/imbalance.rs b/substrate/frame/support/src/traits/tokens/fungibles/imbalance.rs index 1668268ea2dc..17e22cf917e7 100644 --- a/substrate/frame/support/src/traits/tokens/fungibles/imbalance.rs +++ b/substrate/frame/support/src/traits/tokens/fungibles/imbalance.rs @@ -23,7 +23,7 @@ use crate::traits::{ misc::{SameOrOther, TryDrop}, tokens::{AssetId, Balance}, }; -use sp_runtime::{traits::Zero, RuntimeDebug}; +use sp_runtime::traits::Zero; use sp_std::marker::PhantomData; /// Handler for when an imbalance gets dropped. This could handle either a credit (negative) or @@ -38,7 +38,7 @@ pub trait HandleImbalanceDrop { /// /// Importantly, it has a special `Drop` impl, and cannot be created outside of this module. #[must_use] -#[derive(RuntimeDebug, Eq, PartialEq)] +#[derive(Eq, PartialEq)] pub struct Imbalance< A: AssetId, B: Balance, @@ -158,6 +158,35 @@ impl< } } +#[cfg(any(feature = "std", feature = "force-debug"))] +impl< + A: AssetId, + B: Balance, + OnDrop: HandleImbalanceDrop, + OppositeOnDrop: HandleImbalanceDrop, + > sp_std::fmt::Debug for Imbalance +{ + fn fmt(&self, fmt: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { + fmt.debug_struct("Imbalance") + .field("asset", &self.asset) + .field("amount", &self.amount) + .finish() + } +} + +#[cfg(all(not(feature = "std"), not(feature = "force-debug")))] +impl< + A: AssetId, + B: Balance, + OnDrop: HandleImbalanceDrop, + OppositeOnDrop: HandleImbalanceDrop, + > sp_std::fmt::Debug for Imbalance +{ + fn fmt(&self, fmt: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { + fmt.write_str("") + } +} + /// Imbalance implying that the total_issuance value is less than the sum of all account balances. pub type Debt = Imbalance< >::AssetId, From 53c699f3745927984e1729538cc738f0f6efdaa5 Mon Sep 17 00:00:00 2001 From: muharem Date: Thu, 28 Sep 2023 13:16:13 +0200 Subject: [PATCH 15/98] eq and partial_eq impls for imbalance --- .../src/traits/tokens/fungible/imbalance.rs | 20 ++++++++++++++-- .../src/traits/tokens/fungibles/imbalance.rs | 24 ++++++++++++++++++- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/substrate/frame/support/src/traits/tokens/fungible/imbalance.rs b/substrate/frame/support/src/traits/tokens/fungible/imbalance.rs index 1357056d3a07..49b0b506db09 100644 --- a/substrate/frame/support/src/traits/tokens/fungible/imbalance.rs +++ b/substrate/frame/support/src/traits/tokens/fungible/imbalance.rs @@ -43,7 +43,6 @@ impl HandleImbalanceDrop for () { /// /// Importantly, it has a special `Drop` impl, and cannot be created outside of this module. #[must_use] -#[derive(Eq, PartialEq)] pub struct Imbalance< B: Balance, OnDrop: HandleImbalanceDrop, @@ -141,12 +140,29 @@ impl, OppositeOnDrop: HandleImbalance } } +impl, OppositeOnDrop: HandleImbalanceDrop> PartialEq + for Imbalance +{ + fn eq(&self, other: &Self) -> bool { + self.amount.eq(&other.amount) + } +} + +impl, OppositeOnDrop: HandleImbalanceDrop> Eq + for Imbalance +{ +} + #[cfg(any(feature = "std", feature = "force-debug"))] impl, OppositeOnDrop: HandleImbalanceDrop> sp_std::fmt::Debug for Imbalance { fn fmt(&self, fmt: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { - fmt.debug_struct("Imbalance").field("amount", &self.amount).finish() + fmt.debug_struct("Imbalance") + .field("amount", &self.amount) + .field("OnDrop", &sp_std::any::type_name::()) + .field("OppositeOnDrop", &sp_std::any::type_name::()) + .finish() } } diff --git a/substrate/frame/support/src/traits/tokens/fungibles/imbalance.rs b/substrate/frame/support/src/traits/tokens/fungibles/imbalance.rs index 17e22cf917e7..c1880cf087a5 100644 --- a/substrate/frame/support/src/traits/tokens/fungibles/imbalance.rs +++ b/substrate/frame/support/src/traits/tokens/fungibles/imbalance.rs @@ -38,7 +38,6 @@ pub trait HandleImbalanceDrop { /// /// Importantly, it has a special `Drop` impl, and cannot be created outside of this module. #[must_use] -#[derive(Eq, PartialEq)] pub struct Imbalance< A: AssetId, B: Balance, @@ -158,6 +157,27 @@ impl< } } +impl< + A: AssetId, + B: Balance, + OnDrop: HandleImbalanceDrop, + OppositeOnDrop: HandleImbalanceDrop, + > PartialEq for Imbalance +{ + fn eq(&self, other: &Self) -> bool { + self.amount.eq(&other.amount) && self.asset.eq(&other.asset) + } +} + +impl< + A: AssetId, + B: Balance, + OnDrop: HandleImbalanceDrop, + OppositeOnDrop: HandleImbalanceDrop, + > Eq for Imbalance +{ +} + #[cfg(any(feature = "std", feature = "force-debug"))] impl< A: AssetId, @@ -170,6 +190,8 @@ impl< fmt.debug_struct("Imbalance") .field("asset", &self.asset) .field("amount", &self.amount) + .field("OnDrop", &sp_std::any::type_name::()) + .field("OppositeOnDrop", &sp_std::any::type_name::()) .finish() } } From 827ce1bf93049f74f0c53e619063be531443419b Mon Sep 17 00:00:00 2001 From: muharem Date: Thu, 28 Sep 2023 13:20:41 +0200 Subject: [PATCH 16/98] eq, partial_eq, runtime debug for credit --- substrate/frame/asset-conversion/src/lib.rs | 2 +- substrate/frame/asset-conversion/src/types.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/substrate/frame/asset-conversion/src/lib.rs b/substrate/frame/asset-conversion/src/lib.rs index a3fa1464dd52..b1fd109aa8fe 100644 --- a/substrate/frame/asset-conversion/src/lib.rs +++ b/substrate/frame/asset-conversion/src/lib.rs @@ -94,7 +94,7 @@ use sp_runtime::{ CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Ensure, IntegerSquareRoot, MaybeDisplay, One, TrailingZeroInput, Zero, }, - BoundedVec, DispatchError, Saturating, TokenError, TransactionOutcome, + BoundedVec, DispatchError, RuntimeDebug, Saturating, TokenError, TransactionOutcome, }; use sp_std::vec::Vec; diff --git a/substrate/frame/asset-conversion/src/types.rs b/substrate/frame/asset-conversion/src/types.rs index 1b921a757a5a..466faf434031 100644 --- a/substrate/frame/asset-conversion/src/types.rs +++ b/substrate/frame/asset-conversion/src/types.rs @@ -179,6 +179,7 @@ pub type AssetCredit = /// /// Implies a negative imbalance in the system that can be placed into an account or alter the total /// supply. +#[derive(RuntimeDebug, Eq, PartialEq)] pub enum Credit { /// Native credit. Native(NativeCredit), From d533acf69137a79c8fe79f84e6053f0b06f1bff0 Mon Sep 17 00:00:00 2001 From: muharem Date: Thu, 28 Sep 2023 13:21:10 +0200 Subject: [PATCH 17/98] tests --- substrate/frame/asset-conversion/src/tests.rs | 404 +++++++++++++++++- 1 file changed, 403 insertions(+), 1 deletion(-) diff --git a/substrate/frame/asset-conversion/src/tests.rs b/substrate/frame/asset-conversion/src/tests.rs index 6503c4b7d8c0..f86cb6c12d75 100644 --- a/substrate/frame/asset-conversion/src/tests.rs +++ b/substrate/frame/asset-conversion/src/tests.rs @@ -17,7 +17,7 @@ use crate::{mock::*, *}; use frame_support::{ - assert_noop, assert_ok, + assert_noop, assert_ok, assert_storage_noop, instances::Instance1, traits::{fungible, fungibles, fungibles::InspectEnumerable, Get}, }; @@ -1875,3 +1875,405 @@ fn cannot_block_pool_creation() { )); }); } + +#[test] +fn swap_transactional() { + new_test_ext().execute_with(|| { + let user = 1; + let user2 = 2; + let token_1 = NativeOrAssetId::Native; + let token_2 = NativeOrAssetId::Asset(2); + let token_3 = NativeOrAssetId::Asset(3); + + let asset_ed = 150; + create_tokens_with_ed(user, vec![token_2, token_3], asset_ed); + assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), token_1, token_2)); + assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), token_1, token_3)); + + let ed = get_ed(); + assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 20000 + ed)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user, 1000)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 3, user, 1000)); + + assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user2, 20000 + ed)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user2, 1000)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 3, user2, 1000)); + + let liquidity1 = 10000; + let liquidity2 = 200; + + assert_ok!(AssetConversion::add_liquidity( + RuntimeOrigin::signed(user), + token_1, + token_2, + liquidity1, + liquidity2, + 1, + 1, + user, + )); + + assert_ok!(AssetConversion::add_liquidity( + RuntimeOrigin::signed(user), + token_1, + token_3, + liquidity1, + liquidity2, + 1, + 1, + user, + )); + + let pool_1 = AssetConversion::get_pool_account(&(token_1, token_2)); + let pool_2 = AssetConversion::get_pool_account(&(token_1, token_3)); + + assert_eq!(Balances::balance(&pool_1), liquidity1); + assert_eq!(Assets::balance(2, &pool_1), liquidity2); + assert_eq!(Balances::balance(&pool_2), liquidity1); + assert_eq!(Assets::balance(3, &pool_2), liquidity2); + + // the amount that would cause a transfer from the last pool in the path to fail + let expected_out = liquidity2 - asset_ed + 1; + let amount_in = AssetConversion::balance_path_from_amount_out( + expected_out, + vec![token_2, token_1, token_3].try_into().unwrap(), + ) + .unwrap() + .first() + .map(|(_, a)| *a) + .unwrap(); + + // swap credit with `swap_tokens_for_exact_tokens` transactional + let credit_in = Assets::issue(2, amount_in); + let credit_in_err_expected = Assets::issue(2, amount_in); + // avoiding drop of any credit, to assert any storage mutation from an actual call. + let error; + assert_storage_noop!( + error = >::swap_tokens_for_exact_tokens( + bvec![token_2, token_1, token_3], + credit_in.into(), + expected_out, + ) + .unwrap_err() + ); + assert_eq!(error, (credit_in_err_expected.into(), TokenError::NotExpendable.into())); + + // swap credit with `swap_exact_tokens_for_tokens` transactional + let credit_in = Assets::issue(2, amount_in); + let credit_in_err_expected = Assets::issue(2, amount_in); + // avoiding drop of any credit, to assert any storage mutation from an actual call. + let error; + assert_storage_noop!( + error = >::swap_exact_tokens_for_tokens( + bvec![token_2, token_1, token_3], + credit_in.into(), + Some(expected_out), + ) + .unwrap_err() + ); + assert_eq!(error, (credit_in_err_expected.into(), TokenError::NotExpendable.into())); + + // swap with `swap_exact_tokens_for_tokens` transactional + assert_noop!( + >::swap_exact_tokens_for_tokens( + user2, + bvec![token_2, token_1, token_3], + amount_in.into(), + Some(expected_out.into()), + user2, + true, + ), + TokenError::NotExpendable + ); + + // swap with `swap_exact_tokens_for_tokens` transactional + assert_noop!( + >::swap_tokens_for_exact_tokens( + user2, + bvec![token_2, token_1, token_3], + expected_out.into(), + Some(amount_in.into()), + user2, + true, + ), + TokenError::NotExpendable + ); + + assert_eq!(Balances::balance(&pool_1), liquidity1); + assert_eq!(Assets::balance(2, &pool_1), liquidity2); + assert_eq!(Balances::balance(&pool_2), liquidity1); + assert_eq!(Assets::balance(3, &pool_2), liquidity2); + }) +} + +#[test] +fn swap_credit_returns_change() { + new_test_ext().execute_with(|| { + let user = 1; + let token_1 = NativeOrAssetId::Native; + let token_2 = NativeOrAssetId::Asset(2); + + create_tokens(user, vec![token_2]); + assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), token_1, token_2)); + + let ed = get_ed(); + assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 20000 + ed)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user, 1000)); + + let liquidity1 = 10000; + let liquidity2 = 200; + + assert_ok!(AssetConversion::add_liquidity( + RuntimeOrigin::signed(user), + token_1, + token_2, + liquidity1, + liquidity2, + 1, + 1, + user, + )); + + let expected_change = Balances::issue(100); + let expected_credit_out = Assets::issue(2, 20); + + let amount_in_max = + AssetConversion::get_amount_in(&expected_credit_out.peek(), &liquidity1, &liquidity2) + .unwrap(); + + let credit_in = Balances::issue(amount_in_max + expected_change.peek()); + assert_ok!( + >::swap_tokens_for_exact_tokens( + bvec![token_1, token_2], + credit_in.into(), + expected_credit_out.peek(), + ), + (expected_credit_out.into(), expected_change.into()) + ); + }) +} + +#[test] +fn swap_credit_insufficient_amount_bounds() { + new_test_ext().execute_with(|| { + let user = 1; + let user2 = 2; + let token_1 = NativeOrAssetId::Native; + let token_2 = NativeOrAssetId::Asset(2); + + create_tokens(user, vec![token_2]); + assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), token_1, token_2)); + + let ed = get_ed(); + assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 20000 + ed)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user, 1000)); + + assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user2, 20000 + ed)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user2, 1000)); + + let liquidity1 = 10000; + let liquidity2 = 200; + + assert_ok!(AssetConversion::add_liquidity( + RuntimeOrigin::signed(user), + token_1, + token_2, + liquidity1, + liquidity2, + 1, + 1, + user, + )); + + // provided `credit_in` is not sufficient to swap for desired `amount_out_min` + let amount_out_min = 20; + let amount_in = + AssetConversion::get_amount_in(&(amount_out_min - 1), &liquidity2, &liquidity1) + .unwrap(); + let credit_in = Balances::issue(amount_in); + let expected_credit_in = Balances::issue(amount_in); + let error = >::swap_exact_tokens_for_tokens( + bvec![token_1, token_2], + credit_in.into(), + Some(amount_out_min), + ) + .unwrap_err(); + assert_eq!( + error, + (expected_credit_in.into(), Error::::ProvidedMinimumNotSufficientForSwap.into()) + ); + + // provided `credit_in` is not sufficient to swap for desired `amount_out` + let amount_out = 20; + let amount_in_max = + AssetConversion::get_amount_in(&(amount_out - 1), &liquidity2, &liquidity1).unwrap(); + let credit_in = Balances::issue(amount_in_max); + let expected_credit_in = Balances::issue(amount_in_max); + let error = >::swap_tokens_for_exact_tokens( + bvec![token_1, token_2], + credit_in.into(), + amount_out, + ) + .unwrap_err(); + assert_eq!( + error, + (expected_credit_in.into(), Error::::ProvidedMaximumNotSufficientForSwap.into()) + ); + }) +} + +#[test] +fn swap_credit_zero_amount() { + new_test_ext().execute_with(|| { + let user = 1; + let user2 = 2; + let token_1 = NativeOrAssetId::Native; + let token_2 = NativeOrAssetId::Asset(2); + + create_tokens(user, vec![token_2]); + assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), token_1, token_2)); + + let ed = get_ed(); + assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 20000 + ed)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user, 1000)); + + assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user2, 20000 + ed)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user2, 1000)); + + let liquidity1 = 10000; + let liquidity2 = 200; + + assert_ok!(AssetConversion::add_liquidity( + RuntimeOrigin::signed(user), + token_1, + token_2, + liquidity1, + liquidity2, + 1, + 1, + user, + )); + + // swap with zero credit fails for `swap_exact_tokens_for_tokens` + let credit_in = Credit::native_zero(); + let expected_credit_in = Credit::native_zero(); + let error = >::swap_exact_tokens_for_tokens( + bvec![token_1, token_2], + credit_in, + None, + ) + .unwrap_err(); + assert_eq!(error, (expected_credit_in, Error::::ZeroAmount.into())); + + // swap with zero credit fails for `swap_tokens_for_exact_tokens` + let credit_in = Credit::native_zero(); + let expected_credit_in = Credit::native_zero(); + let error = >::swap_tokens_for_exact_tokens( + bvec![token_1, token_2], + credit_in, + 10, + ) + .unwrap_err(); + assert_eq!(error, (expected_credit_in, Error::::ZeroAmount.into())); + + // swap with zero amount_out_min fails for `swap_exact_tokens_for_tokens` + let credit_in = Balances::issue(10); + let expected_credit_in = Balances::issue(10); + let error = >::swap_exact_tokens_for_tokens( + bvec![token_1, token_2], + credit_in.into(), + Some(0), + ) + .unwrap_err(); + assert_eq!(error, (expected_credit_in.into(), Error::::ZeroAmount.into())); + + // swap with zero amount_out fails with `swap_tokens_for_exact_tokens` fails + let credit_in = Balances::issue(10); + let expected_credit_in = Balances::issue(10); + let error = >::swap_tokens_for_exact_tokens( + bvec![token_1, token_2], + credit_in.into(), + 0, + ) + .unwrap_err(); + assert_eq!(error, (expected_credit_in.into(), Error::::ZeroAmount.into())); + }); +} + +#[test] +fn swap_credit_invalid_path() { + new_test_ext().execute_with(|| { + let user = 1; + let user2 = 2; + let token_1 = NativeOrAssetId::Native; + let token_2 = NativeOrAssetId::Asset(2); + + create_tokens(user, vec![token_2]); + assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), token_1, token_2)); + + let ed = get_ed(); + assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 20000 + ed)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user, 1000)); + + assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user2, 20000 + ed)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user2, 1000)); + + let liquidity1 = 10000; + let liquidity2 = 200; + + assert_ok!(AssetConversion::add_liquidity( + RuntimeOrigin::signed(user), + token_1, + token_2, + liquidity1, + liquidity2, + 1, + 1, + user, + )); + + // swap with credit_in.asset different from path[0] asset fails + let credit_in = Balances::issue(10); + let expected_credit_in = Balances::issue(10); + let error = >::swap_exact_tokens_for_tokens( + bvec![token_2, token_1], + credit_in.into(), + None, + ) + .unwrap_err(); + assert_eq!(error, (expected_credit_in.into(), Error::::InvalidPath.into())); + + // swap with credit_in.asset different from path[0] asset fails + let credit_in = Assets::issue(2, 10); + let expected_credit_in = Assets::issue(2, 10); + let error = >::swap_tokens_for_exact_tokens( + bvec![token_1, token_2], + credit_in.into(), + 10, + ) + .unwrap_err(); + assert_eq!(error, (expected_credit_in.into(), Error::::InvalidPath.into())); + + // swap with path.len < 2 fails + let credit_in = Balances::issue(10); + let expected_credit_in = Balances::issue(10); + let error = >::swap_exact_tokens_for_tokens( + bvec![token_2], + credit_in.into(), + None, + ) + .unwrap_err(); + assert_eq!(error, (expected_credit_in.into(), Error::::InvalidPath.into())); + + // swap with path.len < 2 fails + let credit_in = Assets::issue(2, 10); + let expected_credit_in = Assets::issue(2, 10); + let error = >::swap_tokens_for_exact_tokens( + bvec![], + credit_in.into(), + 10, + ) + .unwrap_err(); + assert_eq!(error, (expected_credit_in.into(), Error::::InvalidPath.into())); + }); +} From 69b17f81634ed41daf4f922ae7159e93961dc771 Mon Sep 17 00:00:00 2001 From: Muharem Date: Thu, 28 Sep 2023 13:24:07 +0200 Subject: [PATCH 18/98] Apply suggestions from code review Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> --- substrate/frame/asset-conversion/src/lib.rs | 4 ++-- substrate/frame/asset-conversion/src/swap.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/substrate/frame/asset-conversion/src/lib.rs b/substrate/frame/asset-conversion/src/lib.rs index b1fd109aa8fe..33c761611e25 100644 --- a/substrate/frame/asset-conversion/src/lib.rs +++ b/substrate/frame/asset-conversion/src/lib.rs @@ -283,7 +283,7 @@ pub mod pallet { amount_in: T::AssetBalance, /// The amount of the second asset that was received. amount_out: T::AssetBalance, - /// The route of asset ids with amounts that the swap went through. + /// The route of asset IDs with amounts that the swap went through. /// E.g. (A, amount_in) -> (Dot, amount_out) -> (B, amount_out) path: BalancePath, }, @@ -293,7 +293,7 @@ pub mod pallet { amount_in: T::AssetBalance, /// The amount of the second asset that was received. amount_out: T::AssetBalance, - /// The route of asset ids with amounts that the swap went through. + /// The route of asset IDs with amounts that the swap went through. /// E.g. (A, amount_in) -> (Dot, amount_out) -> (B, amount_out) path: BalancePath, }, diff --git a/substrate/frame/asset-conversion/src/swap.rs b/substrate/frame/asset-conversion/src/swap.rs index 2bff86392b38..fed03363b497 100644 --- a/substrate/frame/asset-conversion/src/swap.rs +++ b/substrate/frame/asset-conversion/src/swap.rs @@ -24,7 +24,7 @@ pub trait Swap { /// Returns the upper limit on the length of the swap path. fn max_path_len() -> u32; - /// Swap exactly `amount_in` of asset `path[0]` for asset `path[1]`. + /// Swap exactly `amount_in` of asset `path[0]` for asset `path[last]`. /// If an `amount_out_min` is specified, it will return an error if it is unable to acquire /// the amount desired. /// From 28edff58cee8ef6026ad2273414d83fa51f89ed4 Mon Sep 17 00:00:00 2001 From: muharem Date: Thu, 28 Sep 2023 13:26:23 +0200 Subject: [PATCH 19/98] correct docs --- substrate/frame/asset-conversion/src/swap.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/substrate/frame/asset-conversion/src/swap.rs b/substrate/frame/asset-conversion/src/swap.rs index fed03363b497..ec1f03a461b1 100644 --- a/substrate/frame/asset-conversion/src/swap.rs +++ b/substrate/frame/asset-conversion/src/swap.rs @@ -28,10 +28,10 @@ pub trait Swap { /// If an `amount_out_min` is specified, it will return an error if it is unable to acquire /// the amount desired. /// - /// Withdraws the `path[0]` asset from `sender`, deposits the `path[1]` asset to `send_to`, + /// Withdraws the `path[0]` asset from `sender`, deposits the `path[last]` asset to `send_to`, /// respecting `keep_alive`. /// - /// If successful, returns the amount of `path[1]` acquired for the `amount_in`. + /// If successful, returns the amount of `path[last]` acquired for the `amount_in`. fn swap_exact_tokens_for_tokens( sender: AccountId, path: Vec, @@ -41,14 +41,14 @@ pub trait Swap { keep_alive: bool, ) -> Result; - /// Take the `path[0]` asset and swap some amount for `amount_out` of the `path[1]`. If an + /// Take the `path[0]` asset and swap some amount for `amount_out` of the `path[last]`. If an /// `amount_in_max` is specified, it will return an error if acquiring `amount_out` would be /// too costly. /// - /// Withdraws `path[0]` asset from `sender`, deposits `path[1]` asset to `send_to`, + /// Withdraws `path[0]` asset from `sender`, deposits `path[last]` asset to `send_to`, /// respecting `keep_alive`. /// - /// If successful returns the amount of the `path[0]` taken to provide `path[1]`. + /// If successful returns the amount of the `path[0]` taken to provide `path[last]`. fn swap_tokens_for_exact_tokens( sender: AccountId, path: Vec, From 697afa6b1ef1c2bfe4b774b648cd59dc0fb492ba Mon Sep 17 00:00:00 2001 From: muharem Date: Thu, 28 Sep 2023 13:32:50 +0200 Subject: [PATCH 20/98] dev comments --- substrate/frame/asset-conversion/src/swap.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/substrate/frame/asset-conversion/src/swap.rs b/substrate/frame/asset-conversion/src/swap.rs index ec1f03a461b1..e65ea08c36b5 100644 --- a/substrate/frame/asset-conversion/src/swap.rs +++ b/substrate/frame/asset-conversion/src/swap.rs @@ -175,6 +175,8 @@ impl SwapCredit for Pallet { Self::do_swap_exact_credit_tokens_for_tokens(path, credit_in, amount_out_min); match &res { Ok(_) => TransactionOutcome::Commit(Ok(res)), + // wrapping `res` with `Ok`, since our `Err` doesn't satisfy the + // `From` bound of the `with_transaction` function. Err(_) => TransactionOutcome::Rollback(Ok(res)), } }); @@ -199,6 +201,8 @@ impl SwapCredit for Pallet { let res = Self::do_swap_credit_tokens_for_exact_tokens(path, credit_in, amount_out); match &res { Ok(_) => TransactionOutcome::Commit(Ok(res)), + // wrapping `res` with `Ok`, since our `Err` doesn't satisfy the + // `From` bound of the `with_transaction` function. Err(_) => TransactionOutcome::Rollback(Ok(res)), } }); From 1c5022014cd21cc698406684209dc88edeb2aee4 Mon Sep 17 00:00:00 2001 From: muharem Date: Fri, 29 Sep 2023 17:50:54 +0200 Subject: [PATCH 21/98] use eq/partial_eq/runtime_debug derives with no bound version --- .../src/traits/tokens/fungible/imbalance.rs | 37 +------------ .../src/traits/tokens/fungibles/imbalance.rs | 54 +------------------ 2 files changed, 4 insertions(+), 87 deletions(-) diff --git a/substrate/frame/support/src/traits/tokens/fungible/imbalance.rs b/substrate/frame/support/src/traits/tokens/fungible/imbalance.rs index 49b0b506db09..32a63fd25b29 100644 --- a/substrate/frame/support/src/traits/tokens/fungible/imbalance.rs +++ b/substrate/frame/support/src/traits/tokens/fungible/imbalance.rs @@ -23,6 +23,7 @@ use crate::traits::{ misc::{SameOrOther, TryDrop}, tokens::Balance, }; +use frame_support_procedural::{EqNoBound, PartialEqNoBound, RuntimeDebugNoBound}; use sp_runtime::traits::Zero; use sp_std::marker::PhantomData; @@ -43,6 +44,7 @@ impl HandleImbalanceDrop for () { /// /// Importantly, it has a special `Drop` impl, and cannot be created outside of this module. #[must_use] +#[derive(EqNoBound, PartialEqNoBound, RuntimeDebugNoBound)] pub struct Imbalance< B: Balance, OnDrop: HandleImbalanceDrop, @@ -140,41 +142,6 @@ impl, OppositeOnDrop: HandleImbalance } } -impl, OppositeOnDrop: HandleImbalanceDrop> PartialEq - for Imbalance -{ - fn eq(&self, other: &Self) -> bool { - self.amount.eq(&other.amount) - } -} - -impl, OppositeOnDrop: HandleImbalanceDrop> Eq - for Imbalance -{ -} - -#[cfg(any(feature = "std", feature = "force-debug"))] -impl, OppositeOnDrop: HandleImbalanceDrop> - sp_std::fmt::Debug for Imbalance -{ - fn fmt(&self, fmt: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { - fmt.debug_struct("Imbalance") - .field("amount", &self.amount) - .field("OnDrop", &sp_std::any::type_name::()) - .field("OppositeOnDrop", &sp_std::any::type_name::()) - .finish() - } -} - -#[cfg(all(not(feature = "std"), not(feature = "force-debug")))] -impl, OppositeOnDrop: HandleImbalanceDrop> - sp_std::fmt::Debug for Imbalance -{ - fn fmt(&self, fmt: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { - fmt.write_str("") - } -} - /// Imbalance implying that the total_issuance value is less than the sum of all account balances. pub type Debt = Imbalance< >::Balance, diff --git a/substrate/frame/support/src/traits/tokens/fungibles/imbalance.rs b/substrate/frame/support/src/traits/tokens/fungibles/imbalance.rs index c1880cf087a5..9f660a5f1689 100644 --- a/substrate/frame/support/src/traits/tokens/fungibles/imbalance.rs +++ b/substrate/frame/support/src/traits/tokens/fungibles/imbalance.rs @@ -23,6 +23,7 @@ use crate::traits::{ misc::{SameOrOther, TryDrop}, tokens::{AssetId, Balance}, }; +use frame_support_procedural::{EqNoBound, PartialEqNoBound, RuntimeDebugNoBound}; use sp_runtime::traits::Zero; use sp_std::marker::PhantomData; @@ -38,6 +39,7 @@ pub trait HandleImbalanceDrop { /// /// Importantly, it has a special `Drop` impl, and cannot be created outside of this module. #[must_use] +#[derive(EqNoBound, PartialEqNoBound, RuntimeDebugNoBound)] pub struct Imbalance< A: AssetId, B: Balance, @@ -157,58 +159,6 @@ impl< } } -impl< - A: AssetId, - B: Balance, - OnDrop: HandleImbalanceDrop, - OppositeOnDrop: HandleImbalanceDrop, - > PartialEq for Imbalance -{ - fn eq(&self, other: &Self) -> bool { - self.amount.eq(&other.amount) && self.asset.eq(&other.asset) - } -} - -impl< - A: AssetId, - B: Balance, - OnDrop: HandleImbalanceDrop, - OppositeOnDrop: HandleImbalanceDrop, - > Eq for Imbalance -{ -} - -#[cfg(any(feature = "std", feature = "force-debug"))] -impl< - A: AssetId, - B: Balance, - OnDrop: HandleImbalanceDrop, - OppositeOnDrop: HandleImbalanceDrop, - > sp_std::fmt::Debug for Imbalance -{ - fn fmt(&self, fmt: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { - fmt.debug_struct("Imbalance") - .field("asset", &self.asset) - .field("amount", &self.amount) - .field("OnDrop", &sp_std::any::type_name::()) - .field("OppositeOnDrop", &sp_std::any::type_name::()) - .finish() - } -} - -#[cfg(all(not(feature = "std"), not(feature = "force-debug")))] -impl< - A: AssetId, - B: Balance, - OnDrop: HandleImbalanceDrop, - OppositeOnDrop: HandleImbalanceDrop, - > sp_std::fmt::Debug for Imbalance -{ - fn fmt(&self, fmt: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { - fmt.write_str("") - } -} - /// Imbalance implying that the total_issuance value is less than the sum of all account balances. pub type Debt = Imbalance< >::AssetId, From e7ffbb412cd62d47ecac5f2906f419755410df52 Mon Sep 17 00:00:00 2001 From: muharem Date: Thu, 5 Oct 2023 13:39:17 +0200 Subject: [PATCH 22/98] swap trait update --- substrate/frame/asset-conversion/src/swap.rs | 44 +++++++++++-------- substrate/frame/asset-conversion/src/tests.rs | 4 +- .../src/payment.rs | 2 +- 3 files changed, 29 insertions(+), 21 deletions(-) diff --git a/substrate/frame/asset-conversion/src/swap.rs b/substrate/frame/asset-conversion/src/swap.rs index e65ea08c36b5..92cd4c8877a5 100644 --- a/substrate/frame/asset-conversion/src/swap.rs +++ b/substrate/frame/asset-conversion/src/swap.rs @@ -20,7 +20,12 @@ use super::*; /// Trait for providing methods to swap between the various asset classes. -pub trait Swap { +pub trait Swap { + /// Measure units of the asset classes for swapping. + type Balance; + /// Kind of assets that are going to be swapped. + type MultiAssetId; + /// Returns the upper limit on the length of the swap path. fn max_path_len() -> u32; @@ -34,12 +39,12 @@ pub trait Swap { /// If successful, returns the amount of `path[last]` acquired for the `amount_in`. fn swap_exact_tokens_for_tokens( sender: AccountId, - path: Vec, - amount_in: Balance, - amount_out_min: Option, + path: Vec, + amount_in: Self::Balance, + amount_out_min: Option, send_to: AccountId, keep_alive: bool, - ) -> Result; + ) -> Result; /// Take the `path[0]` asset and swap some amount for `amount_out` of the `path[last]`. If an /// `amount_in_max` is specified, it will return an error if acquiring `amount_out` would be @@ -51,12 +56,12 @@ pub trait Swap { /// If successful returns the amount of the `path[0]` taken to provide `path[last]`. fn swap_tokens_for_exact_tokens( sender: AccountId, - path: Vec, - amount_out: Balance, - amount_in_max: Option, + path: Vec, + amount_out: Self::Balance, + amount_in_max: Option, send_to: AccountId, keep_alive: bool, - ) -> Result; + ) -> Result; } /// Trait providing methods to swap between the various asset classes. @@ -99,19 +104,22 @@ pub trait SwapCredit { ) -> Result<(Self::Credit, Self::Credit), (Self::Credit, DispatchError)>; } -impl Swap for Pallet { +impl Swap for Pallet { + type Balance = T::HigherPrecisionBalance; + type MultiAssetId = T::MultiAssetId; + fn max_path_len() -> u32 { T::MaxSwapPathLength::get() } fn swap_exact_tokens_for_tokens( sender: T::AccountId, - path: Vec, - amount_in: T::HigherPrecisionBalance, - amount_out_min: Option, + path: Vec, + amount_in: Self::Balance, + amount_out_min: Option, send_to: T::AccountId, keep_alive: bool, - ) -> Result { + ) -> Result { let path = path.try_into().map_err(|_| Error::::PathError)?; let amount_out_min = amount_out_min.map(Self::convert_hpb_to_asset_balance).transpose()?; let amount_out = with_storage_layer(|| { @@ -129,12 +137,12 @@ impl Swap f fn swap_tokens_for_exact_tokens( sender: T::AccountId, - path: Vec, - amount_out: T::HigherPrecisionBalance, - amount_in_max: Option, + path: Vec, + amount_out: Self::Balance, + amount_in_max: Option, send_to: T::AccountId, keep_alive: bool, - ) -> Result { + ) -> Result { let path = path.try_into().map_err(|_| Error::::PathError)?; let amount_in_max = amount_in_max.map(Self::convert_hpb_to_asset_balance).transpose()?; let amount_in = with_storage_layer(|| { diff --git a/substrate/frame/asset-conversion/src/tests.rs b/substrate/frame/asset-conversion/src/tests.rs index f86cb6c12d75..05bdaf1018ed 100644 --- a/substrate/frame/asset-conversion/src/tests.rs +++ b/substrate/frame/asset-conversion/src/tests.rs @@ -1975,7 +1975,7 @@ fn swap_transactional() { // swap with `swap_exact_tokens_for_tokens` transactional assert_noop!( - >::swap_exact_tokens_for_tokens( + >::swap_exact_tokens_for_tokens( user2, bvec![token_2, token_1, token_3], amount_in.into(), @@ -1988,7 +1988,7 @@ fn swap_transactional() { // swap with `swap_exact_tokens_for_tokens` transactional assert_noop!( - >::swap_tokens_for_exact_tokens( + >::swap_tokens_for_exact_tokens( user2, bvec![token_2, token_1, token_3], expected_out.into(), diff --git a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/payment.rs b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/payment.rs index 0d090211d035..ac13e121dd65 100644 --- a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/payment.rs +++ b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/payment.rs @@ -83,7 +83,7 @@ impl OnChargeAssetTransaction for AssetConversionAdapter where T: Config, C: Inspect<::AccountId>, - CON: Swap, + CON: Swap, T::HigherPrecisionBalance: From> + TryInto>, T::MultiAssetId: From>, BalanceOf: IsType<::AccountId>>::Balance>, From ee49f271e5187a3ade386066c14ca43332ad8734 Mon Sep 17 00:00:00 2001 From: muharem Date: Sat, 7 Oct 2023 16:23:36 +0200 Subject: [PATCH 23/98] box multi asset id --- substrate/frame/asset-conversion/src/lib.rs | 38 +- substrate/frame/asset-conversion/src/tests.rs | 475 ++++++++++++------ 2 files changed, 350 insertions(+), 163 deletions(-) diff --git a/substrate/frame/asset-conversion/src/lib.rs b/substrate/frame/asset-conversion/src/lib.rs index 33c761611e25..356b7f41e924 100644 --- a/substrate/frame/asset-conversion/src/lib.rs +++ b/substrate/frame/asset-conversion/src/lib.rs @@ -381,14 +381,14 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::create_pool())] pub fn create_pool( origin: OriginFor, - asset1: T::MultiAssetId, - asset2: T::MultiAssetId, + asset1: Box, + asset2: Box, ) -> DispatchResult { let sender = ensure_signed(origin)?; ensure!(asset1 != asset2, Error::::EqualAssets); // prepare pool_id - let pool_id = Self::get_pool_id(asset1, asset2); + let pool_id = Self::get_pool_id(*asset1, *asset2); ensure!(!Pools::::contains_key(&pool_id), Error::::PoolExists); let (asset1, asset2) = &pool_id; if !T::AllowMultiAssetPools::get() && !T::MultiAssetIdConverter::is_native(asset1) { @@ -459,8 +459,8 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::add_liquidity())] pub fn add_liquidity( origin: OriginFor, - asset1: T::MultiAssetId, - asset2: T::MultiAssetId, + asset1: Box, + asset2: Box, amount1_desired: T::AssetBalance, amount2_desired: T::AssetBalance, amount1_min: T::AssetBalance, @@ -469,10 +469,10 @@ pub mod pallet { ) -> DispatchResult { let sender = ensure_signed(origin)?; - let pool_id = Self::get_pool_id(asset1.clone(), asset2.clone()); + let pool_id = Self::get_pool_id(*asset1.clone(), *asset2); // swap params if needed let (amount1_desired, amount2_desired, amount1_min, amount2_min) = - if pool_id.0 == asset1 { + if pool_id.0 == *asset1 { (amount1_desired, amount2_desired, amount1_min, amount2_min) } else { (amount2_desired, amount1_desired, amount2_min, amount1_min) @@ -571,8 +571,8 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::remove_liquidity())] pub fn remove_liquidity( origin: OriginFor, - asset1: T::MultiAssetId, - asset2: T::MultiAssetId, + asset1: Box, + asset2: Box, lp_token_burn: T::AssetBalance, amount1_min_receive: T::AssetBalance, amount2_min_receive: T::AssetBalance, @@ -580,9 +580,9 @@ pub mod pallet { ) -> DispatchResult { let sender = ensure_signed(origin)?; - let pool_id = Self::get_pool_id(asset1.clone(), asset2.clone()); + let pool_id = Self::get_pool_id(*asset1.clone(), *asset2); // swap params if needed - let (amount1_min_receive, amount2_min_receive) = if pool_id.0 == asset1 { + let (amount1_min_receive, amount2_min_receive) = if pool_id.0 == *asset1 { (amount1_min_receive, amount2_min_receive) } else { (amount2_min_receive, amount1_min_receive) @@ -650,7 +650,7 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::swap_exact_tokens_for_tokens())] pub fn swap_exact_tokens_for_tokens( origin: OriginFor, - path: BoundedVec, + path: BoundedVec, T::MaxSwapPathLength>, amount_in: T::AssetBalance, amount_out_min: T::AssetBalance, send_to: T::AccountId, @@ -659,7 +659,11 @@ pub mod pallet { let sender = ensure_signed(origin)?; Self::do_swap_exact_tokens_for_tokens( sender, - path, + path.into_iter() + .map(|a| *a) + .collect::>() + .try_into() + .map_err(|_| Error::::InvalidPath)?, amount_in, Some(amount_out_min), send_to, @@ -678,7 +682,7 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::swap_tokens_for_exact_tokens())] pub fn swap_tokens_for_exact_tokens( origin: OriginFor, - path: BoundedVec, + path: BoundedVec, T::MaxSwapPathLength>, amount_out: T::AssetBalance, amount_in_max: T::AssetBalance, send_to: T::AccountId, @@ -687,7 +691,11 @@ pub mod pallet { let sender = ensure_signed(origin)?; Self::do_swap_tokens_for_exact_tokens( sender, - path, + path.into_iter() + .map(|a| *a) + .collect::>() + .try_into() + .map_err(|_| Error::::InvalidPath)?, amount_out, Some(amount_in_max), send_to, diff --git a/substrate/frame/asset-conversion/src/tests.rs b/substrate/frame/asset-conversion/src/tests.rs index 05bdaf1018ed..d6a041f98e0c 100644 --- a/substrate/frame/asset-conversion/src/tests.rs +++ b/substrate/frame/asset-conversion/src/tests.rs @@ -100,6 +100,12 @@ macro_rules! bvec { } } +macro_rules! bbvec { + ($( $x:ident ),*) => { + vec![$( Box::new( $x ), )*].try_into().unwrap() + } +} + #[test] fn check_pool_accounts_dont_collide() { use std::collections::HashSet; @@ -149,7 +155,11 @@ fn can_create_pool() { let lp_token = AssetConversion::get_next_pool_asset_id(); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 1000)); - assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), token_2, token_1)); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(user), + Box::new(token_2), + Box::new(token_1) + )); let setup_fee = <::PoolSetupFee as Get<::Balance>>::get(); let pool_account = <::PoolSetupFeeReceiver as Get>::get(); @@ -174,24 +184,40 @@ fn can_create_pool() { assert_eq!(pool_assets(), vec![lp_token]); assert_noop!( - AssetConversion::create_pool(RuntimeOrigin::signed(user), token_1, token_1), + AssetConversion::create_pool( + RuntimeOrigin::signed(user), + Box::new(token_1), + Box::new(token_1) + ), Error::::EqualAssets ); assert_noop!( - AssetConversion::create_pool(RuntimeOrigin::signed(user), token_2, token_2), + AssetConversion::create_pool( + RuntimeOrigin::signed(user), + Box::new(token_2), + Box::new(token_2) + ), Error::::EqualAssets ); // validate we can create Asset(1)/Asset(2) pool let token_1 = NativeOrAssetId::Asset(1); create_tokens(user, vec![token_1]); - assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), token_1, token_2)); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(user), + Box::new(token_1), + Box::new(token_2) + )); // validate we can force the first asset to be the Native currency only AllowMultiAssetPools::set(&false); let token_1 = NativeOrAssetId::Asset(3); assert_noop!( - AssetConversion::create_pool(RuntimeOrigin::signed(user), token_1, token_2), + AssetConversion::create_pool( + RuntimeOrigin::signed(user), + Box::new(token_1), + Box::new(token_2) + ), Error::::PoolMustContainNativeCurrency ); }); @@ -207,19 +233,31 @@ fn create_same_pool_twice_should_fail() { create_tokens(user, vec![token_2]); let lp_token = AssetConversion::get_next_pool_asset_id(); - assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), token_2, token_1)); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(user), + Box::new(token_2), + Box::new(token_1) + )); let expected_free = lp_token + 1; assert_eq!(expected_free, AssetConversion::get_next_pool_asset_id()); assert_noop!( - AssetConversion::create_pool(RuntimeOrigin::signed(user), token_2, token_1), + AssetConversion::create_pool( + RuntimeOrigin::signed(user), + Box::new(token_2), + Box::new(token_1) + ), Error::::PoolExists ); assert_eq!(expected_free, AssetConversion::get_next_pool_asset_id()); // Try switching the same tokens around: assert_noop!( - AssetConversion::create_pool(RuntimeOrigin::signed(user), token_1, token_2), + AssetConversion::create_pool( + RuntimeOrigin::signed(user), + Box::new(token_1), + Box::new(token_2) + ), Error::::PoolExists ); assert_eq!(expected_free, AssetConversion::get_next_pool_asset_id()); @@ -239,7 +277,11 @@ fn different_pools_should_have_different_lp_tokens() { create_tokens(user, vec![token_2, token_3]); let lp_token2_1 = AssetConversion::get_next_pool_asset_id(); - assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), token_2, token_1)); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(user), + Box::new(token_2), + Box::new(token_1) + )); let lp_token3_1 = AssetConversion::get_next_pool_asset_id(); assert_eq!( @@ -252,7 +294,11 @@ fn different_pools_should_have_different_lp_tokens() { }] ); - assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), token_3, token_1)); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(user), + Box::new(token_3), + Box::new(token_1) + )); assert_eq!( events(), [Event::::PoolCreated { @@ -277,9 +323,17 @@ fn can_add_liquidity() { create_tokens(user, vec![token_2, token_3]); let lp_token1 = AssetConversion::get_next_pool_asset_id(); - assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), token_1, token_2)); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(user), + Box::new(token_1), + Box::new(token_2) + )); let lp_token2 = AssetConversion::get_next_pool_asset_id(); - assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), token_1, token_3)); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(user), + Box::new(token_1), + Box::new(token_3) + )); let ed = get_ed(); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 10000 * 2 + ed)); @@ -288,8 +342,8 @@ fn can_add_liquidity() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - token_1, - token_2, + Box::new(token_1), + Box::new(token_2), 10000, 10, 10000, @@ -317,8 +371,8 @@ fn can_add_liquidity() { // try to pass the non-native - native assets, the result should be the same assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - token_3, - token_1, + Box::new(token_3), + Box::new(token_1), 10, 10000, 10, @@ -353,7 +407,11 @@ fn add_tiny_liquidity_leads_to_insufficient_liquidity_minted_error() { let token_2 = NativeOrAssetId::Asset(2); create_tokens(user, vec![token_2]); - assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), token_1, token_2)); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(user), + Box::new(token_1), + Box::new(token_2) + )); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 1000)); assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user, 1000)); @@ -361,8 +419,8 @@ fn add_tiny_liquidity_leads_to_insufficient_liquidity_minted_error() { assert_noop!( AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - token_1, - token_2, + Box::new(token_1), + Box::new(token_2), 1, 1, 1, @@ -375,8 +433,8 @@ fn add_tiny_liquidity_leads_to_insufficient_liquidity_minted_error() { assert_noop!( AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - token_1, - token_2, + Box::new(token_1), + Box::new(token_2), get_ed(), 1, 1, @@ -397,8 +455,16 @@ fn add_tiny_liquidity_directly_to_pool_address() { let token_3 = NativeOrAssetId::Asset(3); create_tokens(user, vec![token_2, token_3]); - assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), token_1, token_2)); - assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), token_1, token_3)); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(user), + Box::new(token_1), + Box::new(token_2) + )); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(user), + Box::new(token_1), + Box::new(token_3) + )); let ed = get_ed(); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 10000 * 2 + ed)); @@ -411,8 +477,8 @@ fn add_tiny_liquidity_directly_to_pool_address() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - token_1, - token_2, + Box::new(token_1), + Box::new(token_2), 10000, 10, 10000, @@ -425,8 +491,8 @@ fn add_tiny_liquidity_directly_to_pool_address() { assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, pallet_account, 1)); assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - token_1, - token_3, + Box::new(token_1), + Box::new(token_3), 10000, 10, 10000, @@ -446,7 +512,11 @@ fn can_remove_liquidity() { create_tokens(user, vec![token_2]); let lp_token = AssetConversion::get_next_pool_asset_id(); - assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), token_1, token_2)); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(user), + Box::new(token_1), + Box::new(token_2) + )); let ed_token_1 = >::minimum_balance(); let ed_token_2 = >::minimum_balance(2); @@ -459,8 +529,8 @@ fn can_remove_liquidity() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - token_1, - token_2, + Box::new(token_1), + Box::new(token_2), 1000000000, 100000, 1000000000, @@ -473,8 +543,8 @@ fn can_remove_liquidity() { assert_ok!(AssetConversion::remove_liquidity( RuntimeOrigin::signed(user), - token_1, - token_2, + Box::new(token_1), + Box::new(token_2), total_lp_received, 0, 0, @@ -512,15 +582,19 @@ fn can_not_redeem_more_lp_tokens_than_were_minted() { let lp_token = AssetConversion::get_next_pool_asset_id(); create_tokens(user, vec![token_2]); - assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), token_1, token_2)); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(user), + Box::new(token_1), + Box::new(token_2) + )); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 10000 + get_ed())); assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user, 1000)); assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - token_1, - token_2, + Box::new(token_1), + Box::new(token_2), 10000, 10, 10000, @@ -534,8 +608,8 @@ fn can_not_redeem_more_lp_tokens_than_were_minted() { assert_noop!( AssetConversion::remove_liquidity( RuntimeOrigin::signed(user), - token_1, - token_2, + Box::new(token_1), + Box::new(token_2), 216 + 1, // Try and redeem 10 lp tokens while only 9 minted. 0, 0, @@ -554,15 +628,19 @@ fn can_quote_price() { let token_2 = NativeOrAssetId::Asset(2); create_tokens(user, vec![token_2]); - assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), token_1, token_2)); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(user), + Box::new(token_1), + Box::new(token_2) + )); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 100000)); assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user, 1000)); assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - token_1, - token_2, + Box::new(token_1), + Box::new(token_2), 10000, 200, 1, @@ -775,15 +853,19 @@ fn quote_price_exact_tokens_for_tokens_matches_execution() { let token_2 = NativeOrAssetId::Asset(2); create_tokens(user, vec![token_2]); - assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), token_1, token_2)); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(user), + Box::new(token_1), + Box::new(token_2) + )); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 100000)); assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user, 1000)); assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - token_1, - token_2, + Box::new(token_1), + Box::new(token_2), 10000, 200, 1, @@ -803,7 +885,7 @@ fn quote_price_exact_tokens_for_tokens_matches_execution() { assert_eq!(prior_dot_balance, balance(user2, token_1)); assert_ok!(AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user2), - bvec![token_2, token_1], + bbvec![token_2, token_1], amount, 1, user2, @@ -823,15 +905,19 @@ fn quote_price_tokens_for_exact_tokens_matches_execution() { let token_2 = NativeOrAssetId::Asset(2); create_tokens(user, vec![token_2]); - assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), token_1, token_2)); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(user), + Box::new(token_1), + Box::new(token_2) + )); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 100000)); assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user, 1000)); assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - token_1, - token_2, + Box::new(token_1), + Box::new(token_2), 10000, 200, 1, @@ -853,7 +939,7 @@ fn quote_price_tokens_for_exact_tokens_matches_execution() { assert_eq!(prior_asset_balance, balance(user2, token_2)); assert_ok!(AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user2), - bvec![token_2, token_1], + bbvec![token_2, token_1], amount, 1, user2, @@ -874,7 +960,11 @@ fn can_swap_with_native() { let pool_id = (token_1, token_2); create_tokens(user, vec![token_2]); - assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), token_1, token_2)); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(user), + Box::new(token_1), + Box::new(token_2) + )); let ed = get_ed(); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 10000 + ed)); @@ -885,8 +975,8 @@ fn can_swap_with_native() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - token_1, - token_2, + Box::new(token_1), + Box::new(token_2), liquidity1, liquidity2, 1, @@ -902,7 +992,7 @@ fn can_swap_with_native() { assert_ok!(AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bvec![token_2, token_1], + bbvec![token_2, token_1], input_amount, 1, user, @@ -924,7 +1014,11 @@ fn can_swap_with_realistic_values() { let dot = NativeOrAssetId::Native; let usd = NativeOrAssetId::Asset(2); create_tokens(user, vec![usd]); - assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), dot, usd)); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(user), + Box::new(dot), + Box::new(usd) + )); const UNIT: u128 = 1_000_000_000; @@ -935,8 +1029,8 @@ fn can_swap_with_realistic_values() { let liquidity_usd = 1_000_000 * UNIT; assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - dot, - usd, + Box::new(dot), + Box::new(usd), liquidity_dot, liquidity_usd, 1, @@ -948,7 +1042,7 @@ fn can_swap_with_realistic_values() { assert_ok!(AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bvec![usd, dot], + bbvec![usd, dot], input_amount, 1, user, @@ -973,13 +1067,17 @@ fn can_not_swap_in_pool_with_no_liquidity_added_yet() { let token_2 = NativeOrAssetId::Asset(2); create_tokens(user, vec![token_2]); - assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), token_1, token_2)); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(user), + Box::new(token_1), + Box::new(token_2) + )); // Check can't swap an empty pool assert_noop!( AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bvec![token_2, token_1], + bbvec![token_2, token_1], 10, 1, user, @@ -1000,7 +1098,11 @@ fn check_no_panic_when_try_swap_close_to_empty_pool() { let lp_token = AssetConversion::get_next_pool_asset_id(); create_tokens(user, vec![token_2]); - assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), token_1, token_2)); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(user), + Box::new(token_1), + Box::new(token_2) + )); let ed = get_ed(); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 10000 + ed)); @@ -1011,8 +1113,8 @@ fn check_no_panic_when_try_swap_close_to_empty_pool() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - token_1, - token_2, + Box::new(token_1), + Box::new(token_2), liquidity1, liquidity2, 1, @@ -1037,8 +1139,8 @@ fn check_no_panic_when_try_swap_close_to_empty_pool() { assert_ok!(AssetConversion::remove_liquidity( RuntimeOrigin::signed(user), - token_1, - token_2, + Box::new(token_1), + Box::new(token_2), lp_token_minted, 1, 1, @@ -1054,7 +1156,7 @@ fn check_no_panic_when_try_swap_close_to_empty_pool() { assert_noop!( AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user), - bvec![token_2, token_1], + bbvec![token_2, token_1], 708 - ed + 1, // amount_out 500, // amount_in_max user, @@ -1065,7 +1167,7 @@ fn check_no_panic_when_try_swap_close_to_empty_pool() { assert_ok!(AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user), - bvec![token_2, token_1], + bbvec![token_2, token_1], 608, // amount_out 500, // amount_in_max user, @@ -1087,7 +1189,7 @@ fn check_no_panic_when_try_swap_close_to_empty_pool() { assert_noop!( AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user), - bvec![token_2, token_1], + bbvec![token_2, token_1], token_1_left - 1, // amount_out 1000, // amount_in_max user, @@ -1100,7 +1202,7 @@ fn check_no_panic_when_try_swap_close_to_empty_pool() { assert_noop!( AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user), - bvec![token_2, token_1], + bbvec![token_2, token_1], token_1_left, // amount_out 1000, // amount_in_max user, @@ -1119,7 +1221,11 @@ fn swap_should_not_work_if_too_much_slippage() { let token_2 = NativeOrAssetId::Asset(2); create_tokens(user, vec![token_2]); - assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), token_1, token_2)); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(user), + Box::new(token_1), + Box::new(token_2) + )); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 10000 + get_ed())); assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user, 1000)); @@ -1129,8 +1235,8 @@ fn swap_should_not_work_if_too_much_slippage() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - token_1, - token_2, + Box::new(token_1), + Box::new(token_2), liquidity1, liquidity2, 1, @@ -1143,7 +1249,7 @@ fn swap_should_not_work_if_too_much_slippage() { assert_noop!( AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bvec![token_2, token_1], + bbvec![token_2, token_1], exchange_amount, // amount_in 4000, // amount_out_min user, @@ -1163,7 +1269,11 @@ fn can_swap_tokens_for_exact_tokens() { let pool_id = (token_1, token_2); create_tokens(user, vec![token_2]); - assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), token_1, token_2)); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(user), + Box::new(token_1), + Box::new(token_2) + )); let ed = get_ed(); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 20000 + ed)); @@ -1178,8 +1288,8 @@ fn can_swap_tokens_for_exact_tokens() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - token_1, - token_2, + Box::new(token_1), + Box::new(token_2), liquidity1, liquidity2, 1, @@ -1194,7 +1304,7 @@ fn can_swap_tokens_for_exact_tokens() { assert_ok!(AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user), - bvec![token_1, token_2], + bbvec![token_1, token_2], exchange_out, // amount_out 3500, // amount_in_max user, @@ -1225,7 +1335,11 @@ fn can_swap_tokens_for_exact_tokens_when_not_liquidity_provider() { let lp_token = AssetConversion::get_next_pool_asset_id(); create_tokens(user2, vec![token_2]); - assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user2), token_1, token_2)); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(user2), + Box::new(token_1), + Box::new(token_2) + )); let ed = get_ed(); let base1 = 10000; @@ -1245,8 +1359,8 @@ fn can_swap_tokens_for_exact_tokens_when_not_liquidity_provider() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user2), - token_1, - token_2, + Box::new(token_1), + Box::new(token_2), liquidity1, liquidity2, 1, @@ -1264,7 +1378,7 @@ fn can_swap_tokens_for_exact_tokens_when_not_liquidity_provider() { assert_ok!(AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user), - bvec![token_1, token_2], + bbvec![token_1, token_2], exchange_out, // amount_out 3500, // amount_in_max user, @@ -1293,8 +1407,8 @@ fn can_swap_tokens_for_exact_tokens_when_not_liquidity_provider() { assert_ok!(AssetConversion::remove_liquidity( RuntimeOrigin::signed(user2), - token_1, - token_2, + Box::new(token_1), + Box::new(token_2), lp_token_minted, 0, 0, @@ -1312,7 +1426,11 @@ fn swap_when_existential_deposit_would_cause_reaping_but_keep_alive_set() { let token_2 = NativeOrAssetId::Asset(2); create_tokens(user2, vec![token_2]); - assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user2), token_1, token_2)); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(user2), + Box::new(token_1), + Box::new(token_2) + )); let ed = get_ed(); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 101)); @@ -1322,8 +1440,8 @@ fn swap_when_existential_deposit_would_cause_reaping_but_keep_alive_set() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user2), - token_1, - token_2, + Box::new(token_1), + Box::new(token_2), 10000, 200, 1, @@ -1334,7 +1452,7 @@ fn swap_when_existential_deposit_would_cause_reaping_but_keep_alive_set() { assert_noop!( AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user), - bvec![token_1, token_2], + bbvec![token_1, token_2], 1, // amount_out 101, // amount_in_max user, @@ -1346,7 +1464,7 @@ fn swap_when_existential_deposit_would_cause_reaping_but_keep_alive_set() { assert_noop!( AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bvec![token_1, token_2], + bbvec![token_1, token_2], 51, // amount_in 1, // amount_out_min user, @@ -1358,7 +1476,7 @@ fn swap_when_existential_deposit_would_cause_reaping_but_keep_alive_set() { assert_noop!( AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user), - bvec![token_2, token_1], + bbvec![token_2, token_1], 51, // amount_out 2, // amount_in_max user, @@ -1370,7 +1488,7 @@ fn swap_when_existential_deposit_would_cause_reaping_but_keep_alive_set() { assert_noop!( AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bvec![token_2, token_1], + bbvec![token_2, token_1], 2, // amount_in 1, // amount_out_min user, @@ -1392,9 +1510,21 @@ fn swap_when_existential_deposit_would_cause_reaping_pool_account() { let ed_assets = 100; create_tokens_with_ed(user2, vec![token_2, token_3], ed_assets); - assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user2), token_1, token_2)); - assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user2), token_1, token_3)); - assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user2), token_2, token_3)); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(user2), + Box::new(token_1), + Box::new(token_2) + )); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(user2), + Box::new(token_1), + Box::new(token_3) + )); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(user2), + Box::new(token_2), + Box::new(token_3) + )); let ed = get_ed(); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 20000 + ed)); @@ -1407,8 +1537,8 @@ fn swap_when_existential_deposit_would_cause_reaping_pool_account() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user2), - token_1, - token_2, + Box::new(token_1), + Box::new(token_2), 10000, 200, 1, @@ -1418,8 +1548,8 @@ fn swap_when_existential_deposit_would_cause_reaping_pool_account() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user2), - token_1, - token_3, + Box::new(token_1), + Box::new(token_3), 200, 10000, 1, @@ -1429,8 +1559,8 @@ fn swap_when_existential_deposit_would_cause_reaping_pool_account() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user2), - token_2, - token_3, + Box::new(token_2), + Box::new(token_3), 200, 10000, 1, @@ -1442,7 +1572,7 @@ fn swap_when_existential_deposit_would_cause_reaping_pool_account() { assert_noop!( AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user), - bvec![token_1, token_2], + bbvec![token_1, token_2], 110, // amount_out 20000, // amount_in_max user, @@ -1455,7 +1585,7 @@ fn swap_when_existential_deposit_would_cause_reaping_pool_account() { assert_noop!( AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bvec![token_1, token_2], + bbvec![token_1, token_2], 15000, // amount_in 110, // amount_out_min user, @@ -1468,7 +1598,7 @@ fn swap_when_existential_deposit_would_cause_reaping_pool_account() { assert_noop!( AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user), - bvec![token_3, token_1], + bbvec![token_3, token_1], 110, // amount_out 20000, // amount_in_max user, @@ -1481,7 +1611,7 @@ fn swap_when_existential_deposit_would_cause_reaping_pool_account() { assert_noop!( AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bvec![token_3, token_1], + bbvec![token_3, token_1], 15000, // amount_in 110, // amount_out_min user, @@ -1503,7 +1633,7 @@ fn swap_when_existential_deposit_would_cause_reaping_pool_account() { assert_noop!( AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bvec![token_3, token_1, token_2], + bbvec![token_3, token_1, token_2], amount_in, // amount_in 1, // amount_out_min user, @@ -1525,7 +1655,7 @@ fn swap_when_existential_deposit_would_cause_reaping_pool_account() { assert_noop!( AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bvec![token_1, token_2, token_3], + bbvec![token_1, token_2, token_3], amount_in, // amount_in 1, // amount_out_min user, @@ -1544,7 +1674,11 @@ fn swap_tokens_for_exact_tokens_should_not_work_if_too_much_slippage() { let token_2 = NativeOrAssetId::Asset(2); create_tokens(user, vec![token_2]); - assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), token_1, token_2)); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(user), + Box::new(token_1), + Box::new(token_2) + )); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 20000 + get_ed())); assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user, 1000)); @@ -1554,8 +1688,8 @@ fn swap_tokens_for_exact_tokens_should_not_work_if_too_much_slippage() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - token_1, - token_2, + Box::new(token_1), + Box::new(token_2), liquidity1, liquidity2, 1, @@ -1568,7 +1702,7 @@ fn swap_tokens_for_exact_tokens_should_not_work_if_too_much_slippage() { assert_noop!( AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user), - bvec![token_1, token_2], + bbvec![token_1, token_2], exchange_out, // amount_out 50, // amount_in_max just greater than slippage. user, @@ -1588,8 +1722,16 @@ fn swap_exact_tokens_for_tokens_in_multi_hops() { let token_3 = NativeOrAssetId::Asset(3); create_tokens(user, vec![token_2, token_3]); - assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), token_1, token_2)); - assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), token_2, token_3)); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(user), + Box::new(token_1), + Box::new(token_2) + )); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(user), + Box::new(token_2), + Box::new(token_3) + )); let ed = get_ed(); let base1 = 10000; @@ -1604,8 +1746,8 @@ fn swap_exact_tokens_for_tokens_in_multi_hops() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - token_1, - token_2, + Box::new(token_1), + Box::new(token_2), liquidity1, liquidity2, 1, @@ -1614,8 +1756,8 @@ fn swap_exact_tokens_for_tokens_in_multi_hops() { )); assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - token_2, - token_3, + Box::new(token_2), + Box::new(token_3), liquidity2, liquidity3, 1, @@ -1634,7 +1776,7 @@ fn swap_exact_tokens_for_tokens_in_multi_hops() { assert_noop!( AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bvec![token_1], + bbvec![token_1], input_amount, 80, user, @@ -1646,7 +1788,7 @@ fn swap_exact_tokens_for_tokens_in_multi_hops() { assert_noop!( AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bvec![token_1, token_2, token_3, token_2], + bbvec![token_1, token_2, token_3, token_2], input_amount, 80, user, @@ -1657,7 +1799,7 @@ fn swap_exact_tokens_for_tokens_in_multi_hops() { assert_ok!(AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bvec![token_1, token_2, token_3], + bbvec![token_1, token_2, token_3], input_amount, // amount_in 80, // amount_out_min user, @@ -1687,8 +1829,16 @@ fn swap_tokens_for_exact_tokens_in_multi_hops() { let token_3 = NativeOrAssetId::Asset(3); create_tokens(user, vec![token_2, token_3]); - assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), token_1, token_2)); - assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), token_2, token_3)); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(user), + Box::new(token_1), + Box::new(token_2) + )); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(user), + Box::new(token_2), + Box::new(token_3) + )); let ed = get_ed(); let base1 = 10000; @@ -1703,8 +1853,8 @@ fn swap_tokens_for_exact_tokens_in_multi_hops() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - token_1, - token_2, + Box::new(token_1), + Box::new(token_2), liquidity1, liquidity2, 1, @@ -1713,8 +1863,8 @@ fn swap_tokens_for_exact_tokens_in_multi_hops() { )); assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - token_2, - token_3, + Box::new(token_2), + Box::new(token_3), liquidity2, liquidity3, 1, @@ -1732,7 +1882,7 @@ fn swap_tokens_for_exact_tokens_in_multi_hops() { assert_ok!(AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user), - bvec![token_1, token_2, token_3], + bbvec![token_1, token_2, token_3], exchange_out3, // amount_out 1000, // amount_in_max user, @@ -1758,6 +1908,7 @@ fn can_not_swap_same_asset() { new_test_ext().execute_with(|| { let user = 1; let token_1 = NativeOrAssetId::Asset(1); + let token_2 = NativeOrAssetId::Native; create_tokens(user, vec![token_1]); assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 1, user, 1000)); @@ -1767,8 +1918,8 @@ fn can_not_swap_same_asset() { assert_noop!( AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - token_1, - token_1, + Box::new(token_1), + Box::new(token_1), liquidity1, liquidity2, 1, @@ -1782,7 +1933,7 @@ fn can_not_swap_same_asset() { assert_noop!( AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bvec![token_1, token_1], + bbvec![token_1, token_1], exchange_amount, 1, user, @@ -1794,7 +1945,7 @@ fn can_not_swap_same_asset() { assert_noop!( AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bvec![NativeOrAssetId::Native, NativeOrAssetId::Native], + bbvec![token_2, token_2], exchange_amount, 1, user, @@ -1854,7 +2005,11 @@ fn cannot_block_pool_creation() { // User can still create the pool create_tokens(user, vec![token_2]); - assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), token_1, token_2)); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(user), + Box::new(token_1), + Box::new(token_2) + )); // User has to transfer one Asset(2) token to the pool account (otherwise add_liquidity will // fail with `AssetTwoDepositDidNotMeetMinimum`) @@ -1865,8 +2020,8 @@ fn cannot_block_pool_creation() { // add_liquidity shouldn't fail because of the number of consumers assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - token_1, - token_2, + Box::new(token_1), + Box::new(token_2), 10000, 100, 10000, @@ -1887,8 +2042,16 @@ fn swap_transactional() { let asset_ed = 150; create_tokens_with_ed(user, vec![token_2, token_3], asset_ed); - assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), token_1, token_2)); - assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), token_1, token_3)); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(user), + Box::new(token_1), + Box::new(token_2) + )); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(user), + Box::new(token_1), + Box::new(token_3) + )); let ed = get_ed(); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 20000 + ed)); @@ -1904,8 +2067,8 @@ fn swap_transactional() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - token_1, - token_2, + Box::new(token_1), + Box::new(token_2), liquidity1, liquidity2, 1, @@ -1915,8 +2078,8 @@ fn swap_transactional() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - token_1, - token_3, + Box::new(token_1), + Box::new(token_3), liquidity1, liquidity2, 1, @@ -2014,7 +2177,11 @@ fn swap_credit_returns_change() { let token_2 = NativeOrAssetId::Asset(2); create_tokens(user, vec![token_2]); - assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), token_1, token_2)); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(user), + Box::new(token_1), + Box::new(token_2) + )); let ed = get_ed(); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 20000 + ed)); @@ -2025,8 +2192,8 @@ fn swap_credit_returns_change() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - token_1, - token_2, + Box::new(token_1), + Box::new(token_2), liquidity1, liquidity2, 1, @@ -2062,7 +2229,11 @@ fn swap_credit_insufficient_amount_bounds() { let token_2 = NativeOrAssetId::Asset(2); create_tokens(user, vec![token_2]); - assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), token_1, token_2)); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(user), + Box::new(token_1), + Box::new(token_2) + )); let ed = get_ed(); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 20000 + ed)); @@ -2076,8 +2247,8 @@ fn swap_credit_insufficient_amount_bounds() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - token_1, - token_2, + Box::new(token_1), + Box::new(token_2), liquidity1, liquidity2, 1, @@ -2131,7 +2302,11 @@ fn swap_credit_zero_amount() { let token_2 = NativeOrAssetId::Asset(2); create_tokens(user, vec![token_2]); - assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), token_1, token_2)); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(user), + Box::new(token_1), + Box::new(token_2) + )); let ed = get_ed(); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 20000 + ed)); @@ -2145,8 +2320,8 @@ fn swap_credit_zero_amount() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - token_1, - token_2, + Box::new(token_1), + Box::new(token_2), liquidity1, liquidity2, 1, @@ -2209,7 +2384,11 @@ fn swap_credit_invalid_path() { let token_2 = NativeOrAssetId::Asset(2); create_tokens(user, vec![token_2]); - assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(user), token_1, token_2)); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(user), + Box::new(token_1), + Box::new(token_2) + )); let ed = get_ed(); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 20000 + ed)); @@ -2223,8 +2402,8 @@ fn swap_credit_invalid_path() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - token_1, - token_2, + Box::new(token_1), + Box::new(token_2), liquidity1, liquidity2, 1, From ee9d2ebc0b86368f92568a0895ffd3f9add1a10d Mon Sep 17 00:00:00 2001 From: muharem Date: Sat, 7 Oct 2023 16:32:54 +0200 Subject: [PATCH 24/98] use vec instead bounded vec --- substrate/frame/asset-conversion/src/lib.rs | 44 ++++----- substrate/frame/asset-conversion/src/swap.rs | 10 -- substrate/frame/asset-conversion/src/tests.rs | 98 +++++++++---------- 3 files changed, 63 insertions(+), 89 deletions(-) diff --git a/substrate/frame/asset-conversion/src/lib.rs b/substrate/frame/asset-conversion/src/lib.rs index 356b7f41e924..37d304bf8494 100644 --- a/substrate/frame/asset-conversion/src/lib.rs +++ b/substrate/frame/asset-conversion/src/lib.rs @@ -86,7 +86,7 @@ use frame_support::{ }, AccountTouch, ContainsPair, Imbalance, Incrementable, }, - BoundedBTreeSet, PalletId, + PalletId, }; use sp_core::Get; use sp_runtime::{ @@ -94,9 +94,9 @@ use sp_runtime::{ CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Ensure, IntegerSquareRoot, MaybeDisplay, One, TrailingZeroInput, Zero, }, - BoundedVec, DispatchError, RuntimeDebug, Saturating, TokenError, TransactionOutcome, + DispatchError, RuntimeDebug, Saturating, TokenError, TransactionOutcome, }; -use sp_std::vec::Vec; +use sp_std::{collections::btree_set::BTreeSet, vec::Vec}; #[frame_support::pallet] pub mod pallet { @@ -650,7 +650,7 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::swap_exact_tokens_for_tokens())] pub fn swap_exact_tokens_for_tokens( origin: OriginFor, - path: BoundedVec, T::MaxSwapPathLength>, + path: Vec>, amount_in: T::AssetBalance, amount_out_min: T::AssetBalance, send_to: T::AccountId, @@ -659,11 +659,7 @@ pub mod pallet { let sender = ensure_signed(origin)?; Self::do_swap_exact_tokens_for_tokens( sender, - path.into_iter() - .map(|a| *a) - .collect::>() - .try_into() - .map_err(|_| Error::::InvalidPath)?, + path.into_iter().map(|a| *a).collect(), amount_in, Some(amount_out_min), send_to, @@ -682,7 +678,7 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::swap_tokens_for_exact_tokens())] pub fn swap_tokens_for_exact_tokens( origin: OriginFor, - path: BoundedVec, T::MaxSwapPathLength>, + path: Vec>, amount_out: T::AssetBalance, amount_in_max: T::AssetBalance, send_to: T::AccountId, @@ -691,11 +687,7 @@ pub mod pallet { let sender = ensure_signed(origin)?; Self::do_swap_tokens_for_exact_tokens( sender, - path.into_iter() - .map(|a| *a) - .collect::>() - .try_into() - .map_err(|_| Error::::InvalidPath)?, + path.into_iter().map(|a| *a).collect(), amount_out, Some(amount_in_max), send_to, @@ -720,7 +712,7 @@ pub mod pallet { /// rollback. pub(crate) fn do_swap_exact_tokens_for_tokens( sender: T::AccountId, - path: BoundedVec, + path: Vec, amount_in: T::AssetBalance, amount_out_min: Option, send_to: T::AccountId, @@ -768,7 +760,7 @@ pub mod pallet { /// rollback. pub(crate) fn do_swap_tokens_for_exact_tokens( sender: T::AccountId, - path: BoundedVec, + path: Vec, amount_out: T::AssetBalance, amount_in_max: Option, send_to: T::AccountId, @@ -814,7 +806,7 @@ pub mod pallet { /// only inside a transactional storage context and an Err result must imply a storage /// rollback. pub(crate) fn do_swap_exact_credit_tokens_for_tokens( - path: BoundedVec, + path: Vec, credit_in: Credit, amount_out_min: Option, ) -> Result, (Credit, DispatchError)> { @@ -862,7 +854,7 @@ pub mod pallet { /// only inside a transactional storage context and an Err result must imply a storage /// rollback. pub(crate) fn do_swap_credit_tokens_for_exact_tokens( - path: BoundedVec, + path: Vec, credit_in: Credit, amount_out: T::AssetBalance, ) -> Result<(Credit, Credit), (Credit, DispatchError)> { @@ -1152,7 +1144,7 @@ pub mod pallet { /// Leading to an amount at the end of a `path`, get the required amounts in. pub(crate) fn balance_path_from_amount_out( amount_out: T::AssetBalance, - path: BoundedVec, + path: Vec, ) -> Result, DispatchError> { let mut balance_path: BalancePath = Vec::with_capacity(path.len()); let mut amount_in: T::AssetBalance = amount_out; @@ -1178,7 +1170,7 @@ pub mod pallet { /// Following an amount into a `path`, get the corresponding amounts out. pub(crate) fn balance_path_from_amount_in( amount_in: T::AssetBalance, - path: BoundedVec, + path: Vec, ) -> Result, DispatchError> { let mut balance_path: BalancePath = Vec::with_capacity(path.len()); let mut amount_out: T::AssetBalance = amount_in; @@ -1391,18 +1383,16 @@ pub mod pallet { } /// Ensure that a path is valid. - fn validate_swap_path( - path: &BoundedVec, - ) -> Result<(), DispatchError> { + fn validate_swap_path(path: &Vec) -> Result<(), DispatchError> { ensure!(path.len() >= 2, Error::::InvalidPath); + ensure!(path.len() as u32 <= T::MaxSwapPathLength::get(), Error::::InvalidPath); // validate all the pools in the path are unique - let mut pools = BoundedBTreeSet::, T::MaxSwapPathLength>::new(); + let mut pools = BTreeSet::>::new(); for assets_pair in path.windows(2) { if let [asset1, asset2] = assets_pair { let pool_id = Self::get_pool_id(asset1.clone(), asset2.clone()); - let new_element = - pools.try_insert(pool_id).map_err(|_| Error::::Overflow)?; + let new_element = pools.insert(pool_id); if !new_element { return Err(Error::::NonUniquePath.into()) } diff --git a/substrate/frame/asset-conversion/src/swap.rs b/substrate/frame/asset-conversion/src/swap.rs index 92cd4c8877a5..a86b55d41a53 100644 --- a/substrate/frame/asset-conversion/src/swap.rs +++ b/substrate/frame/asset-conversion/src/swap.rs @@ -120,7 +120,6 @@ impl Swap for Pallet { send_to: T::AccountId, keep_alive: bool, ) -> Result { - let path = path.try_into().map_err(|_| Error::::PathError)?; let amount_out_min = amount_out_min.map(Self::convert_hpb_to_asset_balance).transpose()?; let amount_out = with_storage_layer(|| { Self::do_swap_exact_tokens_for_tokens( @@ -143,7 +142,6 @@ impl Swap for Pallet { send_to: T::AccountId, keep_alive: bool, ) -> Result { - let path = path.try_into().map_err(|_| Error::::PathError)?; let amount_in_max = amount_in_max.map(Self::convert_hpb_to_asset_balance).transpose()?; let amount_in = with_storage_layer(|| { Self::do_swap_tokens_for_exact_tokens( @@ -173,10 +171,6 @@ impl SwapCredit for Pallet { credit_in: Self::Credit, amount_out_min: Option, ) -> Result { - let path = match path.try_into() { - Ok(p) => p, - Err(_) => return Err((credit_in, Error::::PathError.into())), - }; let transaction_res = with_transaction(|| -> TransactionOutcome> { let res = @@ -200,10 +194,6 @@ impl SwapCredit for Pallet { credit_in: Self::Credit, amount_out: Self::Balance, ) -> Result<(Self::Credit, Self::Credit), (Self::Credit, DispatchError)> { - let path = match path.try_into() { - Ok(p) => p, - Err(_) => return Err((credit_in, Error::::PathError.into())), - }; let transaction_res = with_transaction(|| -> TransactionOutcome> { let res = Self::do_swap_credit_tokens_for_exact_tokens(path, credit_in, amount_out); diff --git a/substrate/frame/asset-conversion/src/tests.rs b/substrate/frame/asset-conversion/src/tests.rs index d6a041f98e0c..db6aca01dece 100644 --- a/substrate/frame/asset-conversion/src/tests.rs +++ b/substrate/frame/asset-conversion/src/tests.rs @@ -95,14 +95,8 @@ fn get_ed() -> u128 { } macro_rules! bvec { - ($( $x:tt )*) => { - vec![$( $x )*].try_into().unwrap() - } -} - -macro_rules! bbvec { ($( $x:ident ),*) => { - vec![$( Box::new( $x ), )*].try_into().unwrap() + vec![$( Box::new( $x ), )*] } } @@ -885,7 +879,7 @@ fn quote_price_exact_tokens_for_tokens_matches_execution() { assert_eq!(prior_dot_balance, balance(user2, token_1)); assert_ok!(AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user2), - bbvec![token_2, token_1], + bvec![token_2, token_1], amount, 1, user2, @@ -939,7 +933,7 @@ fn quote_price_tokens_for_exact_tokens_matches_execution() { assert_eq!(prior_asset_balance, balance(user2, token_2)); assert_ok!(AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user2), - bbvec![token_2, token_1], + bvec![token_2, token_1], amount, 1, user2, @@ -992,7 +986,7 @@ fn can_swap_with_native() { assert_ok!(AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bbvec![token_2, token_1], + bvec![token_2, token_1], input_amount, 1, user, @@ -1042,7 +1036,7 @@ fn can_swap_with_realistic_values() { assert_ok!(AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bbvec![usd, dot], + bvec![usd, dot], input_amount, 1, user, @@ -1054,7 +1048,7 @@ fn can_swap_with_realistic_values() { send_to: user, amount_in: 10 * UNIT, // usd amount_out: 1_993_980_120, // About 2 dot after div by UNIT. - path: bvec![(usd, 10 * UNIT), (dot, 1_993_980_120)], + path: vec![(usd, 10 * UNIT), (dot, 1_993_980_120)], })); }); } @@ -1077,7 +1071,7 @@ fn can_not_swap_in_pool_with_no_liquidity_added_yet() { assert_noop!( AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bbvec![token_2, token_1], + bvec![token_2, token_1], 10, 1, user, @@ -1156,7 +1150,7 @@ fn check_no_panic_when_try_swap_close_to_empty_pool() { assert_noop!( AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user), - bbvec![token_2, token_1], + bvec![token_2, token_1], 708 - ed + 1, // amount_out 500, // amount_in_max user, @@ -1167,7 +1161,7 @@ fn check_no_panic_when_try_swap_close_to_empty_pool() { assert_ok!(AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user), - bbvec![token_2, token_1], + bvec![token_2, token_1], 608, // amount_out 500, // amount_in_max user, @@ -1189,7 +1183,7 @@ fn check_no_panic_when_try_swap_close_to_empty_pool() { assert_noop!( AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user), - bbvec![token_2, token_1], + bvec![token_2, token_1], token_1_left - 1, // amount_out 1000, // amount_in_max user, @@ -1202,7 +1196,7 @@ fn check_no_panic_when_try_swap_close_to_empty_pool() { assert_noop!( AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user), - bbvec![token_2, token_1], + bvec![token_2, token_1], token_1_left, // amount_out 1000, // amount_in_max user, @@ -1249,7 +1243,7 @@ fn swap_should_not_work_if_too_much_slippage() { assert_noop!( AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bbvec![token_2, token_1], + bvec![token_2, token_1], exchange_amount, // amount_in 4000, // amount_out_min user, @@ -1304,7 +1298,7 @@ fn can_swap_tokens_for_exact_tokens() { assert_ok!(AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user), - bbvec![token_1, token_2], + bvec![token_1, token_2], exchange_out, // amount_out 3500, // amount_in_max user, @@ -1378,7 +1372,7 @@ fn can_swap_tokens_for_exact_tokens_when_not_liquidity_provider() { assert_ok!(AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user), - bbvec![token_1, token_2], + bvec![token_1, token_2], exchange_out, // amount_out 3500, // amount_in_max user, @@ -1452,7 +1446,7 @@ fn swap_when_existential_deposit_would_cause_reaping_but_keep_alive_set() { assert_noop!( AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user), - bbvec![token_1, token_2], + bvec![token_1, token_2], 1, // amount_out 101, // amount_in_max user, @@ -1464,7 +1458,7 @@ fn swap_when_existential_deposit_would_cause_reaping_but_keep_alive_set() { assert_noop!( AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bbvec![token_1, token_2], + bvec![token_1, token_2], 51, // amount_in 1, // amount_out_min user, @@ -1476,7 +1470,7 @@ fn swap_when_existential_deposit_would_cause_reaping_but_keep_alive_set() { assert_noop!( AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user), - bbvec![token_2, token_1], + bvec![token_2, token_1], 51, // amount_out 2, // amount_in_max user, @@ -1488,7 +1482,7 @@ fn swap_when_existential_deposit_would_cause_reaping_but_keep_alive_set() { assert_noop!( AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bbvec![token_2, token_1], + bvec![token_2, token_1], 2, // amount_in 1, // amount_out_min user, @@ -1572,7 +1566,7 @@ fn swap_when_existential_deposit_would_cause_reaping_pool_account() { assert_noop!( AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user), - bbvec![token_1, token_2], + bvec![token_1, token_2], 110, // amount_out 20000, // amount_in_max user, @@ -1585,7 +1579,7 @@ fn swap_when_existential_deposit_would_cause_reaping_pool_account() { assert_noop!( AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bbvec![token_1, token_2], + bvec![token_1, token_2], 15000, // amount_in 110, // amount_out_min user, @@ -1598,7 +1592,7 @@ fn swap_when_existential_deposit_would_cause_reaping_pool_account() { assert_noop!( AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user), - bbvec![token_3, token_1], + bvec![token_3, token_1], 110, // amount_out 20000, // amount_in_max user, @@ -1611,7 +1605,7 @@ fn swap_when_existential_deposit_would_cause_reaping_pool_account() { assert_noop!( AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bbvec![token_3, token_1], + bvec![token_3, token_1], 15000, // amount_in 110, // amount_out_min user, @@ -1633,7 +1627,7 @@ fn swap_when_existential_deposit_would_cause_reaping_pool_account() { assert_noop!( AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bbvec![token_3, token_1, token_2], + bvec![token_3, token_1, token_2], amount_in, // amount_in 1, // amount_out_min user, @@ -1655,7 +1649,7 @@ fn swap_when_existential_deposit_would_cause_reaping_pool_account() { assert_noop!( AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bbvec![token_1, token_2, token_3], + bvec![token_1, token_2, token_3], amount_in, // amount_in 1, // amount_out_min user, @@ -1702,7 +1696,7 @@ fn swap_tokens_for_exact_tokens_should_not_work_if_too_much_slippage() { assert_noop!( AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user), - bbvec![token_1, token_2], + bvec![token_1, token_2], exchange_out, // amount_out 50, // amount_in_max just greater than slippage. user, @@ -1776,7 +1770,7 @@ fn swap_exact_tokens_for_tokens_in_multi_hops() { assert_noop!( AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bbvec![token_1], + bvec![token_1], input_amount, 80, user, @@ -1788,7 +1782,7 @@ fn swap_exact_tokens_for_tokens_in_multi_hops() { assert_noop!( AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bbvec![token_1, token_2, token_3, token_2], + bvec![token_1, token_2, token_3, token_2], input_amount, 80, user, @@ -1799,7 +1793,7 @@ fn swap_exact_tokens_for_tokens_in_multi_hops() { assert_ok!(AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bbvec![token_1, token_2, token_3], + bvec![token_1, token_2, token_3], input_amount, // amount_in 80, // amount_out_min user, @@ -1882,7 +1876,7 @@ fn swap_tokens_for_exact_tokens_in_multi_hops() { assert_ok!(AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user), - bbvec![token_1, token_2, token_3], + bvec![token_1, token_2, token_3], exchange_out3, // amount_out 1000, // amount_in_max user, @@ -1933,7 +1927,7 @@ fn can_not_swap_same_asset() { assert_noop!( AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bbvec![token_1, token_1], + bvec![token_1, token_1], exchange_amount, 1, user, @@ -1945,7 +1939,7 @@ fn can_not_swap_same_asset() { assert_noop!( AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bbvec![token_2, token_2], + bvec![token_2, token_2], exchange_amount, 1, user, @@ -2113,7 +2107,7 @@ fn swap_transactional() { let error; assert_storage_noop!( error = >::swap_tokens_for_exact_tokens( - bvec![token_2, token_1, token_3], + vec![token_2, token_1, token_3], credit_in.into(), expected_out, ) @@ -2128,7 +2122,7 @@ fn swap_transactional() { let error; assert_storage_noop!( error = >::swap_exact_tokens_for_tokens( - bvec![token_2, token_1, token_3], + vec![token_2, token_1, token_3], credit_in.into(), Some(expected_out), ) @@ -2140,7 +2134,7 @@ fn swap_transactional() { assert_noop!( >::swap_exact_tokens_for_tokens( user2, - bvec![token_2, token_1, token_3], + vec![token_2, token_1, token_3], amount_in.into(), Some(expected_out.into()), user2, @@ -2153,7 +2147,7 @@ fn swap_transactional() { assert_noop!( >::swap_tokens_for_exact_tokens( user2, - bvec![token_2, token_1, token_3], + vec![token_2, token_1, token_3], expected_out.into(), Some(amount_in.into()), user2, @@ -2211,7 +2205,7 @@ fn swap_credit_returns_change() { let credit_in = Balances::issue(amount_in_max + expected_change.peek()); assert_ok!( >::swap_tokens_for_exact_tokens( - bvec![token_1, token_2], + vec![token_1, token_2], credit_in.into(), expected_credit_out.peek(), ), @@ -2264,7 +2258,7 @@ fn swap_credit_insufficient_amount_bounds() { let credit_in = Balances::issue(amount_in); let expected_credit_in = Balances::issue(amount_in); let error = >::swap_exact_tokens_for_tokens( - bvec![token_1, token_2], + vec![token_1, token_2], credit_in.into(), Some(amount_out_min), ) @@ -2281,7 +2275,7 @@ fn swap_credit_insufficient_amount_bounds() { let credit_in = Balances::issue(amount_in_max); let expected_credit_in = Balances::issue(amount_in_max); let error = >::swap_tokens_for_exact_tokens( - bvec![token_1, token_2], + vec![token_1, token_2], credit_in.into(), amount_out, ) @@ -2333,7 +2327,7 @@ fn swap_credit_zero_amount() { let credit_in = Credit::native_zero(); let expected_credit_in = Credit::native_zero(); let error = >::swap_exact_tokens_for_tokens( - bvec![token_1, token_2], + vec![token_1, token_2], credit_in, None, ) @@ -2344,7 +2338,7 @@ fn swap_credit_zero_amount() { let credit_in = Credit::native_zero(); let expected_credit_in = Credit::native_zero(); let error = >::swap_tokens_for_exact_tokens( - bvec![token_1, token_2], + vec![token_1, token_2], credit_in, 10, ) @@ -2355,7 +2349,7 @@ fn swap_credit_zero_amount() { let credit_in = Balances::issue(10); let expected_credit_in = Balances::issue(10); let error = >::swap_exact_tokens_for_tokens( - bvec![token_1, token_2], + vec![token_1, token_2], credit_in.into(), Some(0), ) @@ -2366,7 +2360,7 @@ fn swap_credit_zero_amount() { let credit_in = Balances::issue(10); let expected_credit_in = Balances::issue(10); let error = >::swap_tokens_for_exact_tokens( - bvec![token_1, token_2], + vec![token_1, token_2], credit_in.into(), 0, ) @@ -2415,7 +2409,7 @@ fn swap_credit_invalid_path() { let credit_in = Balances::issue(10); let expected_credit_in = Balances::issue(10); let error = >::swap_exact_tokens_for_tokens( - bvec![token_2, token_1], + vec![token_2, token_1], credit_in.into(), None, ) @@ -2426,7 +2420,7 @@ fn swap_credit_invalid_path() { let credit_in = Assets::issue(2, 10); let expected_credit_in = Assets::issue(2, 10); let error = >::swap_tokens_for_exact_tokens( - bvec![token_1, token_2], + vec![token_1, token_2], credit_in.into(), 10, ) @@ -2437,7 +2431,7 @@ fn swap_credit_invalid_path() { let credit_in = Balances::issue(10); let expected_credit_in = Balances::issue(10); let error = >::swap_exact_tokens_for_tokens( - bvec![token_2], + vec![token_2], credit_in.into(), None, ) @@ -2448,7 +2442,7 @@ fn swap_credit_invalid_path() { let credit_in = Assets::issue(2, 10); let expected_credit_in = Assets::issue(2, 10); let error = >::swap_tokens_for_exact_tokens( - bvec![], + vec![], credit_in.into(), 10, ) From ced5e99159c6ce149b7865e68f5fcfb0ebc8a112 Mon Sep 17 00:00:00 2001 From: muharem Date: Sat, 7 Oct 2023 17:14:16 +0200 Subject: [PATCH 25/98] update to new api --- .../asset-hub-westend/src/tests/swap.rs | 56 +++++++++---------- .../assets/asset-hub-kusama/src/lib.rs | 10 ++-- .../assets/asset-hub-westend/src/lib.rs | 23 ++++---- .../common/src/local_and_foreign_assets.rs | 43 +++++++------- substrate/frame/asset-conversion/src/lib.rs | 2 +- .../asset-conversion-tx-payment/src/tests.rs | 10 +++- 6 files changed, 72 insertions(+), 72 deletions(-) diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/swap.rs b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/swap.rs index 7d1615c9e291..d56a4c7e4de6 100644 --- a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/swap.rs +++ b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/swap.rs @@ -17,11 +17,11 @@ use crate::*; #[test] fn swap_locally_on_chain_using_local_assets() { - let asset_native = Box::new(asset_hub_westend_runtime::xcm_config::WestendLocation::get()); - let asset_one = Box::new(MultiLocation { + let asset_native = asset_hub_westend_runtime::xcm_config::WestendLocation::get(); + let asset_one = MultiLocation { parents: 0, interior: X2(PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into())), - }); + }; AssetHubWestend::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; @@ -43,8 +43,8 @@ fn swap_locally_on_chain_using_local_assets() { assert_ok!(::AssetConversion::create_pool( ::RuntimeOrigin::signed(AssetHubWestendSender::get()), - asset_native.clone(), - asset_one.clone(), + Box::new(asset_native.clone()), + Box::new(asset_one.clone()), )); assert_expected_events!( @@ -56,8 +56,8 @@ fn swap_locally_on_chain_using_local_assets() { assert_ok!(::AssetConversion::add_liquidity( ::RuntimeOrigin::signed(AssetHubWestendSender::get()), - asset_native.clone(), - asset_one.clone(), + Box::new(asset_native.clone()), + Box::new(asset_one.clone()), 1_000_000_000_000, 2_000_000_000_000, 0, @@ -72,7 +72,7 @@ fn swap_locally_on_chain_using_local_assets() { ] ); - let path = BoundedVec::<_, _>::truncate_from(vec![asset_native.clone(), asset_one.clone()]); + let path = vec![Box::new(asset_native.clone()), Box::new(asset_one.clone())]; assert_ok!(::AssetConversion::swap_exact_tokens_for_tokens( ::RuntimeOrigin::signed(AssetHubWestendSender::get()), @@ -95,8 +95,8 @@ fn swap_locally_on_chain_using_local_assets() { assert_ok!(::AssetConversion::remove_liquidity( ::RuntimeOrigin::signed(AssetHubWestendSender::get()), - asset_native, - asset_one, + Box::new(asset_native), + Box::new(asset_one), 1414213562273 - 2_000_000_000, // all but the 2 EDs can't be retrieved. 0, 0, @@ -109,16 +109,16 @@ fn swap_locally_on_chain_using_local_assets() { fn swap_locally_on_chain_using_foreign_assets() { use frame_support::weights::WeightToFee; - let asset_native = Box::new(asset_hub_westend_runtime::xcm_config::WestendLocation::get()); + let asset_native = asset_hub_westend_runtime::xcm_config::WestendLocation::get(); - let foreign_asset1_at_asset_hub_westend = Box::new(MultiLocation { + let foreign_asset1_at_asset_hub_westend = MultiLocation { parents: 1, interior: X3( Parachain(PenpalWestendA::para_id().into()), PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into()), ), - }); + }; let assets_para_destination: VersionedMultiLocation = MultiLocation { parents: 1, interior: X1(Parachain(AssetHubWestend::para_id().into())) } @@ -163,7 +163,7 @@ fn swap_locally_on_chain_using_foreign_assets() { ::Runtime, Instance2, >::create { - id: *foreign_asset1_at_asset_hub_westend, + id: foreign_asset1_at_asset_hub_westend, min_balance: 1000, admin: sov_penpal_on_asset_hub_westend.clone().into(), }) @@ -211,7 +211,7 @@ fn swap_locally_on_chain_using_foreign_assets() { // Receive XCM message in Assets Parachain AssetHubWestend::execute_with(|| { assert!(::ForeignAssets::asset_exists( - *foreign_asset1_at_asset_hub_westend + foreign_asset1_at_asset_hub_westend )); // 3: Mint foreign asset on asset_hub_westend: @@ -225,7 +225,7 @@ fn swap_locally_on_chain_using_foreign_assets() { ::RuntimeOrigin::signed( sov_penpal_on_asset_hub_westend.clone().into() ), - *foreign_asset1_at_asset_hub_westend, + foreign_asset1_at_asset_hub_westend, sov_penpal_on_asset_hub_westend.clone().into(), 3_000_000_000_000, )); @@ -240,8 +240,8 @@ fn swap_locally_on_chain_using_foreign_assets() { // 4. Create pool: assert_ok!(::AssetConversion::create_pool( ::RuntimeOrigin::signed(AssetHubWestendSender::get()), - asset_native.clone(), - foreign_asset1_at_asset_hub_westend.clone(), + Box::new(asset_native.clone()), + Box::new(foreign_asset1_at_asset_hub_westend.clone()), )); assert_expected_events!( @@ -256,8 +256,8 @@ fn swap_locally_on_chain_using_foreign_assets() { ::RuntimeOrigin::signed( sov_penpal_on_asset_hub_westend.clone() ), - asset_native.clone(), - foreign_asset1_at_asset_hub_westend.clone(), + Box::new(asset_native.clone()), + Box::new(foreign_asset1_at_asset_hub_westend.clone()), 1_000_000_000_000, 2_000_000_000_000, 0, @@ -275,10 +275,10 @@ fn swap_locally_on_chain_using_foreign_assets() { ); // 6. Swap! - let path = BoundedVec::<_, _>::truncate_from(vec![ - asset_native.clone(), - foreign_asset1_at_asset_hub_westend.clone(), - ]); + let path = vec![ + Box::new(asset_native.clone()), + Box::new(foreign_asset1_at_asset_hub_westend.clone()), + ]; assert_ok!(::AssetConversion::swap_exact_tokens_for_tokens( ::RuntimeOrigin::signed(AssetHubWestendSender::get()), @@ -304,8 +304,8 @@ fn swap_locally_on_chain_using_foreign_assets() { ::RuntimeOrigin::signed( sov_penpal_on_asset_hub_westend.clone() ), - asset_native, - foreign_asset1_at_asset_hub_westend, + Box::new(asset_native), + Box::new(foreign_asset1_at_asset_hub_westend), 1414213562273 - 2_000_000_000, // all but the 2 EDs can't be retrieved. 0, 0, @@ -316,7 +316,7 @@ fn swap_locally_on_chain_using_foreign_assets() { #[test] fn cannot_create_pool_from_pool_assets() { - let asset_native = Box::new(asset_hub_westend_runtime::xcm_config::WestendLocation::get()); + let asset_native = asset_hub_westend_runtime::xcm_config::WestendLocation::get(); let mut asset_one = asset_hub_westend_runtime::xcm_config::PoolAssetsPalletLocation::get(); asset_one.append_with(GeneralIndex(ASSET_ID.into())).expect("pool assets"); @@ -341,7 +341,7 @@ fn cannot_create_pool_from_pool_assets() { assert_matches::assert_matches!( ::AssetConversion::create_pool( ::RuntimeOrigin::signed(AssetHubWestendSender::get()), - asset_native.clone(), + Box::new(asset_native.clone()), Box::new(asset_one), ), Err(DispatchError::Module(ModuleError{index: _, error: _, message})) => assert_eq!(message, Some("UnsupportedAsset")) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/lib.rs index 40ce122112d2..efadf4869bf1 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/lib.rs @@ -347,7 +347,7 @@ impl pallet_asset_conversion::Config for Runtime { type PalletId = AssetConversionPalletId; type AllowMultiAssetPools = AllowMultiAssetPools; type MaxSwapPathLength = ConstU32<4>; - type MultiAssetId = Box; + type MultiAssetId = MultiLocation; type MultiAssetIdConverter = MultiLocationConverter; type MintMinLiquidity = ConstU128<100>; @@ -1031,16 +1031,16 @@ impl_runtime_apis! { Block, Balance, u128, - Box, + MultiLocation, > for Runtime { - fn quote_price_exact_tokens_for_tokens(asset1: Box, asset2: Box, amount: u128, include_fee: bool) -> Option { + fn quote_price_exact_tokens_for_tokens(asset1: MultiLocation, asset2: MultiLocation, amount: u128, include_fee: bool) -> Option { AssetConversion::quote_price_exact_tokens_for_tokens(asset1, asset2, amount, include_fee) } - fn quote_price_tokens_for_exact_tokens(asset1: Box, asset2: Box, amount: u128, include_fee: bool) -> Option { + fn quote_price_tokens_for_exact_tokens(asset1: MultiLocation, asset2: MultiLocation, amount: u128, include_fee: bool) -> Option { AssetConversion::quote_price_tokens_for_exact_tokens(asset1, asset2, amount, include_fee) } - fn get_reserves(asset1: Box, asset2: Box) -> Option<(Balance, Balance)> { + fn get_reserves(asset1: MultiLocation, asset2: MultiLocation) -> Option<(Balance, Balance)> { AssetConversion::get_reserves(&asset1, &asset2).ok() } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index 943332087627..9a27a71bb16a 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -323,7 +323,7 @@ impl pallet_asset_conversion::Config for Runtime { type PalletId = AssetConversionPalletId; type AllowMultiAssetPools = AllowMultiAssetPools; type MaxSwapPathLength = ConstU32<4>; - type MultiAssetId = Box; + type MultiAssetId = MultiLocation; type MultiAssetIdConverter = MultiLocationConverter; type MintMinLiquidity = ConstU128<100>; @@ -1080,18 +1080,18 @@ impl_runtime_apis! { Block, Balance, u128, - Box, + MultiLocation, > for Runtime { - fn quote_price_exact_tokens_for_tokens(asset1: Box, asset2: Box, amount: u128, include_fee: bool) -> Option { + fn quote_price_exact_tokens_for_tokens(asset1: MultiLocation, asset2: MultiLocation, amount: u128, include_fee: bool) -> Option { AssetConversion::quote_price_exact_tokens_for_tokens(asset1, asset2, amount, include_fee) } - fn quote_price_tokens_for_exact_tokens(asset1: Box, asset2: Box, amount: u128, include_fee: bool) -> Option { + fn quote_price_tokens_for_exact_tokens(asset1: MultiLocation, asset2: MultiLocation, amount: u128, include_fee: bool) -> Option { AssetConversion::quote_price_tokens_for_exact_tokens(asset1, asset2, amount, include_fee) } - fn get_reserves(asset1: Box, asset2: Box) -> Option<(Balance, Balance)> { + fn get_reserves(asset1: MultiLocation, asset2: MultiLocation) -> Option<(Balance, Balance)> { AssetConversion::get_reserves(&asset1, &asset2).ok() } } @@ -1428,10 +1428,7 @@ pub mod migrations { /// `MultiLocation { parents: 1, interior: Here }` pub struct NativeAssetParents0ToParents1Migration(sp_std::marker::PhantomData); impl< - T: pallet_asset_conversion::Config< - MultiAssetId = Box, - AssetId = MultiLocation, - >, + T: pallet_asset_conversion::Config, > OnRuntimeUpgrade for NativeAssetParents0ToParents1Migration where ::PoolAssetId: Into, @@ -1457,14 +1454,14 @@ pub mod migrations { pallet_asset_conversion::Pallet::::get_pool_account(&old_pool_id); reads.saturating_accrue(1); let pool_asset_id = pool_info.lp_token.clone(); - if old_pool_id.0.as_ref() != &invalid_native_asset { + if old_pool_id.0 != invalid_native_asset { // skip, if ok continue } // fix new account let new_pool_id = pallet_asset_conversion::Pallet::::get_pool_id( - Box::new(valid_native_asset), + valid_native_asset, old_pool_id.1.clone(), ); let new_pool_account = @@ -1504,10 +1501,10 @@ pub mod migrations { // move LocalOrForeignAssets let _ = T::Assets::transfer( - *old_pool_id.1.as_ref(), + old_pool_id.1, &old_pool_account, &new_pool_account, - T::Assets::balance(*old_pool_id.1.as_ref(), &old_pool_account), + T::Assets::balance(old_pool_id.1, &old_pool_account), Preservation::Expendable, ); reads.saturating_accrue(1); diff --git a/cumulus/parachains/runtimes/assets/common/src/local_and_foreign_assets.rs b/cumulus/parachains/runtimes/assets/common/src/local_and_foreign_assets.rs index 5c66d1cabe57..b0802d35de2d 100644 --- a/cumulus/parachains/runtimes/assets/common/src/local_and_foreign_assets.rs +++ b/cumulus/parachains/runtimes/assets/common/src/local_and_foreign_assets.rs @@ -23,37 +23,36 @@ use frame_support::traits::{ use pallet_asset_conversion::{MultiAssetIdConversionResult, MultiAssetIdConverter}; use parachains_common::AccountId; use sp_runtime::{traits::MaybeEquivalence, DispatchError, DispatchResult}; -use sp_std::{boxed::Box, marker::PhantomData}; +use sp_std::marker::PhantomData; use xcm::latest::MultiLocation; pub struct MultiLocationConverter, MultiLocationMatcher> { _phantom: PhantomData<(NativeAssetLocation, MultiLocationMatcher)>, } -impl - MultiAssetIdConverter, MultiLocation> +impl MultiAssetIdConverter for MultiLocationConverter where NativeAssetLocation: Get, MultiLocationMatcher: Contains, { - fn get_native() -> Box { - Box::new(NativeAssetLocation::get()) + fn get_native() -> MultiLocation { + NativeAssetLocation::get() } - fn is_native(asset_id: &Box) -> bool { + fn is_native(asset_id: &MultiLocation) -> bool { *asset_id == Self::get_native() } fn try_convert( - asset_id: &Box, - ) -> MultiAssetIdConversionResult, MultiLocation> { - if Self::is_native(&asset_id) { + asset_id: &MultiLocation, + ) -> MultiAssetIdConversionResult { + if Self::is_native(asset_id) { return MultiAssetIdConversionResult::Native } - if MultiLocationMatcher::contains(&asset_id) { - MultiAssetIdConversionResult::Converted(*asset_id.clone()) + if MultiLocationMatcher::contains(asset_id) { + MultiAssetIdConversionResult::Converted(asset_id.clone()) } else { MultiAssetIdConversionResult::Unsupported(asset_id.clone()) } @@ -442,27 +441,27 @@ mod tests { interior: X2(GlobalConsensus(ByGenesis([1; 32])), Parachain(2222)), }; - assert!(C::is_native(&Box::new(native_asset))); - assert!(!C::is_native(&Box::new(local_asset))); - assert!(!C::is_native(&Box::new(pool_asset))); - assert!(!C::is_native(&Box::new(foreign_asset1))); - assert!(!C::is_native(&Box::new(foreign_asset2))); + assert!(C::is_native(&native_asset)); + assert!(!C::is_native(&local_asset)); + assert!(!C::is_native(&pool_asset)); + assert!(!C::is_native(&foreign_asset1)); + assert!(!C::is_native(&foreign_asset2)); - assert_eq!(C::try_convert(&Box::new(native_asset)), MultiAssetIdConversionResult::Native); + assert_eq!(C::try_convert(&native_asset), MultiAssetIdConversionResult::Native); assert_eq!( - C::try_convert(&Box::new(local_asset)), + C::try_convert(&local_asset), MultiAssetIdConversionResult::Converted(local_asset) ); assert_eq!( - C::try_convert(&Box::new(pool_asset)), - MultiAssetIdConversionResult::Unsupported(Box::new(pool_asset)) + C::try_convert(&pool_asset), + MultiAssetIdConversionResult::Unsupported(pool_asset) ); assert_eq!( - C::try_convert(&Box::new(foreign_asset1)), + C::try_convert(&foreign_asset1), MultiAssetIdConversionResult::Converted(foreign_asset1) ); assert_eq!( - C::try_convert(&Box::new(foreign_asset2)), + C::try_convert(&foreign_asset2), MultiAssetIdConversionResult::Converted(foreign_asset2) ); } diff --git a/substrate/frame/asset-conversion/src/lib.rs b/substrate/frame/asset-conversion/src/lib.rs index 37d304bf8494..45dcdc0063fe 100644 --- a/substrate/frame/asset-conversion/src/lib.rs +++ b/substrate/frame/asset-conversion/src/lib.rs @@ -96,7 +96,7 @@ use sp_runtime::{ }, DispatchError, RuntimeDebug, Saturating, TokenError, TransactionOutcome, }; -use sp_std::{collections::btree_set::BTreeSet, vec::Vec}; +use sp_std::{boxed::Box, collections::btree_set::BTreeSet, vec::Vec}; #[frame_support::pallet] pub mod pallet { diff --git a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/tests.rs b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/tests.rs index cad301dc9599..d50a05164235 100644 --- a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/tests.rs +++ b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/tests.rs @@ -129,12 +129,16 @@ fn setup_lp(asset_id: u32, balance_factor: u64) { let token_1 = NativeOrAssetId::Native; let token_2 = NativeOrAssetId::Asset(asset_id); - assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(lp_provider), token_1, token_2)); + assert_ok!(AssetConversion::create_pool( + RuntimeOrigin::signed(lp_provider), + Box::new(token_1), + Box::new(token_2) + )); assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(lp_provider), - token_1, - token_2, + Box::new(token_1), + Box::new(token_2), 1_000 * balance_factor, // 1 desired 10_000 * balance_factor, // 2 desired 1, // 1 min From 7fee81e62599885202b6a7a95fd1a0a26848011f Mon Sep 17 00:00:00 2001 From: muharem Date: Sat, 7 Oct 2023 19:42:55 +0200 Subject: [PATCH 26/98] drop clone --- .../runtimes/assets/common/src/local_and_foreign_assets.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cumulus/parachains/runtimes/assets/common/src/local_and_foreign_assets.rs b/cumulus/parachains/runtimes/assets/common/src/local_and_foreign_assets.rs index b0802d35de2d..a8bafa5e8b9e 100644 --- a/cumulus/parachains/runtimes/assets/common/src/local_and_foreign_assets.rs +++ b/cumulus/parachains/runtimes/assets/common/src/local_and_foreign_assets.rs @@ -52,9 +52,9 @@ where } if MultiLocationMatcher::contains(asset_id) { - MultiAssetIdConversionResult::Converted(asset_id.clone()) + MultiAssetIdConversionResult::Converted(*asset_id) } else { - MultiAssetIdConversionResult::Unsupported(asset_id.clone()) + MultiAssetIdConversionResult::Unsupported(*asset_id) } } } From b63cd7349a60643feacce9f96e8c6013567e4788 Mon Sep 17 00:00:00 2001 From: muharem Date: Sun, 8 Oct 2023 15:09:07 +0200 Subject: [PATCH 27/98] box arguments and drop bounded vec from benches --- .../asset-conversion/src/benchmarking.rs | 112 ++++++++++++------ 1 file changed, 75 insertions(+), 37 deletions(-) diff --git a/substrate/frame/asset-conversion/src/benchmarking.rs b/substrate/frame/asset-conversion/src/benchmarking.rs index 87b541cd4744..72033c58c8b2 100644 --- a/substrate/frame/asset-conversion/src/benchmarking.rs +++ b/substrate/frame/asset-conversion/src/benchmarking.rs @@ -21,7 +21,6 @@ use super::*; use frame_benchmarking::{benchmarks, whitelisted_caller}; use frame_support::{ assert_ok, - storage::bounded_vec::BoundedVec, traits::{ fungible::{Inspect as InspectFungible, Mutate as MutateFungible, Unbalanced}, fungibles::{Create, Inspect, Mutate}, @@ -80,8 +79,8 @@ where assert_ok!(AssetConversion::::create_pool( SystemOrigin::Signed(caller.clone()).into(), - asset1.clone(), - asset2.clone() + Box::new(asset1.clone()), + Box::new(asset2.clone()) )); let lp_token = get_lp_token_id::(); @@ -110,7 +109,7 @@ benchmarks! { let asset1 = T::MultiAssetIdConverter::get_native(); let asset2 = T::BenchmarkHelper::multiasset_id(0); let (caller, _) = create_asset::(&asset2); - }: _(SystemOrigin::Signed(caller.clone()), asset1.clone(), asset2.clone()) + }: _(SystemOrigin::Signed(caller.clone()), Box::new(asset1.clone()), Box::new(asset2.clone())) verify { let lp_token = get_lp_token_id::(); let pool_id = (asset1.clone(), asset2.clone()); @@ -128,7 +127,7 @@ benchmarks! { let (lp_token, caller, _) = create_asset_and_pool::(&asset1, &asset2); let ed: u128 = T::Currency::minimum_balance().into(); let add_amount = 1000 + ed; - }: _(SystemOrigin::Signed(caller.clone()), asset1.clone(), asset2.clone(), add_amount.into(), 1000.into(), 0.into(), 0.into(), caller.clone()) + }: _(SystemOrigin::Signed(caller.clone()), Box::new(asset1.clone()), Box::new(asset2.clone()), add_amount.into(), 1000.into(), 0.into(), 0.into(), caller.clone()) verify { let pool_id = (asset1.clone(), asset2.clone()); let lp_minted = AssetConversion::::calc_lp_amount_for_zero_supply(&add_amount.into(), &1000.into()).unwrap().into(); @@ -157,8 +156,8 @@ benchmarks! { AssetConversion::::add_liquidity( SystemOrigin::Signed(caller.clone()).into(), - asset1.clone(), - asset2.clone(), + Box::new(asset1.clone()), + Box::new(asset2.clone()), add_amount.into(), 1000.into(), 0.into(), @@ -166,7 +165,7 @@ benchmarks! { caller.clone(), )?; let total_supply = >::total_issuance(lp_token.clone()); - }: _(SystemOrigin::Signed(caller.clone()), asset1, asset2, remove_lp_amount.into(), 0.into(), 0.into(), caller.clone()) + }: _(SystemOrigin::Signed(caller.clone()), Box::new(asset1), Box::new(asset2), remove_lp_amount.into(), 0.into(), 0.into(), caller.clone()) verify { let new_total_supply = >::total_issuance(lp_token.clone()); assert_eq!( @@ -185,8 +184,8 @@ benchmarks! { AssetConversion::::add_liquidity( SystemOrigin::Signed(caller.clone()).into(), - native.clone(), - asset1.clone(), + Box::new(native.clone()), + Box::new(asset1.clone()), (100 * ed).into(), 200.into(), 0.into(), @@ -199,29 +198,45 @@ benchmarks! { // if we only allow the native-asset pools, then the worst case scenario would be to swap // asset1-native-asset2 if !T::AllowMultiAssetPools::get() { - AssetConversion::::create_pool(SystemOrigin::Signed(caller.clone()).into(), native.clone(), asset2.clone())?; + AssetConversion::::create_pool( + SystemOrigin::Signed(caller.clone()).into(), + Box::new(native.clone()), + Box::new(asset2.clone()) + )?; AssetConversion::::add_liquidity( SystemOrigin::Signed(caller.clone()).into(), - native.clone(), - asset2.clone(), + Box::new(native.clone()), + Box::new(asset2.clone()), (500 * ed).into(), 1000.into(), 0.into(), 0.into(), caller.clone(), )?; - path = vec![asset1.clone(), native.clone(), asset2.clone()]; + path = vec![ + Box::new(asset1.clone()), + Box::new(native.clone()), + Box::new(asset2.clone()) + ]; swap_amount = 100.into(); } else { let asset3 = T::BenchmarkHelper::multiasset_id(3); - AssetConversion::::create_pool(SystemOrigin::Signed(caller.clone()).into(), asset1.clone(), asset2.clone())?; + AssetConversion::::create_pool( + SystemOrigin::Signed(caller.clone()).into(), + Box::new(asset1.clone()), + Box::new(asset2.clone()) + )?; let (_, _) = create_asset::(&asset3); - AssetConversion::::create_pool(SystemOrigin::Signed(caller.clone()).into(), asset2.clone(), asset3.clone())?; + AssetConversion::::create_pool( + SystemOrigin::Signed(caller.clone()).into(), + Box::new(asset2.clone()), + Box::new(asset3.clone()) + )?; AssetConversion::::add_liquidity( SystemOrigin::Signed(caller.clone()).into(), - asset1.clone(), - asset2.clone(), + Box::new(asset1.clone()), + Box::new(asset2.clone()), 200.into(), 2000.into(), 0.into(), @@ -230,19 +245,22 @@ benchmarks! { )?; AssetConversion::::add_liquidity( SystemOrigin::Signed(caller.clone()).into(), - asset2.clone(), - asset3.clone(), + Box::new(asset2.clone()), + Box::new(asset3.clone()), 2000.into(), 2000.into(), 0.into(), 0.into(), caller.clone(), )?; - path = vec![native.clone(), asset1.clone(), asset2.clone(), asset3.clone()]; + path = vec![ + Box::new(native.clone()), + Box::new(asset1.clone()), + Box::new(asset2.clone()), + Box::new(asset3.clone()) + ]; swap_amount = ed.into(); } - - let path: BoundedVec<_, T::MaxSwapPathLength> = BoundedVec::try_from(path).unwrap(); let native_balance = T::Currency::balance(&caller); let asset1_balance = T::Assets::balance(T::BenchmarkHelper::asset_id(1), &caller); }: _(SystemOrigin::Signed(caller.clone()), path, swap_amount, 1.into(), caller.clone(), false) @@ -266,8 +284,8 @@ benchmarks! { AssetConversion::::add_liquidity( SystemOrigin::Signed(caller.clone()).into(), - native.clone(), - asset1.clone(), + Box::new(native.clone()), + Box::new(asset1.clone()), (1000 * ed).into(), 500.into(), 0.into(), @@ -279,28 +297,44 @@ benchmarks! { // if we only allow the native-asset pools, then the worst case scenario would be to swap // asset1-native-asset2 if !T::AllowMultiAssetPools::get() { - AssetConversion::::create_pool(SystemOrigin::Signed(caller.clone()).into(), native.clone(), asset2.clone())?; + AssetConversion::::create_pool( + SystemOrigin::Signed(caller.clone()).into(), + Box::new(native.clone()), + Box::new(asset2.clone()) + )?; AssetConversion::::add_liquidity( SystemOrigin::Signed(caller.clone()).into(), - native.clone(), - asset2.clone(), + Box::new(native.clone()), + Box::new(asset2.clone()), (500 * ed).into(), 1000.into(), 0.into(), 0.into(), caller.clone(), )?; - path = vec![asset1.clone(), native.clone(), asset2.clone()]; + path = vec![ + Box::new(asset1.clone()), + Box::new(native.clone()), + Box::new(asset2.clone()) + ]; } else { - AssetConversion::::create_pool(SystemOrigin::Signed(caller.clone()).into(), asset1.clone(), asset2.clone())?; + AssetConversion::::create_pool( + SystemOrigin::Signed(caller.clone()).into(), + Box::new(asset1.clone()), + Box::new(asset2.clone()) + )?; let asset3 = T::BenchmarkHelper::multiasset_id(3); let (_, _) = create_asset::(&asset3); - AssetConversion::::create_pool(SystemOrigin::Signed(caller.clone()).into(), asset2.clone(), asset3.clone())?; + AssetConversion::::create_pool( + SystemOrigin::Signed(caller.clone()).into(), + Box::new(asset2.clone()), + Box::new(asset3.clone()) + )?; AssetConversion::::add_liquidity( SystemOrigin::Signed(caller.clone()).into(), - asset1.clone(), - asset2.clone(), + Box::new(asset1.clone()), + Box::new(asset2.clone()), 2000.into(), 2000.into(), 0.into(), @@ -309,18 +343,22 @@ benchmarks! { )?; AssetConversion::::add_liquidity( SystemOrigin::Signed(caller.clone()).into(), - asset2.clone(), - asset3.clone(), + Box::new(asset2.clone()), + Box::new(asset3.clone()), 2000.into(), 2000.into(), 0.into(), 0.into(), caller.clone(), )?; - path = vec![native.clone(), asset1.clone(), asset2.clone(), asset3.clone()]; + path = vec![ + Box::new(native.clone()), + Box::new(asset1.clone()), + Box::new(asset2.clone()), + Box::new(asset3.clone()) + ]; } - let path: BoundedVec<_, T::MaxSwapPathLength> = BoundedVec::try_from(path).unwrap(); let asset2_balance = T::Assets::balance(T::BenchmarkHelper::asset_id(2), &caller); let asset3_balance = T::Assets::balance(T::BenchmarkHelper::asset_id(3), &caller); }: _(SystemOrigin::Signed(caller.clone()), path.clone(), 100.into(), (1000 * ed).into(), caller.clone(), false) From 01bd4da4499bb97a8cb02a1ab24cd8a8274221b2 Mon Sep 17 00:00:00 2001 From: muharem Date: Sun, 8 Oct 2023 16:27:38 +0200 Subject: [PATCH 28/98] no box for benchmarks impls --- .../runtimes/assets/asset-hub-kusama/src/xcm_config.rs | 7 +++---- .../runtimes/assets/asset-hub-westend/src/xcm_config.rs | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/xcm_config.rs index 0c197598f889..e29f9b6dfcc6 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/xcm_config.rs @@ -604,8 +604,7 @@ pub struct BenchmarkMultiLocationConverter { } #[cfg(feature = "runtime-benchmarks")] -impl - pallet_asset_conversion::BenchmarkHelper> +impl pallet_asset_conversion::BenchmarkHelper for BenchmarkMultiLocationConverter where SelfParaId: Get, @@ -620,7 +619,7 @@ where ), } } - fn multiasset_id(asset_id: u32) -> sp_std::boxed::Box { - sp_std::boxed::Box::new(Self::asset_id(asset_id)) + fn multiasset_id(asset_id: u32) -> MultiLocation { + Self::asset_id(asset_id) } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs index 6981c290c98c..611a920cc039 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs @@ -602,8 +602,7 @@ pub struct BenchmarkMultiLocationConverter { } #[cfg(feature = "runtime-benchmarks")] -impl - pallet_asset_conversion::BenchmarkHelper> +impl pallet_asset_conversion::BenchmarkHelper for BenchmarkMultiLocationConverter where SelfParaId: Get, @@ -619,7 +618,7 @@ where } } - fn multiasset_id(asset_id: u32) -> sp_std::boxed::Box { - sp_std::boxed::Box::new(Self::asset_id(asset_id)) + fn multiasset_id(asset_id: u32) -> MultiLocation { + Self::asset_id(asset_id) } } From e2b99f96f5828f6e04a490b4700658a5e902ab92 Mon Sep 17 00:00:00 2001 From: muharem Date: Sun, 8 Oct 2023 17:59:29 +0200 Subject: [PATCH 29/98] single balance type for Currency and Assets --- .../assets/asset-hub-kusama/src/lib.rs | 6 +- .../assets/asset-hub-westend/src/lib.rs | 6 +- substrate/bin/node/runtime/src/lib.rs | 6 +- .../asset-conversion/src/benchmarking.rs | 5 +- substrate/frame/asset-conversion/src/lib.rs | 210 +++++++----------- substrate/frame/asset-conversion/src/mock.rs | 3 +- substrate/frame/asset-conversion/src/swap.rs | 10 +- substrate/frame/asset-conversion/src/types.rs | 23 +- .../asset-conversion-tx-payment/src/mock.rs | 1 - .../src/payment.rs | 15 +- 10 files changed, 104 insertions(+), 181 deletions(-) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/lib.rs index efadf4869bf1..123791710070 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/lib.rs @@ -330,7 +330,6 @@ impl pallet_asset_conversion::Config for Runtime { type Balance = Balance; type HigherPrecisionBalance = sp_core::U256; type Currency = Balances; - type AssetBalance = Balance; type AssetId = MultiLocation; type Assets = LocalAndForeignAssets< Assets, @@ -1030,14 +1029,13 @@ impl_runtime_apis! { impl pallet_asset_conversion::AssetConversionApi< Block, Balance, - u128, MultiLocation, > for Runtime { - fn quote_price_exact_tokens_for_tokens(asset1: MultiLocation, asset2: MultiLocation, amount: u128, include_fee: bool) -> Option { + fn quote_price_exact_tokens_for_tokens(asset1: MultiLocation, asset2: MultiLocation, amount: Balance, include_fee: bool) -> Option { AssetConversion::quote_price_exact_tokens_for_tokens(asset1, asset2, amount, include_fee) } - fn quote_price_tokens_for_exact_tokens(asset1: MultiLocation, asset2: MultiLocation, amount: u128, include_fee: bool) -> Option { + fn quote_price_tokens_for_exact_tokens(asset1: MultiLocation, asset2: MultiLocation, amount: Balance, include_fee: bool) -> Option { AssetConversion::quote_price_tokens_for_exact_tokens(asset1, asset2, amount, include_fee) } fn get_reserves(asset1: MultiLocation, asset2: MultiLocation) -> Option<(Balance, Balance)> { diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index 9a27a71bb16a..51f14bfef300 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -307,7 +307,6 @@ impl pallet_asset_conversion::Config for Runtime { type Balance = Balance; type HigherPrecisionBalance = sp_core::U256; type Currency = Balances; - type AssetBalance = Balance; type AssetId = MultiLocation; type Assets = LocalAndForeignAssets< Assets, @@ -1079,15 +1078,14 @@ impl_runtime_apis! { impl pallet_asset_conversion::AssetConversionApi< Block, Balance, - u128, MultiLocation, > for Runtime { - fn quote_price_exact_tokens_for_tokens(asset1: MultiLocation, asset2: MultiLocation, amount: u128, include_fee: bool) -> Option { + fn quote_price_exact_tokens_for_tokens(asset1: MultiLocation, asset2: MultiLocation, amount: Balance, include_fee: bool) -> Option { AssetConversion::quote_price_exact_tokens_for_tokens(asset1, asset2, amount, include_fee) } - fn quote_price_tokens_for_exact_tokens(asset1: MultiLocation, asset2: MultiLocation, amount: u128, include_fee: bool) -> Option { + fn quote_price_tokens_for_exact_tokens(asset1: MultiLocation, asset2: MultiLocation, amount: Balance, include_fee: bool) -> Option { AssetConversion::quote_price_tokens_for_exact_tokens(asset1, asset2, amount, include_fee) } diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index f018639b732e..2db3a3ed3cca 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -1632,7 +1632,6 @@ parameter_types! { impl pallet_asset_conversion::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Balances; - type AssetBalance = ::Balance; type HigherPrecisionBalance = sp_core::U256; type Assets = Assets; type Balance = u128; @@ -2526,15 +2525,14 @@ impl_runtime_apis! { impl pallet_asset_conversion::AssetConversionApi< Block, Balance, - u128, NativeOrAssetId > for Runtime { - fn quote_price_exact_tokens_for_tokens(asset1: NativeOrAssetId, asset2: NativeOrAssetId, amount: u128, include_fee: bool) -> Option { + fn quote_price_exact_tokens_for_tokens(asset1: NativeOrAssetId, asset2: NativeOrAssetId, amount: Balance, include_fee: bool) -> Option { AssetConversion::quote_price_exact_tokens_for_tokens(asset1, asset2, amount, include_fee) } - fn quote_price_tokens_for_exact_tokens(asset1: NativeOrAssetId, asset2: NativeOrAssetId, amount: u128, include_fee: bool) -> Option { + fn quote_price_tokens_for_exact_tokens(asset1: NativeOrAssetId, asset2: NativeOrAssetId, amount: Balance, include_fee: bool) -> Option { AssetConversion::quote_price_tokens_for_exact_tokens(asset1, asset2, amount, include_fee) } diff --git a/substrate/frame/asset-conversion/src/benchmarking.rs b/substrate/frame/asset-conversion/src/benchmarking.rs index 72033c58c8b2..628953d0558f 100644 --- a/substrate/frame/asset-conversion/src/benchmarking.rs +++ b/substrate/frame/asset-conversion/src/benchmarking.rs @@ -48,7 +48,7 @@ where fn create_asset(asset: &T::MultiAssetId) -> (T::AccountId, AccountIdLookupOf) where - T::AssetBalance: From, + T::Balance: From, T::Currency: Unbalanced, T::Assets: Create + Mutate, { @@ -69,7 +69,7 @@ fn create_asset_and_pool( asset2: &T::MultiAssetId, ) -> (T::PoolAssetId, T::AccountId, AccountIdLookupOf) where - T::AssetBalance: From, + T::Balance: From, T::Currency: Unbalanced, T::Assets: Create + Mutate, T::PoolAssetId: Into, @@ -98,7 +98,6 @@ fn assert_last_event(generic_event: ::RuntimeEvent) { benchmarks! { where_clause { where - T::AssetBalance: From + Into, T::Currency: Unbalanced, T::Balance: From + Into, T::Assets: Create + Mutate, diff --git a/substrate/frame/asset-conversion/src/lib.rs b/substrate/frame/asset-conversion/src/lib.rs index 45dcdc0063fe..3e118996f8e9 100644 --- a/substrate/frame/asset-conversion/src/lib.rs +++ b/substrate/frame/asset-conversion/src/lib.rs @@ -118,21 +118,16 @@ pub mod pallet { + MutateFungible + BalancedFungible; - /// The `Currency::Balance` type of the native currency. + /// The type in which the assets for swapping are measured. type Balance: Balance; - /// The type used to describe the amount of fractions converted into assets. - type AssetBalance: Balance; - - /// A type used for conversions between `Balance` and `AssetBalance`. + /// A type used for calculations concerning the `Balance` type to avoid possible overflows. type HigherPrecisionBalance: IntegerSquareRoot + One + Ensure + Unsigned + From - + From + From - + TryInto + TryInto; /// Identifier for the class of non-native asset. @@ -154,7 +149,7 @@ pub mod pallet { type PoolAssetId: AssetId + PartialOrd + Incrementable + From; /// Registry for the assets. - type Assets: Inspect + type Assets: Inspect + Mutate + AccountTouch + ContainsPair @@ -162,7 +157,7 @@ pub mod pallet { /// Registry for the lp tokens. Ideally only this pallet should have create permissions on /// the assets. - type PoolAssets: Inspect + type PoolAssets: Inspect + Create + Mutate + AccountTouch; @@ -184,7 +179,7 @@ pub mod pallet { /// The minimum LP token amount that could be minted. Ameliorates rounding errors. #[pallet::constant] - type MintMinLiquidity: Get; + type MintMinLiquidity: Get; /// The max number of hops in a swap. #[pallet::constant] @@ -244,13 +239,13 @@ pub mod pallet { /// The pool id of the pool that the liquidity was added to. pool_id: PoolIdOf, /// The amount of the first asset that was added to the pool. - amount1_provided: T::AssetBalance, + amount1_provided: T::Balance, /// The amount of the second asset that was added to the pool. - amount2_provided: T::AssetBalance, + amount2_provided: T::Balance, /// The id of the lp token that was minted. lp_token: T::PoolAssetId, /// The amount of lp tokens that were minted of that id. - lp_token_minted: T::AssetBalance, + lp_token_minted: T::Balance, }, /// A successful call of the `RemoveLiquidity` extrinsic will create this event. @@ -262,13 +257,13 @@ pub mod pallet { /// The pool id that the liquidity was removed from. pool_id: PoolIdOf, /// The amount of the first asset that was removed from the pool. - amount1: T::AssetBalance, + amount1: T::Balance, /// The amount of the second asset that was removed from the pool. - amount2: T::AssetBalance, + amount2: T::Balance, /// The id of the lp token that was burned. lp_token: T::PoolAssetId, /// The amount of lp tokens that were burned of that id. - lp_token_burned: T::AssetBalance, + lp_token_burned: T::Balance, /// Liquidity withdrawal fee (%). withdrawal_fee: Permill, }, @@ -280,9 +275,9 @@ pub mod pallet { /// The account that the assets were transferred to. send_to: T::AccountId, /// The amount of the first asset that was swapped. - amount_in: T::AssetBalance, + amount_in: T::Balance, /// The amount of the second asset that was received. - amount_out: T::AssetBalance, + amount_out: T::Balance, /// The route of asset IDs with amounts that the swap went through. /// E.g. (A, amount_in) -> (Dot, amount_out) -> (B, amount_out) path: BalancePath, @@ -290,9 +285,9 @@ pub mod pallet { /// Assets have been converted from one to another. CreditSwapExecuted { /// The amount of the first asset that was swapped. - amount_in: T::AssetBalance, + amount_in: T::Balance, /// The amount of the second asset that was received. - amount_out: T::AssetBalance, + amount_out: T::Balance, /// The route of asset IDs with amounts that the swap went through. /// E.g. (A, amount_in) -> (Dot, amount_out) -> (B, amount_out) path: BalancePath, @@ -461,10 +456,10 @@ pub mod pallet { origin: OriginFor, asset1: Box, asset2: Box, - amount1_desired: T::AssetBalance, - amount2_desired: T::AssetBalance, - amount1_min: T::AssetBalance, - amount2_min: T::AssetBalance, + amount1_desired: T::Balance, + amount2_desired: T::Balance, + amount1_min: T::Balance, + amount2_min: T::Balance, mint_to: T::AccountId, ) -> DispatchResult { let sender = ensure_signed(origin)?; @@ -490,8 +485,8 @@ pub mod pallet { let reserve1 = Self::get_balance(&pool_account, asset1)?; let reserve2 = Self::get_balance(&pool_account, asset2)?; - let amount1: T::AssetBalance; - let amount2: T::AssetBalance; + let amount1: T::Balance; + let amount2: T::Balance; if reserve1.is_zero() || reserve2.is_zero() { amount1 = amount1_desired; amount2 = amount2_desired; @@ -530,7 +525,7 @@ pub mod pallet { let total_supply = T::PoolAssets::total_issuance(pool.lp_token.clone()); - let lp_token_amount: T::AssetBalance; + let lp_token_amount: T::Balance; if total_supply.is_zero() { lp_token_amount = Self::calc_lp_amount_for_zero_supply(&amount1, &amount2)?; T::PoolAssets::mint_into( @@ -573,9 +568,9 @@ pub mod pallet { origin: OriginFor, asset1: Box, asset2: Box, - lp_token_burn: T::AssetBalance, - amount1_min_receive: T::AssetBalance, - amount2_min_receive: T::AssetBalance, + lp_token_burn: T::Balance, + amount1_min_receive: T::Balance, + amount2_min_receive: T::Balance, withdraw_to: T::AccountId, ) -> DispatchResult { let sender = ensure_signed(origin)?; @@ -651,8 +646,8 @@ pub mod pallet { pub fn swap_exact_tokens_for_tokens( origin: OriginFor, path: Vec>, - amount_in: T::AssetBalance, - amount_out_min: T::AssetBalance, + amount_in: T::Balance, + amount_out_min: T::Balance, send_to: T::AccountId, keep_alive: bool, ) -> DispatchResult { @@ -679,8 +674,8 @@ pub mod pallet { pub fn swap_tokens_for_exact_tokens( origin: OriginFor, path: Vec>, - amount_out: T::AssetBalance, - amount_in_max: T::AssetBalance, + amount_out: T::Balance, + amount_in_max: T::Balance, send_to: T::AccountId, keep_alive: bool, ) -> DispatchResult { @@ -713,11 +708,11 @@ pub mod pallet { pub(crate) fn do_swap_exact_tokens_for_tokens( sender: T::AccountId, path: Vec, - amount_in: T::AssetBalance, - amount_out_min: Option, + amount_in: T::Balance, + amount_out_min: Option, send_to: T::AccountId, keep_alive: bool, - ) -> Result { + ) -> Result { ensure!(amount_in > Zero::zero(), Error::::ZeroAmount); if let Some(amount_out_min) = amount_out_min { ensure!(amount_out_min > Zero::zero(), Error::::ZeroAmount); @@ -761,11 +756,11 @@ pub mod pallet { pub(crate) fn do_swap_tokens_for_exact_tokens( sender: T::AccountId, path: Vec, - amount_out: T::AssetBalance, - amount_in_max: Option, + amount_out: T::Balance, + amount_in_max: Option, send_to: T::AccountId, keep_alive: bool, - ) -> Result { + ) -> Result { ensure!(amount_out > Zero::zero(), Error::::ZeroAmount); if let Some(amount_in_max) = amount_in_max { ensure!(amount_in_max > Zero::zero(), Error::::ZeroAmount); @@ -808,12 +803,9 @@ pub mod pallet { pub(crate) fn do_swap_exact_credit_tokens_for_tokens( path: Vec, credit_in: Credit, - amount_out_min: Option, + amount_out_min: Option, ) -> Result, (Credit, DispatchError)> { - let amount_in = match credit_in.peek() { - Ok(a) => a, - Err(e) => return Err((credit_in, e)), - }; + let amount_in = credit_in.peek(); let inspect_path = |credit_asset| { ensure!(path.get(0).map_or(false, |a| *a == credit_asset), Error::::InvalidPath); ensure!(!amount_in.is_zero(), Error::::ZeroAmount); @@ -856,12 +848,9 @@ pub mod pallet { pub(crate) fn do_swap_credit_tokens_for_exact_tokens( path: Vec, credit_in: Credit, - amount_out: T::AssetBalance, + amount_out: T::Balance, ) -> Result<(Credit, Credit), (Credit, DispatchError)> { - let amount_in_max = match credit_in.peek() { - Ok(a) => a, - Err(e) => return Err((credit_in, e)), - }; + let amount_in_max = credit_in.peek(); let inspect_path = |credit_asset| { ensure!(path.get(0).map_or(false, |a| a == &credit_asset), Error::::InvalidPath); ensure!(amount_in_max > Zero::zero(), Error::::ZeroAmount); @@ -883,7 +872,7 @@ pub mod pallet { Err(e) => return Err((credit_in, e)), }; - let (credit_in, credit_change) = credit_in.split(amount_in)?; + let (credit_in, credit_change) = credit_in.split(amount_in); let credit_out = Self::credit_swap(credit_in, &path)?; Self::deposit_event(Event::CreditSwapExecuted { amount_in, amount_out, path }); @@ -896,9 +885,9 @@ pub mod pallet { asset_id: &T::MultiAssetId, from: &T::AccountId, to: &T::AccountId, - amount: T::AssetBalance, + amount: T::Balance, keep_alive: bool, - ) -> Result { + ) -> Result { let preservation = match keep_alive { true => Preserve, false => Expendable, @@ -906,15 +895,8 @@ pub mod pallet { match T::MultiAssetIdConverter::try_convert(asset_id) { MultiAssetIdConversionResult::Converted(asset_id) => T::Assets::transfer(asset_id, from, to, amount, preservation), - MultiAssetIdConversionResult::Native => { - let amount = Self::convert_asset_balance_to_native_balance(amount)?; - Ok(Self::convert_native_balance_to_asset_balance(T::Currency::transfer( - from, - to, - amount, - preservation, - )?)?) - }, + MultiAssetIdConversionResult::Native => + Ok(T::Currency::transfer(from, to, amount, preservation)?), MultiAssetIdConversionResult::Unsupported(_) => Err(Error::::UnsupportedAsset.into()), } @@ -934,7 +916,7 @@ pub mod pallet { fn withdraw( asset: &T::MultiAssetId, who: &T::AccountId, - value: T::AssetBalance, + value: T::Balance, keep_alive: bool, ) -> Result, DispatchError> { let preservation = match keep_alive { @@ -954,7 +936,6 @@ pub mod pallet { .map(|c| c.into()) }, MultiAssetIdConversionResult::Native => { - let value = Self::convert_asset_balance_to_native_balance(value)?; if preservation == Preserve { // TODO drop the ensure! when this issue addressed // https://github.com/paritytech/polkadot-sdk/issues/1698 @@ -968,31 +949,6 @@ pub mod pallet { } } - /// Convert a `Balance` type to an `AssetBalance`. - pub(crate) fn convert_native_balance_to_asset_balance( - amount: T::Balance, - ) -> Result> { - T::HigherPrecisionBalance::from(amount) - .try_into() - .map_err(|_| Error::::Overflow) - } - - /// Convert an `AssetBalance` type to a `Balance`. - pub(crate) fn convert_asset_balance_to_native_balance( - amount: T::AssetBalance, - ) -> Result> { - T::HigherPrecisionBalance::from(amount) - .try_into() - .map_err(|_| Error::::Overflow) - } - - /// Convert a `HigherPrecisionBalance` type to an `AssetBalance`. - pub(crate) fn convert_hpb_to_asset_balance( - amount: T::HigherPrecisionBalance, - ) -> Result> { - amount.try_into().map_err(|_| Error::::Overflow) - } - /// Swap assets along the `path`, withdrawing from `sender` and depositing in `send_to`. /// /// Note: It's assumed that the provided `path` is valid. @@ -1080,19 +1036,17 @@ pub mod pallet { } /// Get the `owner`'s balance of `asset`, which could be the chain's native asset or another - /// fungible. Returns a value in the form of an `AssetBalance`. + /// fungible. Returns a value in the form of an `Balance`. fn get_balance( owner: &T::AccountId, asset: &T::MultiAssetId, - ) -> Result> { + ) -> Result> { match T::MultiAssetIdConverter::try_convert(asset) { MultiAssetIdConversionResult::Converted(asset_id) => Ok( <::Assets>::reducible_balance(asset_id, owner, Expendable, Polite), ), MultiAssetIdConversionResult::Native => - Self::convert_native_balance_to_asset_balance( - <::Currency>::reducible_balance(owner, Expendable, Polite), - ), + Ok(<::Currency>::reducible_balance(owner, Expendable, Polite)), MultiAssetIdConversionResult::Unsupported(_) => Err(Error::::UnsupportedAsset.into()), } @@ -1127,7 +1081,7 @@ pub mod pallet { pub fn get_reserves( asset1: &T::MultiAssetId, asset2: &T::MultiAssetId, - ) -> Result<(T::AssetBalance, T::AssetBalance), Error> { + ) -> Result<(T::Balance, T::Balance), Error> { let pool_id = Self::get_pool_id(asset1.clone(), asset2.clone()); let pool_account = Self::get_pool_account(&pool_id); @@ -1143,11 +1097,11 @@ pub mod pallet { /// Leading to an amount at the end of a `path`, get the required amounts in. pub(crate) fn balance_path_from_amount_out( - amount_out: T::AssetBalance, + amount_out: T::Balance, path: Vec, ) -> Result, DispatchError> { let mut balance_path: BalancePath = Vec::with_capacity(path.len()); - let mut amount_in: T::AssetBalance = amount_out; + let mut amount_in: T::Balance = amount_out; let mut iter = path.into_iter().rev().peekable(); while let Some(asset2) = iter.next() { @@ -1169,11 +1123,11 @@ pub mod pallet { /// Following an amount into a `path`, get the corresponding amounts out. pub(crate) fn balance_path_from_amount_in( - amount_in: T::AssetBalance, + amount_in: T::Balance, path: Vec, ) -> Result, DispatchError> { let mut balance_path: BalancePath = Vec::with_capacity(path.len()); - let mut amount_out: T::AssetBalance = amount_in; + let mut amount_out: T::Balance = amount_in; let mut iter = path.into_iter().peekable(); while let Some(asset1) = iter.next() { @@ -1195,9 +1149,9 @@ pub mod pallet { pub fn quote_price_exact_tokens_for_tokens( asset1: T::MultiAssetId, asset2: T::MultiAssetId, - amount: T::AssetBalance, + amount: T::Balance, include_fee: bool, - ) -> Option { + ) -> Option { let pool_id = Self::get_pool_id(asset1.clone(), asset2.clone()); let pool_account = Self::get_pool_account(&pool_id); @@ -1218,9 +1172,9 @@ pub mod pallet { pub fn quote_price_tokens_for_exact_tokens( asset1: T::MultiAssetId, asset2: T::MultiAssetId, - amount: T::AssetBalance, + amount: T::Balance, include_fee: bool, - ) -> Option { + ) -> Option { let pool_id = Self::get_pool_id(asset1.clone(), asset2.clone()); let pool_account = Self::get_pool_account(&pool_id); @@ -1239,18 +1193,18 @@ pub mod pallet { /// Calculates the optimal amount from the reserves. pub fn quote( - amount: &T::AssetBalance, - reserve1: &T::AssetBalance, - reserve2: &T::AssetBalance, - ) -> Result> { + amount: &T::Balance, + reserve1: &T::Balance, + reserve2: &T::Balance, + ) -> Result> { // (amount * reserve2) / reserve1 Self::mul_div(amount, reserve2, reserve1) } pub(super) fn calc_lp_amount_for_zero_supply( - amount1: &T::AssetBalance, - amount2: &T::AssetBalance, - ) -> Result> { + amount1: &T::Balance, + amount2: &T::Balance, + ) -> Result> { let amount1 = T::HigherPrecisionBalance::from(*amount1); let amount2 = T::HigherPrecisionBalance::from(*amount2); @@ -1264,11 +1218,7 @@ pub mod pallet { result.try_into().map_err(|_| Error::::Overflow) } - fn mul_div( - a: &T::AssetBalance, - b: &T::AssetBalance, - c: &T::AssetBalance, - ) -> Result> { + fn mul_div(a: &T::Balance, b: &T::Balance, c: &T::Balance) -> Result> { let a = T::HigherPrecisionBalance::from(*a); let b = T::HigherPrecisionBalance::from(*b); let c = T::HigherPrecisionBalance::from(*c); @@ -1287,10 +1237,10 @@ pub mod pallet { /// Given an input amount of an asset and pair reserves, returns the maximum output amount /// of the other asset. pub fn get_amount_out( - amount_in: &T::AssetBalance, - reserve_in: &T::AssetBalance, - reserve_out: &T::AssetBalance, - ) -> Result> { + amount_in: &T::Balance, + reserve_in: &T::Balance, + reserve_out: &T::Balance, + ) -> Result> { let amount_in = T::HigherPrecisionBalance::from(*amount_in); let reserve_in = T::HigherPrecisionBalance::from(*reserve_in); let reserve_out = T::HigherPrecisionBalance::from(*reserve_out); @@ -1322,10 +1272,10 @@ pub mod pallet { /// Given an output amount of an asset and pair reserves, returns a required input amount /// of the other asset. pub fn get_amount_in( - amount_out: &T::AssetBalance, - reserve_in: &T::AssetBalance, - reserve_out: &T::AssetBalance, - ) -> Result> { + amount_out: &T::Balance, + reserve_in: &T::Balance, + reserve_out: &T::Balance, + ) -> Result> { let amount_out = T::HigherPrecisionBalance::from(*amount_out); let reserve_in = T::HigherPrecisionBalance::from(*reserve_in); let reserve_out = T::HigherPrecisionBalance::from(*reserve_out); @@ -1360,10 +1310,7 @@ pub mod pallet { } /// Ensure that a `value` meets the minimum balance requirements of an `asset` class. - fn validate_minimal_amount( - value: T::AssetBalance, - asset: &T::MultiAssetId, - ) -> Result<(), ()> { + fn validate_minimal_amount(value: T::Balance, asset: &T::MultiAssetId) -> Result<(), ()> { if T::MultiAssetIdConverter::is_native(asset) { let ed = T::Currency::minimum_balance(); ensure!( @@ -1414,22 +1361,21 @@ pub mod pallet { sp_api::decl_runtime_apis! { /// This runtime api allows people to query the size of the liquidity pools /// and quote prices for swaps. - pub trait AssetConversionApi where - Balance: Codec + MaybeDisplay, - AssetBalance: frame_support::traits::tokens::Balance, + pub trait AssetConversionApi where + Balance: frame_support::traits::tokens::Balance + MaybeDisplay, AssetId: Codec { /// Provides a quote for [`Pallet::swap_tokens_for_exact_tokens`]. /// /// Note that the price may have changed by the time the transaction is executed. /// (Use `amount_in_max` to control slippage.) - fn quote_price_tokens_for_exact_tokens(asset1: AssetId, asset2: AssetId, amount: AssetBalance, include_fee: bool) -> Option; + fn quote_price_tokens_for_exact_tokens(asset1: AssetId, asset2: AssetId, amount: Balance, include_fee: bool) -> Option; /// Provides a quote for [`Pallet::swap_exact_tokens_for_tokens`]. /// /// Note that the price may have changed by the time the transaction is executed. /// (Use `amount_out_min` to control slippage.) - fn quote_price_exact_tokens_for_tokens(asset1: AssetId, asset2: AssetId, amount: AssetBalance, include_fee: bool) -> Option; + fn quote_price_exact_tokens_for_tokens(asset1: AssetId, asset2: AssetId, amount: Balance, include_fee: bool) -> Option; /// Returns the size of the liquidity pool for the given asset pair. fn get_reserves(asset1: AssetId, asset2: AssetId) -> Option<(Balance, Balance)>; diff --git a/substrate/frame/asset-conversion/src/mock.rs b/substrate/frame/asset-conversion/src/mock.rs index 3a19f39e7ca6..03949afd07e7 100644 --- a/substrate/frame/asset-conversion/src/mock.rs +++ b/substrate/frame/asset-conversion/src/mock.rs @@ -152,7 +152,6 @@ ord_parameter_types! { impl Config for Test { type RuntimeEvent = RuntimeEvent; type Currency = Balances; - type AssetBalance = ::Balance; type AssetId = u32; type PoolAssetId = u32; type Assets = Assets; @@ -167,7 +166,7 @@ impl Config for Test { type MaxSwapPathLength = ConstU32<4>; type MintMinLiquidity = ConstU128<100>; // 100 is good enough when the main currency has 12 decimals. - type Balance = u128; + type Balance = ::Balance; type HigherPrecisionBalance = sp_core::U256; type MultiAssetId = NativeOrAssetId; diff --git a/substrate/frame/asset-conversion/src/swap.rs b/substrate/frame/asset-conversion/src/swap.rs index a86b55d41a53..a760ffd10a65 100644 --- a/substrate/frame/asset-conversion/src/swap.rs +++ b/substrate/frame/asset-conversion/src/swap.rs @@ -105,7 +105,7 @@ pub trait SwapCredit { } impl Swap for Pallet { - type Balance = T::HigherPrecisionBalance; + type Balance = T::Balance; type MultiAssetId = T::MultiAssetId; fn max_path_len() -> u32 { @@ -120,12 +120,11 @@ impl Swap for Pallet { send_to: T::AccountId, keep_alive: bool, ) -> Result { - let amount_out_min = amount_out_min.map(Self::convert_hpb_to_asset_balance).transpose()?; let amount_out = with_storage_layer(|| { Self::do_swap_exact_tokens_for_tokens( sender, path, - Self::convert_hpb_to_asset_balance(amount_in)?, + amount_in, amount_out_min, send_to, keep_alive, @@ -142,12 +141,11 @@ impl Swap for Pallet { send_to: T::AccountId, keep_alive: bool, ) -> Result { - let amount_in_max = amount_in_max.map(Self::convert_hpb_to_asset_balance).transpose()?; let amount_in = with_storage_layer(|| { Self::do_swap_tokens_for_exact_tokens( sender, path, - Self::convert_hpb_to_asset_balance(amount_out)?, + amount_out, amount_in_max, send_to, keep_alive, @@ -158,7 +156,7 @@ impl Swap for Pallet { } impl SwapCredit for Pallet { - type Balance = T::AssetBalance; + type Balance = T::Balance; type MultiAssetId = T::MultiAssetId; type Credit = Credit; diff --git a/substrate/frame/asset-conversion/src/types.rs b/substrate/frame/asset-conversion/src/types.rs index 466faf434031..c2bc74c51226 100644 --- a/substrate/frame/asset-conversion/src/types.rs +++ b/substrate/frame/asset-conversion/src/types.rs @@ -36,7 +36,7 @@ pub(super) type PoolIdOf = (::MultiAssetId, ::Multi /// 1. `asset(asset1, amount_in)` take from `user` and move to the pool(asset1, asset2); /// 2. `asset(asset2, amount_out2)` transfer from pool(asset1, asset2) to pool(asset2, asset3); /// 3. `asset(asset3, amount_out3)` move from pool(asset2, asset3) to `user`. -pub(super) type BalancePath = Vec<(::MultiAssetId, ::AssetBalance)>; +pub(super) type BalancePath = Vec<(::MultiAssetId, ::Balance)>; /// Stores the lp_token asset id a particular pool has been assigned. #[derive(Decode, Encode, Default, PartialEq, Eq, MaxEncodedLen, TypeInfo)] @@ -206,14 +206,11 @@ impl Credit { } /// Amount of `self`. - pub fn peek(&self) -> Result { - let amount = match self { - Credit::Native(c) => T::HigherPrecisionBalance::from(c.peek()) - .try_into() - .map_err(|_| Error::::Overflow)?, + pub fn peek(&self) -> T::Balance { + match self { + Credit::Native(c) => c.peek(), Credit::Asset(c) => c.peek(), - }; - Ok(amount) + } } /// Asset class of `self`. @@ -226,19 +223,15 @@ impl Credit { /// Consume `self` and return two independent instances; the first is guaranteed to be at most /// `amount` and the second will be the remainder. - pub fn split(self, amount: T::AssetBalance) -> Result<(Self, Self), (Self, DispatchError)> { + pub fn split(self, amount: T::Balance) -> (Self, Self) { match self { Credit::Native(c) => { - let amount = match T::HigherPrecisionBalance::from(amount).try_into() { - Ok(a) => a, - Err(_) => return Err((Self::Native(c), Error::::Overflow.into())), - }; let (left, right) = c.split(amount); - Ok((left.into(), right.into())) + (left.into(), right.into()) }, Credit::Asset(c) => { let (left, right) = c.split(amount); - Ok((left.into(), right.into())) + (left.into(), right.into()) }, } } diff --git a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs index bfbe8b4178ce..0091861f85fc 100644 --- a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs +++ b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs @@ -235,7 +235,6 @@ ord_parameter_types! { impl pallet_asset_conversion::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Balances; - type AssetBalance = ::Balance; type AssetId = u32; type PoolAssetId = u32; type Assets = Assets; diff --git a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/payment.rs b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/payment.rs index ac13e121dd65..ccea9e55bbed 100644 --- a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/payment.rs +++ b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/payment.rs @@ -83,8 +83,8 @@ impl OnChargeAssetTransaction for AssetConversionAdapter where T: Config, C: Inspect<::AccountId>, - CON: Swap, - T::HigherPrecisionBalance: From> + TryInto>, + CON: Swap, MultiAssetId = T::MultiAssetId>, + BalanceOf: Into>, T::MultiAssetId: From>, BalanceOf: IsType<::AccountId>>::Balance>, { @@ -117,22 +117,18 @@ where let asset_consumed = CON::swap_tokens_for_exact_tokens( who.clone(), vec![asset_id.into(), T::MultiAssetIdConverter::get_native()], - T::HigherPrecisionBalance::from(native_asset_required), + native_asset_required, None, who.clone(), true, ) .map_err(|_| TransactionValidityError::from(InvalidTransaction::Payment))?; - let asset_consumed = asset_consumed - .try_into() - .map_err(|_| TransactionValidityError::from(InvalidTransaction::Payment))?; - ensure!(asset_consumed > Zero::zero(), InvalidTransaction::Payment); // charge the fee in native currency ::withdraw_fee(who, call, info, fee, tip) - .map(|r| (r, native_asset_required, asset_consumed)) + .map(|r| (r, native_asset_required, asset_consumed.into())) } /// Correct the fee and swap the refund back to asset. @@ -175,8 +171,7 @@ where T::MultiAssetIdConverter::get_native(), // we provide the native asset_id.into(), // we want asset_id back ], - T::HigherPrecisionBalance::from(swap_back), /* amount of the native asset to - * convert to `asset_id` */ + swap_back, // amount of the native asset to convert to `asset_id` None, // no minimum amount back who.clone(), // we will refund to `who` false, // no need to keep alive From 37b2e34f6dd1ee334566507c753a73e991055f42 Mon Sep 17 00:00:00 2001 From: muharem Date: Sun, 8 Oct 2023 18:00:15 +0200 Subject: [PATCH 30/98] Balance trait bound on Balance generic type --- substrate/frame/asset-conversion/src/swap.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/substrate/frame/asset-conversion/src/swap.rs b/substrate/frame/asset-conversion/src/swap.rs index a760ffd10a65..5dff3403fb83 100644 --- a/substrate/frame/asset-conversion/src/swap.rs +++ b/substrate/frame/asset-conversion/src/swap.rs @@ -22,7 +22,7 @@ use super::*; /// Trait for providing methods to swap between the various asset classes. pub trait Swap { /// Measure units of the asset classes for swapping. - type Balance; + type Balance: Balance; /// Kind of assets that are going to be swapped. type MultiAssetId; @@ -67,7 +67,7 @@ pub trait Swap { /// Trait providing methods to swap between the various asset classes. pub trait SwapCredit { /// Measure units of the asset classes for swapping. - type Balance; + type Balance: Balance; /// Kind of assets that are going to be swapped. type MultiAssetId; /// Credit implying a negative imbalance in the system that can be placed into an account or From d9bc83607f349dde5dc11aae2a8a0169f8a90247 Mon Sep 17 00:00:00 2001 From: muharem Date: Sun, 8 Oct 2023 19:13:26 +0200 Subject: [PATCH 31/98] try into for credit --- substrate/frame/asset-conversion/src/types.rs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/substrate/frame/asset-conversion/src/types.rs b/substrate/frame/asset-conversion/src/types.rs index c2bc74c51226..4e9ebf792ca2 100644 --- a/substrate/frame/asset-conversion/src/types.rs +++ b/substrate/frame/asset-conversion/src/types.rs @@ -161,6 +161,12 @@ impl MultiAssetIdConverter, Asset } } +/// Provides a way to retrieve an underlying value without changing the state of the object. +pub trait Inspectable { + /// Retrieves the underlying value without modifying it. + fn peek(&self) -> Value; +} + /// Credit of [Config::Currency]. /// /// Implies a negative imbalance in the system that can be placed into an account or alter the total @@ -199,6 +205,26 @@ impl From> for Credit { } } +impl TryInto> for Credit { + type Error = (); + fn try_into(self) -> Result, ()> { + match self { + Credit::Native(c) => Ok(c), + _ => Err(()), + } + } +} + +impl TryInto> for Credit { + type Error = (); + fn try_into(self) -> Result, ()> { + match self { + Credit::Asset(c) => Ok(c), + _ => Err(()), + } + } +} + impl Credit { /// Create zero native credit. pub fn native_zero() -> Self { From 01938649d7dc70d80ca864e05d7bd75d7d95a645 Mon Sep 17 00:00:00 2001 From: muharem Date: Sun, 8 Oct 2023 19:15:23 +0200 Subject: [PATCH 32/98] drop clone --- cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index 51f14bfef300..14170ed99af3 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -1460,7 +1460,7 @@ pub mod migrations { // fix new account let new_pool_id = pallet_asset_conversion::Pallet::::get_pool_id( valid_native_asset, - old_pool_id.1.clone(), + *old_pool_id.1, ); let new_pool_account = pallet_asset_conversion::Pallet::::get_pool_account(&new_pool_id); From ae19bb86901d1f777210265e81693d7fda2726c2 Mon Sep 17 00:00:00 2001 From: muharem Date: Sun, 8 Oct 2023 20:12:17 +0200 Subject: [PATCH 33/98] fix --- cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index 14170ed99af3..cd7fc226693c 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -1460,7 +1460,7 @@ pub mod migrations { // fix new account let new_pool_id = pallet_asset_conversion::Pallet::::get_pool_id( valid_native_asset, - *old_pool_id.1, + old_pool_id.1, ); let new_pool_account = pallet_asset_conversion::Pallet::::get_pool_account(&new_pool_id); From 23722774b973c6f19a4ab6606097cfc3163049e4 Mon Sep 17 00:00:00 2001 From: muharem Date: Mon, 9 Oct 2023 10:50:39 +0200 Subject: [PATCH 34/98] clippy fixes --- .../asset-hub-westend/src/tests/swap.rs | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/swap.rs b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/swap.rs index d56a4c7e4de6..feb30fa0347e 100644 --- a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/swap.rs +++ b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/swap.rs @@ -43,8 +43,8 @@ fn swap_locally_on_chain_using_local_assets() { assert_ok!(::AssetConversion::create_pool( ::RuntimeOrigin::signed(AssetHubWestendSender::get()), - Box::new(asset_native.clone()), - Box::new(asset_one.clone()), + Box::new(asset_native), + Box::new(asset_one), )); assert_expected_events!( @@ -56,8 +56,8 @@ fn swap_locally_on_chain_using_local_assets() { assert_ok!(::AssetConversion::add_liquidity( ::RuntimeOrigin::signed(AssetHubWestendSender::get()), - Box::new(asset_native.clone()), - Box::new(asset_one.clone()), + Box::new(asset_native), + Box::new(asset_one), 1_000_000_000_000, 2_000_000_000_000, 0, @@ -72,7 +72,7 @@ fn swap_locally_on_chain_using_local_assets() { ] ); - let path = vec![Box::new(asset_native.clone()), Box::new(asset_one.clone())]; + let path = vec![Box::new(asset_native), Box::new(asset_one)]; assert_ok!(::AssetConversion::swap_exact_tokens_for_tokens( ::RuntimeOrigin::signed(AssetHubWestendSender::get()), @@ -240,8 +240,8 @@ fn swap_locally_on_chain_using_foreign_assets() { // 4. Create pool: assert_ok!(::AssetConversion::create_pool( ::RuntimeOrigin::signed(AssetHubWestendSender::get()), - Box::new(asset_native.clone()), - Box::new(foreign_asset1_at_asset_hub_westend.clone()), + Box::new(asset_native), + Box::new(foreign_asset1_at_asset_hub_westend), )); assert_expected_events!( @@ -256,8 +256,8 @@ fn swap_locally_on_chain_using_foreign_assets() { ::RuntimeOrigin::signed( sov_penpal_on_asset_hub_westend.clone() ), - Box::new(asset_native.clone()), - Box::new(foreign_asset1_at_asset_hub_westend.clone()), + Box::new(asset_native), + Box::new(foreign_asset1_at_asset_hub_westend), 1_000_000_000_000, 2_000_000_000_000, 0, @@ -275,10 +275,7 @@ fn swap_locally_on_chain_using_foreign_assets() { ); // 6. Swap! - let path = vec![ - Box::new(asset_native.clone()), - Box::new(foreign_asset1_at_asset_hub_westend.clone()), - ]; + let path = vec![Box::new(asset_native), Box::new(foreign_asset1_at_asset_hub_westend)]; assert_ok!(::AssetConversion::swap_exact_tokens_for_tokens( ::RuntimeOrigin::signed(AssetHubWestendSender::get()), @@ -341,7 +338,7 @@ fn cannot_create_pool_from_pool_assets() { assert_matches::assert_matches!( ::AssetConversion::create_pool( ::RuntimeOrigin::signed(AssetHubWestendSender::get()), - Box::new(asset_native.clone()), + Box::new(asset_native), Box::new(asset_one), ), Err(DispatchError::Module(ModuleError{index: _, error: _, message})) => assert_eq!(message, Some("UnsupportedAsset")) From d1f37c781995eef02561f36a709f4bf554744e7b Mon Sep 17 00:00:00 2001 From: muharem Date: Mon, 9 Oct 2023 11:34:40 +0200 Subject: [PATCH 35/98] review fixes --- substrate/frame/asset-conversion/src/lib.rs | 8 +-- substrate/frame/asset-conversion/src/swap.rs | 53 ++++++++----------- substrate/frame/asset-conversion/src/types.rs | 5 +- 3 files changed, 28 insertions(+), 38 deletions(-) diff --git a/substrate/frame/asset-conversion/src/lib.rs b/substrate/frame/asset-conversion/src/lib.rs index 3e118996f8e9..e25725157c32 100644 --- a/substrate/frame/asset-conversion/src/lib.rs +++ b/substrate/frame/asset-conversion/src/lib.rs @@ -283,7 +283,7 @@ pub mod pallet { path: BalancePath, }, /// Assets have been converted from one to another. - CreditSwapExecuted { + SwapCreditExecuted { /// The amount of the first asset that was swapped. amount_in: T::Balance, /// The amount of the second asset that was received. @@ -351,7 +351,7 @@ pub mod pallet { NonUniquePath, /// It was not possible to get or increment the Id of the pool. IncorrectPoolAssetId, - /// Account cannot exist with the funds that would be given. + /// The destination account cannot exist with the swapped funds. BelowMinimum, } @@ -828,7 +828,7 @@ pub mod pallet { let credit_out = Self::credit_swap(credit_in, &path)?; - Self::deposit_event(Event::CreditSwapExecuted { amount_in, amount_out, path }); + Self::deposit_event(Event::SwapCreditExecuted { amount_in, amount_out, path }); Ok(credit_out) } @@ -875,7 +875,7 @@ pub mod pallet { let (credit_in, credit_change) = credit_in.split(amount_in); let credit_out = Self::credit_swap(credit_in, &path)?; - Self::deposit_event(Event::CreditSwapExecuted { amount_in, amount_out, path }); + Self::deposit_event(Event::SwapCreditExecuted { amount_in, amount_out, path }); Ok((credit_out, credit_change)) } diff --git a/substrate/frame/asset-conversion/src/swap.rs b/substrate/frame/asset-conversion/src/swap.rs index 5dff3403fb83..26b5e45ca84b 100644 --- a/substrate/frame/asset-conversion/src/swap.rs +++ b/substrate/frame/asset-conversion/src/swap.rs @@ -169,22 +169,17 @@ impl SwapCredit for Pallet { credit_in: Self::Credit, amount_out_min: Option, ) -> Result { - let transaction_res = - with_transaction(|| -> TransactionOutcome> { - let res = - Self::do_swap_exact_credit_tokens_for_tokens(path, credit_in, amount_out_min); - match &res { - Ok(_) => TransactionOutcome::Commit(Ok(res)), - // wrapping `res` with `Ok`, since our `Err` doesn't satisfy the - // `From` bound of the `with_transaction` function. - Err(_) => TransactionOutcome::Rollback(Ok(res)), - } - }); - match transaction_res { - Ok(r) => r, - // should never happen, `with_transaction` above never returns an `Err` variant. - Err(_) => Err((Self::Credit::native_zero(), DispatchError::Corruption)), - } + with_transaction(|| -> TransactionOutcome> { + let res = Self::do_swap_exact_credit_tokens_for_tokens(path, credit_in, amount_out_min); + match &res { + Ok(_) => TransactionOutcome::Commit(Ok(res)), + // wrapping `res` with `Ok`, since our `Err` doesn't satisfy the + // `From` bound of the `with_transaction` function. + Err(_) => TransactionOutcome::Rollback(Ok(res)), + } + }) + // should never map an error since `with_transaction` above never returns it. + .map_err(|_| (Self::Credit::native_zero(), DispatchError::Corruption))? } fn swap_tokens_for_exact_tokens( @@ -192,20 +187,16 @@ impl SwapCredit for Pallet { credit_in: Self::Credit, amount_out: Self::Balance, ) -> Result<(Self::Credit, Self::Credit), (Self::Credit, DispatchError)> { - let transaction_res = - with_transaction(|| -> TransactionOutcome> { - let res = Self::do_swap_credit_tokens_for_exact_tokens(path, credit_in, amount_out); - match &res { - Ok(_) => TransactionOutcome::Commit(Ok(res)), - // wrapping `res` with `Ok`, since our `Err` doesn't satisfy the - // `From` bound of the `with_transaction` function. - Err(_) => TransactionOutcome::Rollback(Ok(res)), - } - }); - match transaction_res { - Ok(r) => r, - // should never happen, `with_transaction` above never returns an `Err` variant. - Err(_) => Err((Self::Credit::native_zero(), DispatchError::Corruption)), - } + with_transaction(|| -> TransactionOutcome> { + let res = Self::do_swap_credit_tokens_for_exact_tokens(path, credit_in, amount_out); + match &res { + Ok(_) => TransactionOutcome::Commit(Ok(res)), + // wrapping `res` with `Ok`, since our `Err` doesn't satisfy the + // `From` bound of the `with_transaction` function. + Err(_) => TransactionOutcome::Rollback(Ok(res)), + } + }) + // should never map an error since `with_transaction` above never returns it. + .map_err(|_| (Self::Credit::native_zero(), DispatchError::Corruption))? } } diff --git a/substrate/frame/asset-conversion/src/types.rs b/substrate/frame/asset-conversion/src/types.rs index 4e9ebf792ca2..849b1fc9a1cd 100644 --- a/substrate/frame/asset-conversion/src/types.rs +++ b/substrate/frame/asset-conversion/src/types.rs @@ -27,9 +27,8 @@ use sp_std::{cmp::Ordering, marker::PhantomData}; /// migration. pub(super) type PoolIdOf = (::MultiAssetId, ::MultiAssetId); -/// Represents a swap path with associated asset amounts for input and output. -/// -/// Each pair of neighboring assets forms a pool. +/// Represents a swap path with associated asset amounts indicating how much of the asset needs to +/// be deposited to get the following asset's amount withdrawn (this is inclusive of fees). /// /// Example: /// Given path [(asset1, amount_in), (asset2, amount_out2), (asset3, amount_out3)], can be resolved: From 3a4935ec1a8e2b5244c886febc129069abec10e8 Mon Sep 17 00:00:00 2001 From: muharem Date: Mon, 9 Oct 2023 12:56:28 +0200 Subject: [PATCH 36/98] remove unused trait --- substrate/frame/asset-conversion/src/types.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/substrate/frame/asset-conversion/src/types.rs b/substrate/frame/asset-conversion/src/types.rs index 849b1fc9a1cd..d5ad8f590653 100644 --- a/substrate/frame/asset-conversion/src/types.rs +++ b/substrate/frame/asset-conversion/src/types.rs @@ -160,12 +160,6 @@ impl MultiAssetIdConverter, Asset } } -/// Provides a way to retrieve an underlying value without changing the state of the object. -pub trait Inspectable { - /// Retrieves the underlying value without modifying it. - fn peek(&self) -> Value; -} - /// Credit of [Config::Currency]. /// /// Implies a negative imbalance in the system that can be placed into an account or alter the total From 75a7b8a3b53a054653b68e54298fa59ede77f18e Mon Sep 17 00:00:00 2001 From: muharem Date: Tue, 10 Oct 2023 12:53:59 +0200 Subject: [PATCH 37/98] note in docs for swap traits --- substrate/frame/asset-conversion/src/swap.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/substrate/frame/asset-conversion/src/swap.rs b/substrate/frame/asset-conversion/src/swap.rs index 26b5e45ca84b..7f59b8f9b804 100644 --- a/substrate/frame/asset-conversion/src/swap.rs +++ b/substrate/frame/asset-conversion/src/swap.rs @@ -37,6 +37,8 @@ pub trait Swap { /// respecting `keep_alive`. /// /// If successful, returns the amount of `path[last]` acquired for the `amount_in`. + /// + /// This operation is expected to be atomic. fn swap_exact_tokens_for_tokens( sender: AccountId, path: Vec, @@ -54,6 +56,8 @@ pub trait Swap { /// respecting `keep_alive`. /// /// If successful returns the amount of the `path[0]` taken to provide `path[last]`. + /// + /// This operation is expected to be atomic. fn swap_tokens_for_exact_tokens( sender: AccountId, path: Vec, @@ -83,6 +87,8 @@ pub trait SwapCredit { /// On a successful swap, the function returns the `credit_out` of `path[last]` obtained from /// the `credit_in`. On failure, it returns an `Err` containing the original `credit_in` and the /// associated error code. + /// + /// This operation is expected to be atomic. fn swap_exact_tokens_for_tokens( path: Vec, credit_in: Self::Credit, @@ -97,6 +103,8 @@ pub trait SwapCredit { /// represents the acquired amount of the `path[last]` asset, and `credit_change` is the /// remaining portion from the `credit_in`. On failure, an `Err` with the initial `credit_in` /// and error code is returned. + /// + /// This operation is expected to be atomic. fn swap_tokens_for_exact_tokens( path: Vec, credit_in: Self::Credit, From 03cee5b028734c8764ba15580eb8fbc2380cf54d Mon Sep 17 00:00:00 2001 From: muharem Date: Wed, 11 Oct 2023 11:50:18 +0200 Subject: [PATCH 38/98] extract for imbalance --- substrate/frame/balances/src/impl_currency.rs | 10 ++++++++++ .../support/src/traits/tokens/fungible/imbalance.rs | 7 +++++++ .../support/src/traits/tokens/fungibles/imbalance.rs | 9 +++++++++ substrate/frame/support/src/traits/tokens/imbalance.rs | 7 +++++++ 4 files changed, 33 insertions(+) diff --git a/substrate/frame/balances/src/impl_currency.rs b/substrate/frame/balances/src/impl_currency.rs index c64a4929dd56..1ac882ade70d 100644 --- a/substrate/frame/balances/src/impl_currency.rs +++ b/substrate/frame/balances/src/impl_currency.rs @@ -100,6 +100,11 @@ mod imbalances { mem::forget(self); (Self(first), Self(second)) } + fn extract(&mut self, amount: T::Balance) -> Self { + let new = self.0.min(amount); + self.0 = self.0 - new; + Self(new) + } fn merge(mut self, other: Self) -> Self { self.0 = self.0.saturating_add(other.0); mem::forget(other); @@ -159,6 +164,11 @@ mod imbalances { mem::forget(self); (Self(first), Self(second)) } + fn extract(&mut self, amount: T::Balance) -> Self { + let new = self.0.min(amount); + self.0 = self.0 - new; + Self(new) + } fn merge(mut self, other: Self) -> Self { self.0 = self.0.saturating_add(other.0); mem::forget(other); diff --git a/substrate/frame/support/src/traits/tokens/fungible/imbalance.rs b/substrate/frame/support/src/traits/tokens/fungible/imbalance.rs index 32a63fd25b29..995797bc8f66 100644 --- a/substrate/frame/support/src/traits/tokens/fungible/imbalance.rs +++ b/substrate/frame/support/src/traits/tokens/fungible/imbalance.rs @@ -113,6 +113,13 @@ impl, OppositeOnDrop: HandleImbalance sp_std::mem::forget(self); (Imbalance::new(first), Imbalance::new(second)) } + + fn extract(&mut self, amount: B) -> Self { + let new = self.amount.min(amount); + self.amount = self.amount - new; + Imbalance::new(new) + } + fn merge(mut self, other: Self) -> Self { self.amount = self.amount.saturating_add(other.amount); sp_std::mem::forget(other); diff --git a/substrate/frame/support/src/traits/tokens/fungibles/imbalance.rs b/substrate/frame/support/src/traits/tokens/fungibles/imbalance.rs index 9f660a5f1689..7c0d7721a2e6 100644 --- a/substrate/frame/support/src/traits/tokens/fungibles/imbalance.rs +++ b/substrate/frame/support/src/traits/tokens/fungibles/imbalance.rs @@ -109,6 +109,15 @@ impl< sp_std::mem::forget(self); (Imbalance::new(asset.clone(), first), Imbalance::new(asset, second)) } + + /// Mutate `self` by extracting a new instance with at most `amount` value, reducing `self` + /// accordingly. + pub fn extract(&mut self, amount: B) -> Self { + let new = self.amount.min(amount); + self.amount = self.amount - new; + Imbalance::new(self.asset.clone(), new) + } + pub fn merge(mut self, other: Self) -> Result { if self.asset == other.asset { self.amount = self.amount.saturating_add(other.amount); diff --git a/substrate/frame/support/src/traits/tokens/imbalance.rs b/substrate/frame/support/src/traits/tokens/imbalance.rs index 403321725042..e487b8122341 100644 --- a/substrate/frame/support/src/traits/tokens/imbalance.rs +++ b/substrate/frame/support/src/traits/tokens/imbalance.rs @@ -72,6 +72,10 @@ pub trait Imbalance: Sized + TryDrop + Default { /// is guaranteed to be at most `amount` and the second will be the remainder. fn split(self, amount: Balance) -> (Self, Self); + /// Mutate `self` by extracting a new instance with at most `amount` value, reducing `self` + /// accordingly. + fn extract(&mut self, amount: Balance) -> Self; + /// Consume `self` and return two independent instances; the amounts returned will be in /// approximately the same ratio as `first`:`second`. /// @@ -190,6 +194,9 @@ impl Imbalance for () { fn split(self, _: Balance) -> (Self, Self) { ((), ()) } + fn extract(&mut self, _: Balance) -> Self { + () + } fn ration(self, _: u32, _: u32) -> (Self, Self) where Balance: From + Saturating + Div, From 6b0c847ac419b3d0ed3b894751c16e1cbfed1600 Mon Sep 17 00:00:00 2001 From: muharem Date: Wed, 11 Oct 2023 12:13:27 +0200 Subject: [PATCH 39/98] swap weight trader --- Cargo.lock | 1 + cumulus/primitives/utility/Cargo.toml | 4 +- cumulus/primitives/utility/src/lib.rs | 223 +++++++++++++++++++++++++- 3 files changed, 223 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 46f6b6ea1fc9..3b1424017c1c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3675,6 +3675,7 @@ dependencies = [ "cumulus-primitives-core", "frame-support", "log", + "pallet-asset-conversion", "parity-scale-codec", "polkadot-runtime-common", "sp-io", diff --git a/cumulus/primitives/utility/Cargo.toml b/cumulus/primitives/utility/Cargo.toml index 9ed1e664ac21..ec0f9b6b3f98 100644 --- a/cumulus/primitives/utility/Cargo.toml +++ b/cumulus/primitives/utility/Cargo.toml @@ -13,6 +13,7 @@ frame-support = { path = "../../../substrate/frame/support", default-features = sp-io = { path = "../../../substrate/primitives/io", default-features = false} sp-runtime = { path = "../../../substrate/primitives/runtime", default-features = false} sp-std = { path = "../../../substrate/primitives/std", default-features = false} +pallet-asset-conversion = { path = "../../../substrate/frame/asset-conversion", default-features = false} # Polkadot polkadot-runtime-common = { path = "../../../polkadot/runtime/common", default-features = false} @@ -20,7 +21,6 @@ xcm = { package = "staging-xcm", path = "../../../polkadot/xcm", default-feature xcm-executor = { package = "staging-xcm-executor", path = "../../../polkadot/xcm/xcm-executor", default-features = false} xcm-builder = { package = "staging-xcm-builder", path = "../../../polkadot/xcm/xcm-builder", default-features = false} - # Cumulus cumulus-primitives-core = { path = "../core", default-features = false } @@ -37,6 +37,7 @@ std = [ "xcm-builder/std", "xcm-executor/std", "xcm/std", + "pallet-asset-conversion/std", ] runtime-benchmarks = [ @@ -45,4 +46,5 @@ runtime-benchmarks = [ "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", + "pallet-asset-conversion/runtime-benchmarks", ] diff --git a/cumulus/primitives/utility/src/lib.rs b/cumulus/primitives/utility/src/lib.rs index 7d07bc329ed4..bcc05b10d0d7 100644 --- a/cumulus/primitives/utility/src/lib.rs +++ b/cumulus/primitives/utility/src/lib.rs @@ -23,13 +23,24 @@ use codec::Encode; use cumulus_primitives_core::{MessageSendError, UpwardMessageSender}; use frame_support::{ traits::{ - tokens::{fungibles, fungibles::Inspect}, - Get, + tokens::{ + fungible, + fungible::Credit as FungibleCredit, + fungibles, + fungibles::{Credit as FungiblesCredit, Inspect}, + }, + Get, Imbalance, OnUnbalanced as OnUnbalancedT, }, - weights::Weight, + weights::{Weight, WeightToFee as WeightToFeeT}, +}; +use pallet_asset_conversion::{ + MultiAssetIdConverter as SwapAssetMatcherT, SwapCredit as SwapCreditT, }; use polkadot_runtime_common::xcm_sender::ConstantPrice; -use sp_runtime::{traits::Saturating, SaturatedConversion}; +use sp_runtime::{ + traits::{Saturating, Zero}, + SaturatedConversion, +}; use sp_std::{marker::PhantomData, prelude::*}; use xcm::{latest::prelude::*, WrapVersion}; use xcm_builder::TakeRevenue; @@ -309,6 +320,210 @@ pub trait ChargeWeightInFungibles Result<>::Balance, XcmError>; } +/// Provides an implementation of [`WeightTrader`] to charge for weight using the first asset +/// specified in the `payment` argument. +/// +/// Note: The asset utilized for weight must be non-native and must be exchangeable for a native +/// asset through `SwapCredit`. +// TODO: decouple implementation from the native asset concept, allowing the `TargetAsset` +// argument as an exchange asset. Refer to https://github.com/paritytech/polkadot-sdk/issues/1842. +pub struct SwapFirstAssetTrader< + AccountId, + SwapCredit: SwapCreditT, + SwapAssetMatcher: SwapAssetMatcherT, + WeightToFee: WeightToFeeT, + Fungibles: fungibles::Balanced, + FungiblesAssetMatcher: MatchesFungibles, + Fungible: fungible::Balanced, + OnUnbalanced: OnUnbalancedT>, +> where + SwapCredit::MultiAssetId: From, + SwapCredit::Credit: From> + + From> + + TryInto> + + TryInto>, + Fungible::Balance: Into, + Fungibles::Balance: Into, +{ + /// Accumulated fee paid for XCM execution. + total_fee: FungibleCredit, + /// Last asset utilized by a client to settle a fee. + last_fee_asset: Option, + _phantom_data: PhantomData<( + AccountId, + SwapCredit, + SwapAssetMatcher, + WeightToFee, + Fungibles, + FungiblesAssetMatcher, + Fungible, + OnUnbalanced, + )>, +} + +impl< + AccountId, + SwapCredit: SwapCreditT, + SwapAssetMatcher: SwapAssetMatcherT, + WeightToFee: WeightToFeeT, + Fungibles: fungibles::Balanced, + FungiblesAssetMatcher: MatchesFungibles, + Fungible: fungible::Balanced, + OnUnbalanced: OnUnbalancedT>, + > WeightTrader + for SwapFirstAssetTrader< + AccountId, + SwapCredit, + SwapAssetMatcher, + WeightToFee, + Fungibles, + FungiblesAssetMatcher, + Fungible, + OnUnbalanced, + > where + SwapCredit::MultiAssetId: From, + SwapCredit::Credit: From> + + From> + + TryInto> + + TryInto>, + Fungible::Balance: Into, + Fungibles::Balance: Into, +{ + fn new() -> Self { + Self { + total_fee: FungibleCredit::::zero(), + last_fee_asset: None, + _phantom_data: PhantomData, + } + } + + fn buy_weight( + &mut self, + weight: Weight, + mut payment: xcm_executor::Assets, + _context: &XcmContext, + ) -> Result { + let first_asset: MultiAsset = + payment.fungible.pop_first().ok_or(XcmError::AssetNotFound)?.into(); + let (fungibles_asset, balance) = FungiblesAssetMatcher::matches_fungibles(&first_asset) + .map_err(|_| XcmError::FeesNotMet)?; + + let swap_asset = fungibles_asset.clone().into(); + if SwapAssetMatcher::is_native(&swap_asset) { + return Err(XcmError::FeesNotMet) + } + + let credit_in = Fungibles::issue(fungibles_asset, balance); + let fee = WeightToFee::weight_to_fee(&weight); + + let (credit_out, credit_change) = SwapCredit::swap_tokens_for_exact_tokens( + vec![swap_asset, SwapAssetMatcher::get_native()], + credit_in.into(), + fee, + ) + .map_err(|(credit_in, _)| { + drop(credit_in); + XcmError::FeesNotMet + })?; + + // the execution below must be infallible to keep this operation transactional. + self.total_fee.subsume( + // should never fail since `credit_out` is `FungibleCredit`. + // TODO: make this infallible. Refer to https://github.com/paritytech/polkadot-sdk/issues/1842. + credit_out.try_into().map_err(|_| XcmError::FeesNotMet)?, + ); + self.last_fee_asset = Some(first_asset.id); + + let credit_change: FungiblesCredit = + // should never fail since `credit_change` is `FungiblesCredit`. + // TODO: make this infallible. Refer to https://github.com/paritytech/polkadot-sdk/issues/1842. + credit_change.try_into().map_err(|_| XcmError::FeesNotMet)?; + + payment.fungible.insert(first_asset.id, credit_change.peek().into()); + drop(credit_change); + Ok(payment) + } + + fn refund_weight(&mut self, weight: Weight, _context: &XcmContext) -> Option { + if self.total_fee.peek().is_zero() { + return None + } + let mut refund_asset = if let Some(asset) = &self.last_fee_asset { + (asset.clone(), 0).into() + } else { + return None + }; + let refund_amount = WeightToFee::weight_to_fee(&weight); + if refund_amount >= self.total_fee.peek() { + return None + } + + let refund_swap_asset = FungiblesAssetMatcher::matches_fungibles(&refund_asset) + .map(|(a, _)| a.into()) + .ok()?; + + let refund = self.total_fee.extract(refund_amount); + let refund: FungibleCredit = + match SwapCredit::swap_exact_tokens_for_tokens( + vec![SwapAssetMatcher::get_native(), refund_swap_asset], + refund.into(), + None, + ) { + Ok(r) => r.try_into().ok()?, + Err((c, _)) => { + self.total_fee.subsume( + // should never fail since `credit_in` is `FungibleCredit`. + // TODO: make this infallible. Refer to https://github.com/paritytech/polkadot-sdk/issues/1842. + c.try_into().ok()?, + ); + return None + }, + }; + + // the execution below must be infallible to keep this operation atomic. + refund_asset.fun = refund.peek().into().into(); + drop(refund); + Some(refund_asset) + } +} + +impl< + AccountId, + SwapCredit: SwapCreditT, + SwapAssetMatcher: SwapAssetMatcherT, + WeightToFee: WeightToFeeT, + Fungibles: fungibles::Balanced, + FungiblesAssetMatcher: MatchesFungibles, + Fungible: fungible::Balanced, + OnUnbalanced: OnUnbalancedT>, + > Drop + for SwapFirstAssetTrader< + AccountId, + SwapCredit, + SwapAssetMatcher, + WeightToFee, + Fungibles, + FungiblesAssetMatcher, + Fungible, + OnUnbalanced, + > where + SwapCredit::MultiAssetId: From, + SwapCredit::Credit: From> + + From> + + TryInto> + + TryInto>, + Fungible::Balance: Into, + Fungibles::Balance: Into, +{ + fn drop(&mut self) { + if self.total_fee.peek().is_zero() { + return + } + let total_fee = self.total_fee.extract(self.total_fee.peek()); + OnUnbalanced::on_unbalanced(total_fee); + } +} + #[cfg(test)] mod tests { use super::*; From 1847090c0b6bbb5b11bef67ab5f62bcd2b613617 Mon Sep 17 00:00:00 2001 From: muharem Date: Wed, 11 Oct 2023 12:14:07 +0200 Subject: [PATCH 40/98] swap weight trader for westend xcm executor --- .../assets/asset-hub-westend/src/lib.rs | 12 +++++++----- .../assets/asset-hub-westend/src/xcm_config.rs | 18 ++++++++++-------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index cd7fc226693c..167ac1fa064f 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -302,17 +302,19 @@ impl pallet_assets::Config for Runtime { type BenchmarkHelper = (); } +type LocalAndTrustedForeignAssets = LocalAndForeignAssets< + Assets, + AssetIdForTrustBackedAssetsConvert, + ForeignAssets, +>; + impl pallet_asset_conversion::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Balance = Balance; type HigherPrecisionBalance = sp_core::U256; type Currency = Balances; type AssetId = MultiLocation; - type Assets = LocalAndForeignAssets< - Assets, - AssetIdForTrustBackedAssetsConvert, - ForeignAssets, - >; + type Assets = LocalAndTrustedForeignAssets; type PoolAssets = PoolAssets; type PoolAssetId = u32; type PoolSetupFee = ConstU128<0>; // Asset class deposit fees are sufficient to prevent spam diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs index 611a920cc039..855b0ec423ca 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs @@ -499,16 +499,18 @@ impl xcm_executor::Config for XcmConfig { >; type Trader = ( UsingComponents>, - cumulus_primitives_utility::TakeFirstAssetTrader< + cumulus_primitives_utility::SwapFirstAssetTrader< AccountId, - AssetFeeAsExistentialDepositMultiplierFeeCharger, - TrustBackedAssetsConvertedConcreteId, - Assets, - cumulus_primitives_utility::XcmFeesTo32ByteAccount< - FungiblesTransactor, - AccountId, - XcmAssetFeesReceiver, + crate::AssetConversion, + crate::MultiLocationConverter< + WestendLocation, + LocalAndForeignAssetsMultiLocationMatcher, >, + WeightToFee, + super::LocalAndTrustedForeignAssets, + ForeignAssetsConvertedConcreteId, + crate::Balances, + (), // TODO: deposit to treasury account. >, ); type ResponseHandler = PolkadotXcm; From 1c11bc3eaba13c6534c949e166378baee843f940 Mon Sep 17 00:00:00 2001 From: muharem Date: Wed, 11 Oct 2023 12:45:27 +0200 Subject: [PATCH 41/98] clippi fix: remove clone --- cumulus/primitives/utility/src/lib.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/cumulus/primitives/utility/src/lib.rs b/cumulus/primitives/utility/src/lib.rs index bcc05b10d0d7..6bda965d00cc 100644 --- a/cumulus/primitives/utility/src/lib.rs +++ b/cumulus/primitives/utility/src/lib.rs @@ -448,11 +448,8 @@ impl< if self.total_fee.peek().is_zero() { return None } - let mut refund_asset = if let Some(asset) = &self.last_fee_asset { - (asset.clone(), 0).into() - } else { - return None - }; + let mut refund_asset = + if let Some(asset) = &self.last_fee_asset { (*asset, 0).into() } else { return None }; let refund_amount = WeightToFee::weight_to_fee(&weight); if refund_amount >= self.total_fee.peek() { return None From 991b734a827df869a5a8700d7ca2a56984c575be Mon Sep 17 00:00:00 2001 From: muharem Date: Wed, 11 Oct 2023 14:46:51 +0200 Subject: [PATCH 42/98] fmt --- cumulus/primitives/utility/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cumulus/primitives/utility/Cargo.toml b/cumulus/primitives/utility/Cargo.toml index ec0f9b6b3f98..891ac9e571be 100644 --- a/cumulus/primitives/utility/Cargo.toml +++ b/cumulus/primitives/utility/Cargo.toml @@ -30,6 +30,7 @@ std = [ "codec/std", "cumulus-primitives-core/std", "frame-support/std", + "pallet-asset-conversion/std", "polkadot-runtime-common/std", "sp-io/std", "sp-runtime/std", @@ -37,14 +38,13 @@ std = [ "xcm-builder/std", "xcm-executor/std", "xcm/std", - "pallet-asset-conversion/std", ] runtime-benchmarks = [ "frame-support/runtime-benchmarks", + "pallet-asset-conversion/runtime-benchmarks", "polkadot-runtime-common/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", - "pallet-asset-conversion/runtime-benchmarks", ] From ec25bca3185927c37d2e693e707c538b4298856c Mon Sep 17 00:00:00 2001 From: muharem Date: Wed, 11 Oct 2023 14:48:46 +0200 Subject: [PATCH 43/98] ignore tests --- .../runtimes/assets/asset-hub-westend/tests/tests.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs index 599ff90e254a..a408945c5d8b 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs @@ -67,6 +67,8 @@ fn collator_session_keys() -> CollatorSessionKeys { ) } +// TODO: rewrite for a new weight trader +#[ignore] #[test] fn test_asset_xcm_trader() { ExtBuilder::::default() @@ -147,6 +149,8 @@ fn test_asset_xcm_trader() { }); } +// TODO: rewrite for a new weight trader +#[ignore] #[test] fn test_asset_xcm_trader_with_refund() { ExtBuilder::::default() @@ -278,6 +282,8 @@ fn test_asset_xcm_trader_refund_not_possible_since_amount_less_than_ed() { }); } +// TODO: rewrite for a new weight trader +#[ignore] #[test] fn test_that_buying_ed_refund_does_not_refund() { ExtBuilder::::default() From b3d3caf2c57ef33f29020426c86d3f92561f566c Mon Sep 17 00:00:00 2001 From: muharem Date: Wed, 11 Oct 2023 16:02:34 +0200 Subject: [PATCH 44/98] resolve credit to account impl --- cumulus/parachains/common/src/impls.rs | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/cumulus/parachains/common/src/impls.rs b/cumulus/parachains/common/src/impls.rs index 107cd5c68732..4979d469604c 100644 --- a/cumulus/parachains/common/src/impls.rs +++ b/cumulus/parachains/common/src/impls.rs @@ -17,10 +17,12 @@ //! Taken from polkadot/runtime/common (at a21cd64) and adapted for parachains. use frame_support::traits::{ - fungibles::{self, Balanced, Credit}, + fungible::{Balanced, Credit}, + fungibles::{self, Balanced as FungiblesBalanced, Credit as FungiblesCredit}, Contains, ContainsPair, Currency, Get, Imbalance, OnUnbalanced, }; use pallet_asset_tx_payment::HandleCredit; +use sp_core::TypedGet; use sp_runtime::traits::Zero; use sp_std::marker::PhantomData; use xcm::latest::{AssetId, Fungibility::Fungible, MultiAsset, MultiLocation}; @@ -75,7 +77,7 @@ where R: pallet_authorship::Config + pallet_assets::Config, AccountIdOf: From + Into, { - fn handle_credit(credit: Credit, pallet_assets::Pallet>) { + fn handle_credit(credit: FungiblesCredit, pallet_assets::Pallet>) { if let Some(author) = pallet_authorship::Pallet::::author() { // In case of error: Will drop the result triggering the `OnDrop` of the imbalance. let _ = pallet_assets::Pallet::::resolve(&author, credit); @@ -83,6 +85,17 @@ where } } +/// Implementation of [`OnUnbalanced`] which resolves the received credit to the specified account. +/// +/// If an account does not exist and the credit amount is less than the existential deposit, the +/// credit cannot be resolved and is dropped. +pub struct ResolveCreditTo(PhantomData<(A, F)>); +impl> OnUnbalanced> for ResolveCreditTo { + fn on_unbalanced(credit: Credit) { + let _ = F::resolve(&A::get(), credit).map_err(|c| drop(c)); + } +} + /// Allow checking in assets that have issuance > 0. pub struct NonZeroIssuance(PhantomData<(AccountId, Assets)>); impl Contains<>::AssetId> @@ -250,8 +263,8 @@ mod tests { #[test] fn test_fees_and_tip_split() { new_test_ext().execute_with(|| { - let fee = Balances::issue(10); - let tip = Balances::issue(20); + let fee = >::issue(10); + let tip = >::issue(20); assert_eq!(Balances::free_balance(TEST_ACCOUNT), 0); From e16f2f7c1f5b1909cc17e505de3fdea39c9b8fe2 Mon Sep 17 00:00:00 2001 From: muharem Date: Wed, 11 Oct 2023 16:03:01 +0200 Subject: [PATCH 45/98] resolve fees to treasury account --- .../assets/asset-hub-westend/Cargo.toml | 4 +- .../asset-hub-westend/src/xcm_config.rs | 38 +++++++++++++++---- 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml index f7f6fdf68e46..dc71dd7b131e 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml @@ -7,7 +7,7 @@ description = "Westend variant of Asset Hub parachain runtime" [dependencies] codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "max-encoded-len"] } -hex-literal = { version = "0.4.1", optional = true } +hex-literal = { version = "0.4.1" } log = { version = "0.4.20", default-features = false } scale-info = { version = "2.9.0", default-features = false, features = ["derive"] } smallvec = "1.11.0" @@ -79,7 +79,6 @@ parachains-common = { path = "../../../common", default-features = false } assets-common = { path = "../common", default-features = false } [dev-dependencies] -hex-literal = "0.4.1" asset-test-utils = { path = "../test-utils" } sp-io = { path = "../../../../../substrate/primitives/io" } @@ -98,7 +97,6 @@ runtime-benchmarks = [ "frame-support/runtime-benchmarks", "frame-system-benchmarking/runtime-benchmarks", "frame-system/runtime-benchmarks", - "hex-literal", "pallet-asset-conversion/runtime-benchmarks", "pallet-assets/runtime-benchmarks", "pallet-balances/runtime-benchmarks", diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs index 855b0ec423ca..694c01cf7c2c 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs @@ -31,18 +31,22 @@ use frame_support::{ }; use frame_system::EnsureRoot; use pallet_xcm::XcmPassthrough; -use parachains_common::{impls::ToStakingPot, xcm_config::AssetFeeAsExistentialDepositMultiplier}; +use parachains_common::{ + impls::{ResolveCreditTo, ToStakingPot}, + xcm_config::AssetFeeAsExistentialDepositMultiplier, +}; use polkadot_parachain_primitives::primitives::Sibling; use sp_runtime::traits::ConvertInto; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, CurrencyAdapter, - DenyReserveTransferToRelayChain, DenyThenTry, EnsureXcmOrigin, FungiblesAdapter, IsConcrete, - LocalMint, NativeAsset, NoChecking, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, - SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, - SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, - UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, + DenyReserveTransferToRelayChain, DenyThenTry, DescribeFamily, DescribePalletTerminal, + EnsureXcmOrigin, FungiblesAdapter, HashedDescription, IsConcrete, LocalMint, NativeAsset, + NoChecking, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, + SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, + WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, }; use xcm_executor::{traits::WithOriginFilter, XcmExecutor}; @@ -63,8 +67,17 @@ parameter_types! { pub PoolAssetsPalletLocation: MultiLocation = PalletInstance(::index() as u8).into(); pub CheckingAccount: AccountId = PolkadotXcm::check_account(); + /// Relay Chain Treasury pallet account derived via [`PalletLocationToAccountId`]. + // proof: [`self::ensure_relay_treasury_account_correct()`]. + pub RelayTreasuryAccount: AccountId = + AccountId::from(hex_literal::hex!("8bb46fbc6413c0f273b1b09af7b83f30695801958b9151a8f899f468f80064b1")); } +// Foreign chain account alias into local accounts according to a hash of their standard +// description. +pub type PalletLocationToAccountId = + HashedDescription>; + /// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used /// when determining ownership of accounts for asset transacting and when attempting to use XCM /// `Transact` in order to determine the dispatch Origin. @@ -75,6 +88,9 @@ pub type LocationToAccountId = ( SiblingParachainConvertsVia, // Straight up local `AccountId32` origins just alias directly to `AccountId`. AccountId32Aliases, + // Foreign chain account alias into local accounts according to a hash of their standard + // description. + PalletLocationToAccountId, ); /// Means for transacting the native currency on this chain. @@ -510,7 +526,7 @@ impl xcm_executor::Config for XcmConfig { super::LocalAndTrustedForeignAssets, ForeignAssetsConvertedConcreteId, crate::Balances, - (), // TODO: deposit to treasury account. + ResolveCreditTo, >, ); type ResponseHandler = PolkadotXcm; @@ -624,3 +640,11 @@ where Self::asset_id(asset_id) } } + +#[test] +fn ensure_relay_treasury_account_correct() { + use xcm_executor::traits::ConvertLocation; + let location = MultiLocation::new(1, X1(Junction::PalletInstance(37))); + let account = PalletLocationToAccountId::convert_location(&location).unwrap(); + assert_eq!(account, RelayTreasuryAccount::get()); +} From 20b85a5fada8f55c98ba831964f5866ffeadf4da Mon Sep 17 00:00:00 2001 From: muharem Date: Wed, 11 Oct 2023 16:13:40 +0200 Subject: [PATCH 46/98] ignore test --- .../emulated/assets/asset-hub-westend/src/tests/send.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/send.rs b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/send.rs index 424d222bef38..a1b7baef713e 100644 --- a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/send.rs +++ b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/send.rs @@ -66,6 +66,8 @@ fn send_transact_sudo_from_relay_to_system_para_works() { /// Parachain should be able to send XCM paying its fee with sufficient asset /// in the System Parachain +// TODO: rewrite for a new weight trader +#[ignore] #[test] fn send_xcm_from_para_to_system_para_paying_fee_with_assets_works() { let para_sovereign_account = AssetHubWestend::sovereign_account_id_of( From ec2cf1d5085bcba7f9b6bf828eaae63551c2c6a1 Mon Sep 17 00:00:00 2001 From: muharem Date: Wed, 18 Oct 2023 17:21:53 +0200 Subject: [PATCH 47/98] impl on_nonzero_unbalanced instead on_unbalanced --- .../traits/tokens/imbalance/on_unbalanced.rs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/substrate/frame/support/src/traits/tokens/imbalance/on_unbalanced.rs b/substrate/frame/support/src/traits/tokens/imbalance/on_unbalanced.rs index 27bfe46e181e..4be6cec346b8 100644 --- a/substrate/frame/support/src/traits/tokens/imbalance/on_unbalanced.rs +++ b/substrate/frame/support/src/traits/tokens/imbalance/on_unbalanced.rs @@ -53,3 +53,29 @@ pub trait OnUnbalanced { } impl OnUnbalanced for () {} + +/// Resolves received asset credit to account `A`, implementing [`OnUnbalanced`]. +/// +/// Credits that cannot be resolved to account `A` are dropped. This may occur if the account for +/// address `A` does not exist and the existential deposit requirement is not met. +pub struct ResolveTo(PhantomData<(A, F)>); +impl> OnUnbalanced> + for ResolveTo +{ + fn on_nonzero_unbalanced(credit: fungible::Credit) { + let _ = F::resolve(&A::get(), credit).map_err(|c| drop(c)); + } +} + +/// Resolves received asset credit to account `A`, implementing [`OnUnbalanced`]. +/// +/// Credits that cannot be resolved to account `A` are dropped. This may occur if the account for +/// address `A` does not exist and the existential deposit requirement is not met. +pub struct ResolveAssetTo(PhantomData<(A, F)>); +impl> OnUnbalanced> + for ResolveAssetTo +{ + fn on_nonzero_unbalanced(credit: fungibles::Credit) { + let _ = F::resolve(&A::get(), credit).map_err(|c| drop(c)); + } +} From 2b13f27e951c6fb76326a467a4afa8e369cb8359 Mon Sep 17 00:00:00 2001 From: muharem Date: Tue, 24 Oct 2023 12:34:15 +0200 Subject: [PATCH 48/98] adjustments for rococo setup --- .../runtimes/assets/asset-hub-rococo/src/lib.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs index 20b59368a5b5..b8487f2e8f9e 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -338,7 +338,6 @@ impl pallet_asset_conversion::Config for Runtime { type Balance = Balance; type HigherPrecisionBalance = sp_core::U256; type Currency = Balances; - type AssetBalance = Balance; type AssetId = MultiLocation; type Assets = LocalAndForeignAssets< Assets, @@ -355,7 +354,7 @@ impl pallet_asset_conversion::Config for Runtime { type PalletId = AssetConversionPalletId; type AllowMultiAssetPools = AllowMultiAssetPools; type MaxSwapPathLength = ConstU32<4>; - type MultiAssetId = Box; + type MultiAssetId = MultiLocation; type MultiAssetIdConverter = MultiLocationConverter; type MintMinLiquidity = ConstU128<100>; @@ -1111,17 +1110,18 @@ impl_runtime_apis! { impl pallet_asset_conversion::AssetConversionApi< Block, Balance, - u128, - Box, + MultiLocation, > for Runtime { - fn quote_price_exact_tokens_for_tokens(asset1: Box, asset2: Box, amount: u128, include_fee: bool) -> Option { + fn quote_price_exact_tokens_for_tokens(asset1: MultiLocation, asset2: MultiLocation, amount: Balance, include_fee: bool) -> Option { AssetConversion::quote_price_exact_tokens_for_tokens(asset1, asset2, amount, include_fee) } - fn quote_price_tokens_for_exact_tokens(asset1: Box, asset2: Box, amount: u128, include_fee: bool) -> Option { + + fn quote_price_tokens_for_exact_tokens(asset1: MultiLocation, asset2: MultiLocation, amount: Balance, include_fee: bool) -> Option { AssetConversion::quote_price_tokens_for_exact_tokens(asset1, asset2, amount, include_fee) } - fn get_reserves(asset1: Box, asset2: Box) -> Option<(Balance, Balance)> { + + fn get_reserves(asset1: MultiLocation, asset2: MultiLocation) -> Option<(Balance, Balance)> { AssetConversion::get_reserves(&asset1, &asset2).ok() } } From bbca52d63f72fccbe6d5e72ef7c4a0b5a092128f Mon Sep 17 00:00:00 2001 From: muharem Date: Tue, 24 Oct 2023 13:31:48 +0200 Subject: [PATCH 49/98] adjustments for rococo tests --- .../assets/asset-hub-rococo/src/tests/swap.rs | 55 +++++++++---------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-rococo/src/tests/swap.rs b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-rococo/src/tests/swap.rs index f9da0bf946ed..b16667eaeaec 100644 --- a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-rococo/src/tests/swap.rs +++ b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-rococo/src/tests/swap.rs @@ -14,17 +14,17 @@ // limitations under the License. use crate::*; -use frame_support::{instances::Instance2, BoundedVec}; +use frame_support::instances::Instance2; use parachains_common::rococo::currency::EXISTENTIAL_DEPOSIT; use sp_runtime::{DispatchError, ModuleError}; #[test] fn swap_locally_on_chain_using_local_assets() { - let asset_native = Box::new(asset_hub_rococo_runtime::xcm_config::TokenLocation::get()); - let asset_one = Box::new(MultiLocation { + let asset_native = asset_hub_rococo_runtime::xcm_config::TokenLocation::get(); + let asset_one = MultiLocation { parents: 0, interior: X2(PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into())), - }); + }; AssetHubRococo::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; @@ -52,8 +52,8 @@ fn swap_locally_on_chain_using_local_assets() { assert_ok!(::AssetConversion::create_pool( ::RuntimeOrigin::signed(AssetHubRococoSender::get()), - asset_native.clone(), - asset_one.clone(), + Box::new(asset_native), + Box::new(asset_one), )); assert_expected_events!( @@ -65,8 +65,8 @@ fn swap_locally_on_chain_using_local_assets() { assert_ok!(::AssetConversion::add_liquidity( ::RuntimeOrigin::signed(AssetHubRococoSender::get()), - asset_native.clone(), - asset_one.clone(), + Box::new(asset_native), + Box::new(asset_one), 1_000_000_000_000, 2_000_000_000_000, 0, @@ -81,7 +81,7 @@ fn swap_locally_on_chain_using_local_assets() { ] ); - let path = BoundedVec::<_, _>::truncate_from(vec![asset_native.clone(), asset_one.clone()]); + let path = vec![Box::new(asset_native), Box::new(asset_one)]; assert_ok!( ::AssetConversion::swap_exact_tokens_for_tokens( @@ -106,8 +106,8 @@ fn swap_locally_on_chain_using_local_assets() { assert_ok!(::AssetConversion::remove_liquidity( ::RuntimeOrigin::signed(AssetHubRococoSender::get()), - asset_native, - asset_one, + Box::new(asset_native), + Box::new(asset_one), 1414213562273 - EXISTENTIAL_DEPOSIT * 2, // all but the 2 EDs can't be retrieved. 0, 0, @@ -120,16 +120,16 @@ fn swap_locally_on_chain_using_local_assets() { fn swap_locally_on_chain_using_foreign_assets() { use frame_support::weights::WeightToFee; - let asset_native = Box::new(asset_hub_rococo_runtime::xcm_config::TokenLocation::get()); + let asset_native = asset_hub_rococo_runtime::xcm_config::TokenLocation::get(); - let foreign_asset1_at_asset_hub_rococo = Box::new(MultiLocation { + let foreign_asset1_at_asset_hub_rococo = MultiLocation { parents: 1, interior: X3( Parachain(PenpalRococoA::para_id().into()), PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into()), ), - }); + }; let assets_para_destination: VersionedMultiLocation = MultiLocation { parents: 1, interior: X1(Parachain(AssetHubRococo::para_id().into())) } @@ -175,7 +175,7 @@ fn swap_locally_on_chain_using_foreign_assets() { ::Runtime, Instance2, >::create { - id: *foreign_asset1_at_asset_hub_rococo, + id: foreign_asset1_at_asset_hub_rococo, min_balance: 1000, admin: sov_penpal_on_asset_hub_rococo.clone().into(), }) @@ -223,7 +223,7 @@ fn swap_locally_on_chain_using_foreign_assets() { // Receive XCM message in Assets Parachain AssetHubRococo::execute_with(|| { assert!(::ForeignAssets::asset_exists( - *foreign_asset1_at_asset_hub_rococo + foreign_asset1_at_asset_hub_rococo )); // 3: Mint foreign asset on asset_hub_rococo: @@ -237,7 +237,7 @@ fn swap_locally_on_chain_using_foreign_assets() { ::RuntimeOrigin::signed( sov_penpal_on_asset_hub_rococo.clone().into() ), - *foreign_asset1_at_asset_hub_rococo, + foreign_asset1_at_asset_hub_rococo, sov_penpal_on_asset_hub_rococo.clone().into(), 3_000_000_000_000, )); @@ -252,8 +252,8 @@ fn swap_locally_on_chain_using_foreign_assets() { // 4. Create pool: assert_ok!(::AssetConversion::create_pool( ::RuntimeOrigin::signed(AssetHubRococoSender::get()), - asset_native.clone(), - foreign_asset1_at_asset_hub_rococo.clone(), + Box::new(asset_native), + Box::new(foreign_asset1_at_asset_hub_rococo), )); assert_expected_events!( @@ -268,8 +268,8 @@ fn swap_locally_on_chain_using_foreign_assets() { ::RuntimeOrigin::signed( sov_penpal_on_asset_hub_rococo.clone() ), - asset_native.clone(), - foreign_asset1_at_asset_hub_rococo.clone(), + Box::new(asset_native), + Box::new(foreign_asset1_at_asset_hub_rococo), 1_000_000_000_000, 2_000_000_000_000, 0, @@ -287,10 +287,7 @@ fn swap_locally_on_chain_using_foreign_assets() { ); // 6. Swap! - let path = BoundedVec::<_, _>::truncate_from(vec![ - asset_native.clone(), - foreign_asset1_at_asset_hub_rococo.clone(), - ]); + let path = vec![Box::new(asset_native), Box::new(foreign_asset1_at_asset_hub_rococo)]; assert_ok!( ::AssetConversion::swap_exact_tokens_for_tokens( @@ -318,8 +315,8 @@ fn swap_locally_on_chain_using_foreign_assets() { ::RuntimeOrigin::signed( sov_penpal_on_asset_hub_rococo.clone() ), - asset_native, - foreign_asset1_at_asset_hub_rococo, + Box::new(asset_native), + Box::new(foreign_asset1_at_asset_hub_rococo), 1414213562273 - 2_000_000_000, // all but the 2 EDs can't be retrieved. 0, 0, @@ -330,7 +327,7 @@ fn swap_locally_on_chain_using_foreign_assets() { #[test] fn cannot_create_pool_from_pool_assets() { - let asset_native = Box::new(asset_hub_rococo_runtime::xcm_config::TokenLocation::get()); + let asset_native = asset_hub_rococo_runtime::xcm_config::TokenLocation::get(); let mut asset_one = asset_hub_rococo_runtime::xcm_config::PoolAssetsPalletLocation::get(); asset_one.append_with(GeneralIndex(ASSET_ID.into())).expect("pool assets"); @@ -355,7 +352,7 @@ fn cannot_create_pool_from_pool_assets() { assert_matches::assert_matches!( ::AssetConversion::create_pool( ::RuntimeOrigin::signed(AssetHubRococoSender::get()), - asset_native.clone(), + Box::new(asset_native), Box::new(asset_one), ), Err(DispatchError::Module(ModuleError{index: _, error: _, message})) => assert_eq!(message, Some("UnsupportedAsset")) From 66a66110b7bf80aaa9de2d0b5701040f68caf6d6 Mon Sep 17 00:00:00 2001 From: muharem Date: Tue, 24 Oct 2023 15:13:23 +0200 Subject: [PATCH 50/98] rococo benchmarks --- .../runtimes/assets/asset-hub-rococo/src/xcm_config.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs index ae4a275d43ac..b88de9efb09c 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs @@ -724,8 +724,7 @@ pub struct BenchmarkMultiLocationConverter { } #[cfg(feature = "runtime-benchmarks")] -impl - pallet_asset_conversion::BenchmarkHelper> +impl pallet_asset_conversion::BenchmarkHelper for BenchmarkMultiLocationConverter where SelfParaId: Get, @@ -740,8 +739,8 @@ where ), } } - fn multiasset_id(asset_id: u32) -> sp_std::boxed::Box { - sp_std::boxed::Box::new(Self::asset_id(asset_id)) + fn multiasset_id(asset_id: u32) -> MultiLocation { + Self::asset_id(asset_id) } } From b21d0aa400597f44176a958a0107b0dee50bfcaa Mon Sep 17 00:00:00 2001 From: muharem Date: Wed, 25 Oct 2023 16:24:27 +0200 Subject: [PATCH 51/98] fungible/s union_of --- .../src/traits/tokens/fungible/imbalance.rs | 5 + .../src/traits/tokens/fungible/item_of.rs | 66 +- .../support/src/traits/tokens/fungible/mod.rs | 2 + .../src/traits/tokens/fungible/union_of.rs | 1005 +++++++++++++++++ .../src/traits/tokens/fungibles/imbalance.rs | 5 + .../src/traits/tokens/fungibles/mod.rs | 2 + .../src/traits/tokens/fungibles/union_of.rs | 986 ++++++++++++++++ 7 files changed, 2055 insertions(+), 16 deletions(-) create mode 100644 substrate/frame/support/src/traits/tokens/fungible/union_of.rs create mode 100644 substrate/frame/support/src/traits/tokens/fungibles/union_of.rs diff --git a/substrate/frame/support/src/traits/tokens/fungible/imbalance.rs b/substrate/frame/support/src/traits/tokens/fungible/imbalance.rs index 995797bc8f66..39bcca16553a 100644 --- a/substrate/frame/support/src/traits/tokens/fungible/imbalance.rs +++ b/substrate/frame/support/src/traits/tokens/fungible/imbalance.rs @@ -87,6 +87,11 @@ impl, OppositeOnDrop: HandleImbalance pub(crate) fn new(amount: B) -> Self { Self { amount, _phantom: PhantomData } } + + /// Forget the imbalance without invoking the on-drop handler. + pub(crate) fn forget(imbalance: Self) { + sp_std::mem::forget(imbalance); + } } impl, OppositeOnDrop: HandleImbalanceDrop> diff --git a/substrate/frame/support/src/traits/tokens/fungible/item_of.rs b/substrate/frame/support/src/traits/tokens/fungible/item_of.rs index a47998eb134e..3e054472a2f9 100644 --- a/substrate/frame/support/src/traits/tokens/fungible/item_of.rs +++ b/substrate/frame/support/src/traits/tokens/fungible/item_of.rs @@ -380,36 +380,64 @@ impl< value: Self::Balance, precision: Precision, ) -> Result, DispatchError> { - >::deposit(A::get(), who, value, precision) - .map(|debt| Imbalance::new(debt.peek())) + >::deposit(A::get(), who, value, precision).map( + |inner_debt| { + let debt = Imbalance::new(inner_debt.peek()); + fungibles::Imbalance::forget(inner_debt); + debt + }, + ) } fn issue(amount: Self::Balance) -> Credit { - Imbalance::new(>::issue(A::get(), amount).peek()) + let inner_credit = >::issue(A::get(), amount); + let credit = Imbalance::new(inner_credit.peek()); + fungibles::Imbalance::forget(inner_credit); + credit } fn pair(amount: Self::Balance) -> (Debt, Credit) { - let (a, b) = >::pair(A::get(), amount); - (Imbalance::new(a.peek()), Imbalance::new(b.peek())) + let (inner_a, inner_b) = >::pair(A::get(), amount); + let (a, b) = (Imbalance::new(inner_a.peek()), Imbalance::new(inner_b.peek())); + fungibles::Imbalance::forget(inner_a); + fungibles::Imbalance::forget(inner_b); + (a, b) } fn rescind(amount: Self::Balance) -> Debt { - Imbalance::new(>::rescind(A::get(), amount).peek()) + let inner_debt = >::rescind(A::get(), amount); + let debt = Imbalance::new(inner_debt.peek()); + fungibles::Imbalance::forget(inner_debt); + debt } fn resolve( who: &AccountId, credit: Credit, ) -> Result<(), Credit> { - let credit = fungibles::Imbalance::new(A::get(), credit.peek()); - >::resolve(who, credit) - .map_err(|credit| Imbalance::new(credit.peek())) + let inner = fungibles::Imbalance::new(A::get(), credit.peek()); + Imbalance::forget(credit); + >::resolve(who, inner).map_err(|inner| { + let credit = Imbalance::new(inner.peek()); + fungibles::Imbalance::forget(inner); + credit + }) } fn settle( who: &AccountId, debt: Debt, preservation: Preservation, ) -> Result, Debt> { - let debt = fungibles::Imbalance::new(A::get(), debt.peek()); - >::settle(who, debt, preservation) - .map(|credit| Imbalance::new(credit.peek())) - .map_err(|debt| Imbalance::new(debt.peek())) + let inner_debt = fungibles::Imbalance::new(A::get(), debt.peek()); + Imbalance::forget(debt); + match >::settle(who, inner_debt, preservation) { + Ok(inner_credit) => { + let credit = Imbalance::new(inner_credit.peek()); + fungibles::Imbalance::forget(inner_credit); + Ok(credit) + }, + Err(inner_debt) => { + let debt = Imbalance::new(inner_debt.peek()); + fungibles::Imbalance::forget(inner_debt); + Err(debt) + }, + } } fn withdraw( who: &AccountId, @@ -426,7 +454,11 @@ impl< preservation, force, ) - .map(|credit| Imbalance::new(credit.peek())) + .map(|inner_credit| { + let credit = Imbalance::new(inner_credit.peek()); + fungibles::Imbalance::forget(inner_credit); + credit + }) } } @@ -441,9 +473,11 @@ impl< who: &AccountId, amount: Self::Balance, ) -> (Credit, Self::Balance) { - let (credit, amount) = + let (inner_credit, amount) = >::slash(A::get(), reason, who, amount); - (Imbalance::new(credit.peek()), amount) + let credit = Imbalance::new(inner_credit.peek()); + fungibles::Imbalance::forget(inner_credit); + (credit, amount) } } diff --git a/substrate/frame/support/src/traits/tokens/fungible/mod.rs b/substrate/frame/support/src/traits/tokens/fungible/mod.rs index 61b75fd6563c..f8f378a44941 100644 --- a/substrate/frame/support/src/traits/tokens/fungible/mod.rs +++ b/substrate/frame/support/src/traits/tokens/fungible/mod.rs @@ -44,6 +44,7 @@ pub mod hold; mod imbalance; mod item_of; mod regular; +mod union_of; use codec::{Decode, Encode, MaxEncodedLen}; use frame_support_procedural::{CloneNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound}; @@ -67,6 +68,7 @@ pub use regular::{ use sp_arithmetic::traits::Zero; use sp_core::Get; use sp_runtime::{traits::Convert, DispatchError}; +pub use union_of::{NativeFromLeft, NativeOrWithId, UnionOf}; use crate::{ ensure, diff --git a/substrate/frame/support/src/traits/tokens/fungible/union_of.rs b/substrate/frame/support/src/traits/tokens/fungible/union_of.rs new file mode 100644 index 000000000000..0ba8cc28d83c --- /dev/null +++ b/substrate/frame/support/src/traits/tokens/fungible/union_of.rs @@ -0,0 +1,1005 @@ +// This file is part of Substrate. + +// Copyright (Criterion) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Types to combine some `fungible::*` and `fungibles::*` implementations into one union +//! `fungibles::*` implementation. + +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::traits::{ + tokens::{ + fungible, fungibles, AssetId, DepositConsequence, Fortitude, Imbalance as ImbalanceT, + Precision, Preservation, Provenance, Restriction, WithdrawConsequence, + }, + AccountTouch, ContainsPair, +}; +use scale_info::TypeInfo; +use sp_runtime::{ + traits::Convert, + DispatchError, DispatchResult, Either, + Either::{Left, Right}, +}; +use sp_std::cmp::Ordering; + +/// The `NativeOrWithId` enum classifies an asset as either `Native`` to the current chain or as an +/// asset with a specific ID. +#[derive(Decode, Encode, Default, MaxEncodedLen, TypeInfo, Clone, Debug)] +pub enum NativeOrWithId +where + AssetId: Ord, +{ + /// Represents the native asset of the current chain. + /// + /// E.g., DOT for the Polkadot Asset Hub. + #[default] + Native, + /// Represents an asset identified by its underlying `AssetId`. + WithId(AssetId), +} +impl From for NativeOrWithId { + fn from(asset: AssetId) -> Self { + Self::WithId(asset) + } +} +impl Ord for NativeOrWithId { + fn cmp(&self, other: &Self) -> Ordering { + match (self, other) { + (Self::Native, Self::Native) => Ordering::Equal, + (Self::Native, Self::WithId(_)) => Ordering::Less, + (Self::WithId(_), Self::Native) => Ordering::Greater, + (Self::WithId(id1), Self::WithId(id2)) => ::cmp(id1, id2), + } + } +} +impl PartialOrd for NativeOrWithId { + fn partial_cmp(&self, other: &Self) -> Option { + Some(::cmp(self, other)) + } +} +impl PartialEq for NativeOrWithId { + fn eq(&self, other: &Self) -> bool { + self.cmp(other) == Ordering::Equal + } +} +impl Eq for NativeOrWithId {} + +/// Criterion for [`UnionOf`] where a set for [`NativeOrWithId::Native`] asset located from the left +/// and for [`NativeOrWithId::WithId`] from the right. +pub struct NativeFromLeft; +impl Convert, Either<(), AssetId>> for NativeFromLeft { + fn convert(asset: NativeOrWithId) -> Either<(), AssetId> { + match asset { + NativeOrWithId::Native => Either::Left(()), + NativeOrWithId::WithId(id) => Either::Right(id), + } + } +} + +/// Type to combine some `fungible::*` and `fungibles::*` implementations into one union +/// `fungibles::*` implementation. +/// +/// ### Generic Parameters: +/// - `Left` is `fungible::*` implementation that is incorporated into the resulting union. +/// - `Right` is `fungibles::*` implementation that is incorporated into the resulting union. +/// - `Criterion` determines whether the `AssetKind` belongs to the `Left` or `Right` set. +/// - `AssetKind` is a superset type encompassing asset kinds from `Left`` and `Right` sets. +/// - `AccountId` is an account identifier type. +pub struct UnionOf( + sp_std::marker::PhantomData<(Left, Right, Criterion, AssetKind, AccountId)>, +); + +impl< + Left: fungible::Inspect, + Right: fungibles::Inspect, + Criterion: Convert>, + AssetKind: AssetId, + AccountId, + > fungibles::Inspect for UnionOf +{ + type AssetId = AssetKind; + type Balance = Left::Balance; + + fn total_issuance(asset: Self::AssetId) -> Self::Balance { + match Criterion::convert(asset) { + Left(()) => >::total_issuance(), + Right(a) => >::total_issuance(a), + } + } + fn active_issuance(asset: Self::AssetId) -> Self::Balance { + match Criterion::convert(asset) { + Left(()) => >::active_issuance(), + Right(a) => >::active_issuance(a), + } + } + fn minimum_balance(asset: Self::AssetId) -> Self::Balance { + match Criterion::convert(asset) { + Left(()) => >::minimum_balance(), + Right(a) => >::minimum_balance(a), + } + } + fn balance(asset: Self::AssetId, who: &AccountId) -> Self::Balance { + match Criterion::convert(asset) { + Left(()) => >::balance(who), + Right(a) => >::balance(a, who), + } + } + fn total_balance(asset: Self::AssetId, who: &AccountId) -> Self::Balance { + match Criterion::convert(asset) { + Left(()) => >::total_balance(who), + Right(a) => >::total_balance(a, who), + } + } + fn reducible_balance( + asset: Self::AssetId, + who: &AccountId, + preservation: Preservation, + force: Fortitude, + ) -> Self::Balance { + match Criterion::convert(asset) { + Left(()) => + >::reducible_balance(who, preservation, force), + Right(a) => >::reducible_balance( + a, + who, + preservation, + force, + ), + } + } + fn can_deposit( + asset: Self::AssetId, + who: &AccountId, + amount: Self::Balance, + provenance: Provenance, + ) -> DepositConsequence { + match Criterion::convert(asset) { + Left(()) => + >::can_deposit(who, amount, provenance), + Right(a) => + >::can_deposit(a, who, amount, provenance), + } + } + fn can_withdraw( + asset: Self::AssetId, + who: &AccountId, + amount: Self::Balance, + ) -> WithdrawConsequence { + match Criterion::convert(asset) { + Left(()) => >::can_withdraw(who, amount), + Right(a) => >::can_withdraw(a, who, amount), + } + } + fn asset_exists(asset: Self::AssetId) -> bool { + match Criterion::convert(asset) { + Left(()) => true, + Right(a) => >::asset_exists(a), + } + } +} + +impl< + Left: fungible::InspectHold, + Right: fungibles::InspectHold, + Criterion: Convert>, + AssetKind: AssetId, + AccountId, + > fungibles::InspectHold for UnionOf +{ + type Reason = Left::Reason; + + fn reducible_total_balance_on_hold( + asset: Self::AssetId, + who: &AccountId, + force: Fortitude, + ) -> Self::Balance { + match Criterion::convert(asset) { + Left(()) => + >::reducible_total_balance_on_hold( + who, force, + ), + Right(a) => + >::reducible_total_balance_on_hold( + a, who, force, + ), + } + } + fn hold_available(asset: Self::AssetId, reason: &Self::Reason, who: &AccountId) -> bool { + match Criterion::convert(asset) { + Left(()) => >::hold_available(reason, who), + Right(a) => + >::hold_available(a, reason, who), + } + } + fn total_balance_on_hold(asset: Self::AssetId, who: &AccountId) -> Self::Balance { + match Criterion::convert(asset) { + Left(()) => >::total_balance_on_hold(who), + Right(a) => >::total_balance_on_hold(a, who), + } + } + fn balance_on_hold( + asset: Self::AssetId, + reason: &Self::Reason, + who: &AccountId, + ) -> Self::Balance { + match Criterion::convert(asset) { + Left(()) => >::balance_on_hold(reason, who), + Right(a) => + >::balance_on_hold(a, reason, who), + } + } + fn can_hold( + asset: Self::AssetId, + reason: &Self::Reason, + who: &AccountId, + amount: Self::Balance, + ) -> bool { + match Criterion::convert(asset) { + Left(()) => >::can_hold(reason, who, amount), + Right(a) => + >::can_hold(a, reason, who, amount), + } + } +} + +impl< + Left: fungible::InspectFreeze, + Right: fungibles::InspectFreeze, + Criterion: Convert>, + AssetKind: AssetId, + AccountId, + > fungibles::InspectFreeze for UnionOf +{ + type Id = Left::Id; + fn balance_frozen(asset: Self::AssetId, id: &Self::Id, who: &AccountId) -> Self::Balance { + match Criterion::convert(asset) { + Left(()) => >::balance_frozen(id, who), + Right(a) => >::balance_frozen(a, id, who), + } + } + fn balance_freezable(asset: Self::AssetId, who: &AccountId) -> Self::Balance { + match Criterion::convert(asset) { + Left(()) => >::balance_freezable(who), + Right(a) => >::balance_freezable(a, who), + } + } + fn can_freeze(asset: Self::AssetId, id: &Self::Id, who: &AccountId) -> bool { + match Criterion::convert(asset) { + Left(()) => >::can_freeze(id, who), + Right(a) => >::can_freeze(a, id, who), + } + } +} + +impl< + Left: fungible::Unbalanced, + Right: fungibles::Unbalanced, + Criterion: Convert>, + AssetKind: AssetId, + AccountId, + > fungibles::Unbalanced for UnionOf +{ + fn handle_dust(dust: fungibles::Dust) + where + Self: Sized, + { + match Criterion::convert(dust.0) { + Left(()) => + >::handle_dust(fungible::Dust(dust.1)), + Right(a) => + >::handle_dust(fungibles::Dust(a, dust.1)), + } + } + fn write_balance( + asset: Self::AssetId, + who: &AccountId, + amount: Self::Balance, + ) -> Result, DispatchError> { + match Criterion::convert(asset) { + Left(()) => >::write_balance(who, amount), + Right(a) => >::write_balance(a, who, amount), + } + } + fn set_total_issuance(asset: Self::AssetId, amount: Self::Balance) -> () { + match Criterion::convert(asset) { + Left(()) => >::set_total_issuance(amount), + Right(a) => >::set_total_issuance(a, amount), + } + } + fn decrease_balance( + asset: Self::AssetId, + who: &AccountId, + amount: Self::Balance, + precision: Precision, + preservation: Preservation, + force: Fortitude, + ) -> Result { + match Criterion::convert(asset) { + Left(()) => >::decrease_balance( + who, + amount, + precision, + preservation, + force, + ), + Right(a) => >::decrease_balance( + a, + who, + amount, + precision, + preservation, + force, + ), + } + } + fn increase_balance( + asset: Self::AssetId, + who: &AccountId, + amount: Self::Balance, + precision: Precision, + ) -> Result { + match Criterion::convert(asset) { + Left(()) => + >::increase_balance(who, amount, precision), + Right(a) => >::increase_balance( + a, who, amount, precision, + ), + } + } +} + +impl< + Left: fungible::UnbalancedHold, + Right: fungibles::UnbalancedHold, + Criterion: Convert>, + AssetKind: AssetId, + AccountId, + > fungibles::UnbalancedHold for UnionOf +{ + fn set_balance_on_hold( + asset: Self::AssetId, + reason: &Self::Reason, + who: &AccountId, + amount: Self::Balance, + ) -> DispatchResult { + match Criterion::convert(asset) { + Left(()) => >::set_balance_on_hold( + reason, who, amount, + ), + Right(a) => >::set_balance_on_hold( + a, reason, who, amount, + ), + } + } + fn decrease_balance_on_hold( + asset: Self::AssetId, + reason: &Self::Reason, + who: &AccountId, + amount: Self::Balance, + precision: Precision, + ) -> Result { + match Criterion::convert(asset) { + Left(()) => >::decrease_balance_on_hold( + reason, who, amount, precision, + ), + Right(a) => >::decrease_balance_on_hold( + a, reason, who, amount, precision, + ), + } + } + fn increase_balance_on_hold( + asset: Self::AssetId, + reason: &Self::Reason, + who: &AccountId, + amount: Self::Balance, + precision: Precision, + ) -> Result { + match Criterion::convert(asset) { + Left(()) => >::decrease_balance_on_hold( + reason, who, amount, precision, + ), + Right(a) => >::decrease_balance_on_hold( + a, reason, who, amount, precision, + ), + } + } +} + +impl< + Left: fungible::Mutate, + Right: fungibles::Mutate, + Criterion: Convert>, + AssetKind: AssetId, + AccountId, + > fungibles::Mutate for UnionOf +{ + fn mint_into( + asset: Self::AssetId, + who: &AccountId, + amount: Self::Balance, + ) -> Result { + match Criterion::convert(asset) { + Left(()) => >::mint_into(who, amount), + Right(a) => >::mint_into(a, who, amount), + } + } + fn burn_from( + asset: Self::AssetId, + who: &AccountId, + amount: Self::Balance, + precision: Precision, + force: Fortitude, + ) -> Result { + match Criterion::convert(asset) { + Left(()) => + >::burn_from(who, amount, precision, force), + Right(a) => + >::burn_from(a, who, amount, precision, force), + } + } + fn shelve( + asset: Self::AssetId, + who: &AccountId, + amount: Self::Balance, + ) -> Result { + match Criterion::convert(asset) { + Left(()) => >::shelve(who, amount), + Right(a) => >::shelve(a, who, amount), + } + } + fn restore( + asset: Self::AssetId, + who: &AccountId, + amount: Self::Balance, + ) -> Result { + match Criterion::convert(asset) { + Left(()) => >::restore(who, amount), + Right(a) => >::restore(a, who, amount), + } + } + fn transfer( + asset: Self::AssetId, + source: &AccountId, + dest: &AccountId, + amount: Self::Balance, + preservation: Preservation, + ) -> Result { + match Criterion::convert(asset) { + Left(()) => + >::transfer(source, dest, amount, preservation), + Right(a) => >::transfer( + a, + source, + dest, + amount, + preservation, + ), + } + } + + fn set_balance(asset: Self::AssetId, who: &AccountId, amount: Self::Balance) -> Self::Balance { + match Criterion::convert(asset) { + Left(()) => >::set_balance(who, amount), + Right(a) => >::set_balance(a, who, amount), + } + } +} + +impl< + Left: fungible::MutateHold, + Right: fungibles::MutateHold, + Criterion: Convert>, + AssetKind: AssetId, + AccountId, + > fungibles::MutateHold for UnionOf +{ + fn hold( + asset: Self::AssetId, + reason: &Self::Reason, + who: &AccountId, + amount: Self::Balance, + ) -> DispatchResult { + match Criterion::convert(asset) { + Left(()) => >::hold(reason, who, amount), + Right(a) => >::hold(a, reason, who, amount), + } + } + fn release( + asset: Self::AssetId, + reason: &Self::Reason, + who: &AccountId, + amount: Self::Balance, + precision: Precision, + ) -> Result { + match Criterion::convert(asset) { + Left(()) => + >::release(reason, who, amount, precision), + Right(a) => >::release( + a, reason, who, amount, precision, + ), + } + } + fn burn_held( + asset: Self::AssetId, + reason: &Self::Reason, + who: &AccountId, + amount: Self::Balance, + precision: Precision, + force: Fortitude, + ) -> Result { + match Criterion::convert(asset) { + Left(()) => >::burn_held( + reason, who, amount, precision, force, + ), + Right(a) => >::burn_held( + a, reason, who, amount, precision, force, + ), + } + } + fn transfer_on_hold( + asset: Self::AssetId, + reason: &Self::Reason, + source: &AccountId, + dest: &AccountId, + amount: Self::Balance, + precision: Precision, + mode: Restriction, + force: Fortitude, + ) -> Result { + match Criterion::convert(asset) { + Left(()) => >::transfer_on_hold( + reason, source, dest, amount, precision, mode, force, + ), + Right(a) => >::transfer_on_hold( + a, reason, source, dest, amount, precision, mode, force, + ), + } + } + fn transfer_and_hold( + asset: Self::AssetId, + reason: &Self::Reason, + source: &AccountId, + dest: &AccountId, + amount: Self::Balance, + precision: Precision, + preservation: Preservation, + force: Fortitude, + ) -> Result { + match Criterion::convert(asset) { + Left(()) => >::transfer_and_hold( + reason, + source, + dest, + amount, + precision, + preservation, + force, + ), + Right(a) => >::transfer_and_hold( + a, + reason, + source, + dest, + amount, + precision, + preservation, + force, + ), + } + } +} + +impl< + Left: fungible::MutateFreeze, + Right: fungibles::MutateFreeze, + Criterion: Convert>, + AssetKind: AssetId, + AccountId, + > fungibles::MutateFreeze for UnionOf +{ + fn set_freeze( + asset: Self::AssetId, + id: &Self::Id, + who: &AccountId, + amount: Self::Balance, + ) -> DispatchResult { + match Criterion::convert(asset) { + Left(()) => >::set_freeze(id, who, amount), + Right(a) => + >::set_freeze(a, id, who, amount), + } + } + fn extend_freeze( + asset: Self::AssetId, + id: &Self::Id, + who: &AccountId, + amount: Self::Balance, + ) -> DispatchResult { + match Criterion::convert(asset) { + Left(()) => >::extend_freeze(id, who, amount), + Right(a) => + >::extend_freeze(a, id, who, amount), + } + } + fn thaw(asset: Self::AssetId, id: &Self::Id, who: &AccountId) -> DispatchResult { + match Criterion::convert(asset) { + Left(()) => >::thaw(id, who), + Right(a) => >::thaw(a, id, who), + } + } +} + +pub struct ConvertImbalanceDropHandler< + Left, + Right, + Criterion, + AssetKind, + Balance, + AssetId, + AccountId, +>(sp_std::marker::PhantomData<(Left, Right, Criterion, AssetKind, Balance, AssetId, AccountId)>); + +impl< + Left: fungible::HandleImbalanceDrop, + Right: fungibles::HandleImbalanceDrop, + Criterion: Convert>, + AssetKind, + Balance, + AssetId, + AccountId, + > fungibles::HandleImbalanceDrop + for ConvertImbalanceDropHandler +{ + fn handle(asset: AssetKind, amount: Balance) { + match Criterion::convert(asset) { + Left(()) => Left::handle(amount), + Right(a) => Right::handle(a, amount), + } + } +} + +impl< + Left: fungible::Balanced, + Right: fungibles::Balanced, + Criterion: Convert>, + AssetKind: AssetId, + AccountId, + > fungibles::Balanced for UnionOf +{ + type OnDropDebt = ConvertImbalanceDropHandler< + Left::OnDropDebt, + Right::OnDropDebt, + Criterion, + AssetKind, + Left::Balance, + Right::AssetId, + AccountId, + >; + type OnDropCredit = ConvertImbalanceDropHandler< + Left::OnDropCredit, + Right::OnDropCredit, + Criterion, + AssetKind, + Left::Balance, + Right::AssetId, + AccountId, + >; + + fn deposit( + asset: Self::AssetId, + who: &AccountId, + value: Self::Balance, + precision: Precision, + ) -> Result, DispatchError> { + match Criterion::convert(asset.clone()) { + Left(()) => >::deposit(who, value, precision) + .map(|inner_debt| { + let debt = fungibles::Imbalance::new(asset, inner_debt.peek()); + fungible::Imbalance::forget(inner_debt); + debt + }), + Right(a) => >::deposit( + a, who, value, precision, + ) + .map(|inner_debt| { + let debt = fungibles::Imbalance::new(asset, inner_debt.peek()); + fungibles::Imbalance::forget(inner_debt); + debt + }), + } + } + fn issue(asset: Self::AssetId, amount: Self::Balance) -> fungibles::Credit { + match Criterion::convert(asset.clone()) { + Left(()) => { + let inner_credit = >::issue(amount); + let credit = fungibles::Imbalance::new(asset, inner_credit.peek()); + fungible::Imbalance::forget(inner_credit); + credit + }, + Right(a) => { + let inner_credit = >::issue(a, amount); + let credit = fungibles::Imbalance::new(asset, inner_credit.peek()); + fungibles::Imbalance::forget(inner_credit); + credit + }, + } + } + fn pair( + asset: Self::AssetId, + amount: Self::Balance, + ) -> (fungibles::Debt, fungibles::Credit) { + match Criterion::convert(asset.clone()) { + Left(()) => { + let (inner_a, inner_b) = >::pair(amount); + let (a, b) = ( + fungibles::Imbalance::new(asset.clone(), inner_a.peek()), + fungibles::Imbalance::new(asset, inner_b.peek()), + ); + fungible::Imbalance::forget(inner_a); + fungible::Imbalance::forget(inner_b); + (a, b) + }, + Right(a) => { + let (inner_a, inner_b) = >::pair(a, amount); + let (a, b) = ( + fungibles::Imbalance::new(asset.clone(), inner_a.peek()), + fungibles::Imbalance::new(asset, inner_b.peek()), + ); + fungibles::Imbalance::forget(inner_a); + fungibles::Imbalance::forget(inner_b); + (a, b) + }, + } + } + fn rescind(asset: Self::AssetId, amount: Self::Balance) -> fungibles::Debt { + match Criterion::convert(asset.clone()) { + Left(()) => { + let inner_debt = >::rescind(amount); + let debt = fungibles::Imbalance::new(asset, inner_debt.peek()); + fungible::Imbalance::forget(inner_debt); + debt + }, + Right(a) => { + let inner_debt = >::rescind(a, amount); + let debt = fungibles::Imbalance::new(asset, inner_debt.peek()); + fungibles::Imbalance::forget(inner_debt); + debt + }, + } + } + fn resolve( + who: &AccountId, + credit: fungibles::Credit, + ) -> Result<(), fungibles::Credit> { + let asset = credit.asset(); + match Criterion::convert(asset.clone()) { + Left(()) => { + let inner_credit = fungible::Imbalance::new(credit.peek()); + fungibles::Imbalance::forget(credit); + >::resolve(who, inner_credit).map_err( + |inner_credit| { + let credit = fungibles::Imbalance::new(asset, inner_credit.peek()); + fungible::Imbalance::forget(inner_credit); + credit + }, + ) + }, + Right(a) => { + let inner_credit = fungibles::Imbalance::new(a, credit.peek()); + fungibles::Imbalance::forget(credit); + >::resolve(who, inner_credit).map_err( + |inner_credit| { + let credit = fungibles::Imbalance::new(asset, inner_credit.peek()); + fungibles::Imbalance::forget(inner_credit); + credit + }, + ) + }, + } + } + fn settle( + who: &AccountId, + debt: fungibles::Debt, + preservation: Preservation, + ) -> Result, fungibles::Debt> { + let asset = debt.asset(); + match Criterion::convert(asset.clone()) { + Left(()) => { + let inner_debt = fungible::Imbalance::new(debt.peek()); + fungibles::Imbalance::forget(debt); + match >::settle(who, inner_debt, preservation) + { + Ok(inner_credit) => { + let credit = fungibles::Imbalance::new(asset, inner_credit.peek()); + fungible::Imbalance::forget(inner_credit); + Ok(credit) + }, + Err(inner_debt) => { + let debt = fungibles::Imbalance::new(asset, inner_debt.peek()); + fungible::Imbalance::forget(inner_debt); + Err(debt) + }, + } + }, + Right(a) => { + let inner_debt = fungibles::Imbalance::new(a, debt.peek()); + fungibles::Imbalance::forget(debt); + match >::settle( + who, + inner_debt, + preservation, + ) { + Ok(inner_credit) => { + let credit = fungibles::Imbalance::new(asset, inner_credit.peek()); + fungibles::Imbalance::forget(inner_credit); + Ok(credit) + }, + Err(inner_debt) => { + let debt = fungibles::Imbalance::new(asset, inner_debt.peek()); + fungibles::Imbalance::forget(inner_debt); + Err(debt) + }, + } + }, + } + } + fn withdraw( + asset: Self::AssetId, + who: &AccountId, + value: Self::Balance, + precision: Precision, + preservation: Preservation, + force: Fortitude, + ) -> Result, DispatchError> { + match Criterion::convert(asset.clone()) { + Left(()) => >::withdraw( + who, + value, + precision, + preservation, + force, + ) + .map(|inner_credit| { + let credit = fungibles::Imbalance::new(asset, inner_credit.peek()); + fungible::Imbalance::forget(inner_credit); + credit + }), + Right(a) => >::withdraw( + a, + who, + value, + precision, + preservation, + force, + ) + .map(|inner_credit| { + let credit = fungibles::Imbalance::new(asset, inner_credit.peek()); + fungibles::Imbalance::forget(inner_credit); + credit + }), + } + } +} + +impl< + Left: fungible::BalancedHold, + Right: fungibles::BalancedHold, + Criterion: Convert>, + AssetKind: AssetId, + AccountId, + > fungibles::BalancedHold for UnionOf +{ + fn slash( + asset: Self::AssetId, + reason: &Self::Reason, + who: &AccountId, + amount: Self::Balance, + ) -> (fungibles::Credit, Self::Balance) { + match Criterion::convert(asset.clone()) { + Left(()) => { + let (inner_credit, amount) = + >::slash(reason, who, amount); + let credit = fungibles::Imbalance::new(asset, inner_credit.peek()); + fungible::Imbalance::forget(inner_credit); + (credit, amount) + }, + Right(a) => { + let (inner_credit, amount) = + >::slash(a, reason, who, amount); + let credit = fungibles::Imbalance::new(asset, inner_credit.peek()); + fungibles::Imbalance::forget(inner_credit); + (credit, amount) + }, + } + } +} + +impl< + Left: fungible::Inspect, + Right: fungibles::Inspect + fungibles::Create, + Criterion: Convert>, + AssetKind: AssetId, + AccountId, + > fungibles::Create for UnionOf +{ + fn create( + asset: AssetKind, + admin: AccountId, + is_sufficient: bool, + min_balance: Self::Balance, + ) -> DispatchResult { + match Criterion::convert(asset) { + // no-op for `Left` since `Create` trait is not defined within `fungible::*`. + Left(()) => Ok(()), + Right(a) => >::create( + a, + admin, + is_sufficient, + min_balance, + ), + } + } +} + +impl< + Left: fungible::Inspect, + Right: fungibles::Inspect + + AccountTouch< + Right::AssetId, + AccountId, + Balance = >::Balance, + >, + Criterion: Convert>, + AssetKind: AssetId, + AccountId, + > AccountTouch for UnionOf +{ + type Balance = >::Balance; + + fn deposit_required(asset: AssetKind) -> Self::Balance { + match Criterion::convert(asset) { + Left(()) => >::minimum_balance(), + Right(a) => >::deposit_required(a), + } + } + + fn touch(asset: AssetKind, who: AccountId, depositor: AccountId) -> DispatchResult { + match Criterion::convert(asset) { + // no-op for `Left` since `AccountTouch` trait is not defined within `fungible::*`. + Left(()) => Ok(()), + Right(a) => + >::touch(a, who, depositor), + } + } +} + +impl< + Left: fungible::Inspect, + Right: fungibles::Inspect + + ContainsPair, + Criterion: Convert>, + AssetKind: AssetId, + AccountId, + > ContainsPair for UnionOf +{ + fn contains(asset: &AssetKind, who: &AccountId) -> bool { + match Criterion::convert(asset.clone()) { + Left(()) => + >::total_balance(who) >= + >::minimum_balance(), + Right(a) => >::contains(&a, who), + } + } +} diff --git a/substrate/frame/support/src/traits/tokens/fungibles/imbalance.rs b/substrate/frame/support/src/traits/tokens/fungibles/imbalance.rs index 7c0d7721a2e6..2c43a0c9c7e2 100644 --- a/substrate/frame/support/src/traits/tokens/fungibles/imbalance.rs +++ b/substrate/frame/support/src/traits/tokens/fungibles/imbalance.rs @@ -93,6 +93,11 @@ impl< Self { asset, amount, _phantom: PhantomData } } + /// Forget the imbalance without invoking the on-drop handler. + pub(crate) fn forget(imbalance: Self) { + sp_std::mem::forget(imbalance); + } + pub fn drop_zero(self) -> Result<(), Self> { if self.amount.is_zero() { sp_std::mem::forget(self); diff --git a/substrate/frame/support/src/traits/tokens/fungibles/mod.rs b/substrate/frame/support/src/traits/tokens/fungibles/mod.rs index 4fd6ef43a15f..a7b7933aaf6d 100644 --- a/substrate/frame/support/src/traits/tokens/fungibles/mod.rs +++ b/substrate/frame/support/src/traits/tokens/fungibles/mod.rs @@ -26,6 +26,7 @@ mod lifetime; pub mod metadata; mod regular; pub mod roles; +mod union_of; pub use enumerable::Inspect as InspectEnumerable; pub use freeze::{Inspect as InspectFreeze, Mutate as MutateFreeze}; @@ -38,3 +39,4 @@ pub use lifetime::{Create, Destroy}; pub use regular::{ Balanced, DecreaseIssuance, Dust, IncreaseIssuance, Inspect, Mutate, Unbalanced, }; +pub use union_of::UnionOf; diff --git a/substrate/frame/support/src/traits/tokens/fungibles/union_of.rs b/substrate/frame/support/src/traits/tokens/fungibles/union_of.rs new file mode 100644 index 000000000000..eb6927553d49 --- /dev/null +++ b/substrate/frame/support/src/traits/tokens/fungibles/union_of.rs @@ -0,0 +1,986 @@ +// This file is part of Substrate. + +// Copyright (Criterion) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Type to combine two `fungibles::*` implementations into one union `fungibles::*` implementation. + +use frame_support::traits::{ + tokens::{ + fungibles, AssetId, DepositConsequence, Fortitude, Precision, Preservation, Provenance, + Restriction, WithdrawConsequence, + }, + AccountTouch, ContainsPair, +}; +use sp_runtime::{ + traits::Convert, + DispatchError, DispatchResult, Either, + Either::{Left, Right}, +}; + +/// Type to combine two `fungibles::*` implementations into one union `fungibles::*` implementation. +/// +/// ### Generic Parameters: +/// - `Left` is `fungibles::*` implementation that is incorporated into the resulting union. +/// - `Right` is `fungibles::*` implementation that is incorporated into the resulting union. +/// - `Criterion` determines whether the `AssetKind` belongs to the `Left` or `Right` set. +/// - `AssetKind` is a superset type encompassing asset kinds from `Left`` and `Right` sets. +/// - `AccountId` is an account identifier type. +pub struct UnionOf( + sp_std::marker::PhantomData<(Left, Right, Criterion, AssetKind, AccountId)>, +); + +impl< + Left: fungibles::Inspect, + Right: fungibles::Inspect, + Criterion: Convert>, + AssetKind: AssetId, + AccountId, + > fungibles::Inspect for UnionOf +{ + type AssetId = AssetKind; + type Balance = Left::Balance; + + fn total_issuance(asset: Self::AssetId) -> Self::Balance { + match Criterion::convert(asset) { + Left(a) => >::total_issuance(a), + Right(a) => >::total_issuance(a), + } + } + fn active_issuance(asset: Self::AssetId) -> Self::Balance { + match Criterion::convert(asset) { + Left(a) => >::active_issuance(a), + Right(a) => >::active_issuance(a), + } + } + fn minimum_balance(asset: Self::AssetId) -> Self::Balance { + match Criterion::convert(asset) { + Left(a) => >::minimum_balance(a), + Right(a) => >::minimum_balance(a), + } + } + fn balance(asset: Self::AssetId, who: &AccountId) -> Self::Balance { + match Criterion::convert(asset) { + Left(a) => >::balance(a, who), + Right(a) => >::balance(a, who), + } + } + fn total_balance(asset: Self::AssetId, who: &AccountId) -> Self::Balance { + match Criterion::convert(asset) { + Left(a) => >::total_balance(a, who), + Right(a) => >::total_balance(a, who), + } + } + fn reducible_balance( + asset: Self::AssetId, + who: &AccountId, + preservation: Preservation, + force: Fortitude, + ) -> Self::Balance { + match Criterion::convert(asset) { + Left(a) => >::reducible_balance( + a, + who, + preservation, + force, + ), + Right(a) => >::reducible_balance( + a, + who, + preservation, + force, + ), + } + } + fn can_deposit( + asset: Self::AssetId, + who: &AccountId, + amount: Self::Balance, + provenance: Provenance, + ) -> DepositConsequence { + match Criterion::convert(asset) { + Left(a) => + >::can_deposit(a, who, amount, provenance), + Right(a) => + >::can_deposit(a, who, amount, provenance), + } + } + fn can_withdraw( + asset: Self::AssetId, + who: &AccountId, + amount: Self::Balance, + ) -> WithdrawConsequence { + match Criterion::convert(asset) { + Left(a) => >::can_withdraw(a, who, amount), + Right(a) => >::can_withdraw(a, who, amount), + } + } + fn asset_exists(asset: Self::AssetId) -> bool { + match Criterion::convert(asset) { + Left(a) => >::asset_exists(a), + Right(a) => >::asset_exists(a), + } + } +} + +impl< + Left: fungibles::InspectHold, + Right: fungibles::InspectHold, + Criterion: Convert>, + AssetKind: AssetId, + AccountId, + > fungibles::InspectHold for UnionOf +{ + type Reason = Left::Reason; + + fn reducible_total_balance_on_hold( + asset: Self::AssetId, + who: &AccountId, + force: Fortitude, + ) -> Self::Balance { + match Criterion::convert(asset) { + Left(a) => + >::reducible_total_balance_on_hold( + a, who, force, + ), + Right(a) => + >::reducible_total_balance_on_hold( + a, who, force, + ), + } + } + fn hold_available(asset: Self::AssetId, reason: &Self::Reason, who: &AccountId) -> bool { + match Criterion::convert(asset) { + Left(a) => >::hold_available(a, reason, who), + Right(a) => + >::hold_available(a, reason, who), + } + } + fn total_balance_on_hold(asset: Self::AssetId, who: &AccountId) -> Self::Balance { + match Criterion::convert(asset) { + Left(a) => >::total_balance_on_hold(a, who), + Right(a) => >::total_balance_on_hold(a, who), + } + } + fn balance_on_hold( + asset: Self::AssetId, + reason: &Self::Reason, + who: &AccountId, + ) -> Self::Balance { + match Criterion::convert(asset) { + Left(a) => >::balance_on_hold(a, reason, who), + Right(a) => + >::balance_on_hold(a, reason, who), + } + } + fn can_hold( + asset: Self::AssetId, + reason: &Self::Reason, + who: &AccountId, + amount: Self::Balance, + ) -> bool { + match Criterion::convert(asset) { + Left(a) => + >::can_hold(a, reason, who, amount), + Right(a) => + >::can_hold(a, reason, who, amount), + } + } +} + +impl< + Left: fungibles::InspectFreeze, + Right: fungibles::InspectFreeze, + Criterion: Convert>, + AssetKind: AssetId, + AccountId, + > fungibles::InspectFreeze for UnionOf +{ + type Id = Left::Id; + fn balance_frozen(asset: Self::AssetId, id: &Self::Id, who: &AccountId) -> Self::Balance { + match Criterion::convert(asset) { + Left(a) => >::balance_frozen(a, id, who), + Right(a) => >::balance_frozen(a, id, who), + } + } + fn balance_freezable(asset: Self::AssetId, who: &AccountId) -> Self::Balance { + match Criterion::convert(asset) { + Left(a) => >::balance_freezable(a, who), + Right(a) => >::balance_freezable(a, who), + } + } + fn can_freeze(asset: Self::AssetId, id: &Self::Id, who: &AccountId) -> bool { + match Criterion::convert(asset) { + Left(a) => >::can_freeze(a, id, who), + Right(a) => >::can_freeze(a, id, who), + } + } +} + +impl< + Left: fungibles::Unbalanced, + Right: fungibles::Unbalanced, + Criterion: Convert>, + AssetKind: AssetId, + AccountId, + > fungibles::Unbalanced for UnionOf +{ + fn handle_dust(dust: fungibles::Dust) + where + Self: Sized, + { + match Criterion::convert(dust.0) { + Left(a) => + >::handle_dust(fungibles::Dust(a, dust.1)), + Right(a) => + >::handle_dust(fungibles::Dust(a, dust.1)), + } + } + fn write_balance( + asset: Self::AssetId, + who: &AccountId, + amount: Self::Balance, + ) -> Result, DispatchError> { + match Criterion::convert(asset) { + Left(a) => >::write_balance(a, who, amount), + Right(a) => >::write_balance(a, who, amount), + } + } + fn set_total_issuance(asset: Self::AssetId, amount: Self::Balance) -> () { + match Criterion::convert(asset) { + Left(a) => >::set_total_issuance(a, amount), + Right(a) => >::set_total_issuance(a, amount), + } + } + fn decrease_balance( + asset: Self::AssetId, + who: &AccountId, + amount: Self::Balance, + precision: Precision, + preservation: Preservation, + force: Fortitude, + ) -> Result { + match Criterion::convert(asset) { + Left(a) => >::decrease_balance( + a, + who, + amount, + precision, + preservation, + force, + ), + Right(a) => >::decrease_balance( + a, + who, + amount, + precision, + preservation, + force, + ), + } + } + fn increase_balance( + asset: Self::AssetId, + who: &AccountId, + amount: Self::Balance, + precision: Precision, + ) -> Result { + match Criterion::convert(asset) { + Left(a) => >::increase_balance( + a, who, amount, precision, + ), + Right(a) => >::increase_balance( + a, who, amount, precision, + ), + } + } +} + +impl< + Left: fungibles::UnbalancedHold, + Right: fungibles::UnbalancedHold, + Criterion: Convert>, + AssetKind: AssetId, + AccountId, + > fungibles::UnbalancedHold for UnionOf +{ + fn set_balance_on_hold( + asset: Self::AssetId, + reason: &Self::Reason, + who: &AccountId, + amount: Self::Balance, + ) -> DispatchResult { + match Criterion::convert(asset) { + Left(a) => >::set_balance_on_hold( + a, reason, who, amount, + ), + Right(a) => >::set_balance_on_hold( + a, reason, who, amount, + ), + } + } + fn decrease_balance_on_hold( + asset: Self::AssetId, + reason: &Self::Reason, + who: &AccountId, + amount: Self::Balance, + precision: Precision, + ) -> Result { + match Criterion::convert(asset) { + Left(a) => >::decrease_balance_on_hold( + a, reason, who, amount, precision, + ), + Right(a) => >::decrease_balance_on_hold( + a, reason, who, amount, precision, + ), + } + } + fn increase_balance_on_hold( + asset: Self::AssetId, + reason: &Self::Reason, + who: &AccountId, + amount: Self::Balance, + precision: Precision, + ) -> Result { + match Criterion::convert(asset) { + Left(a) => >::decrease_balance_on_hold( + a, reason, who, amount, precision, + ), + Right(a) => >::decrease_balance_on_hold( + a, reason, who, amount, precision, + ), + } + } +} + +impl< + Left: fungibles::Mutate, + Right: fungibles::Mutate, + Criterion: Convert>, + AssetKind: AssetId, + AccountId, + > fungibles::Mutate for UnionOf +{ + fn mint_into( + asset: Self::AssetId, + who: &AccountId, + amount: Self::Balance, + ) -> Result { + match Criterion::convert(asset) { + Left(a) => >::mint_into(a, who, amount), + Right(a) => >::mint_into(a, who, amount), + } + } + fn burn_from( + asset: Self::AssetId, + who: &AccountId, + amount: Self::Balance, + precision: Precision, + force: Fortitude, + ) -> Result { + match Criterion::convert(asset) { + Left(a) => + >::burn_from(a, who, amount, precision, force), + Right(a) => + >::burn_from(a, who, amount, precision, force), + } + } + fn shelve( + asset: Self::AssetId, + who: &AccountId, + amount: Self::Balance, + ) -> Result { + match Criterion::convert(asset) { + Left(a) => >::shelve(a, who, amount), + Right(a) => >::shelve(a, who, amount), + } + } + fn restore( + asset: Self::AssetId, + who: &AccountId, + amount: Self::Balance, + ) -> Result { + match Criterion::convert(asset) { + Left(a) => >::restore(a, who, amount), + Right(a) => >::restore(a, who, amount), + } + } + fn transfer( + asset: Self::AssetId, + source: &AccountId, + dest: &AccountId, + amount: Self::Balance, + preservation: Preservation, + ) -> Result { + match Criterion::convert(asset) { + Left(a) => >::transfer( + a, + source, + dest, + amount, + preservation, + ), + Right(a) => >::transfer( + a, + source, + dest, + amount, + preservation, + ), + } + } + + fn set_balance(asset: Self::AssetId, who: &AccountId, amount: Self::Balance) -> Self::Balance { + match Criterion::convert(asset) { + Left(a) => >::set_balance(a, who, amount), + Right(a) => >::set_balance(a, who, amount), + } + } +} + +impl< + Left: fungibles::MutateHold, + Right: fungibles::MutateHold, + Criterion: Convert>, + AssetKind: AssetId, + AccountId, + > fungibles::MutateHold for UnionOf +{ + fn hold( + asset: Self::AssetId, + reason: &Self::Reason, + who: &AccountId, + amount: Self::Balance, + ) -> DispatchResult { + match Criterion::convert(asset) { + Left(a) => >::hold(a, reason, who, amount), + Right(a) => >::hold(a, reason, who, amount), + } + } + fn release( + asset: Self::AssetId, + reason: &Self::Reason, + who: &AccountId, + amount: Self::Balance, + precision: Precision, + ) -> Result { + match Criterion::convert(asset) { + Left(a) => >::release( + a, reason, who, amount, precision, + ), + Right(a) => >::release( + a, reason, who, amount, precision, + ), + } + } + fn burn_held( + asset: Self::AssetId, + reason: &Self::Reason, + who: &AccountId, + amount: Self::Balance, + precision: Precision, + force: Fortitude, + ) -> Result { + match Criterion::convert(asset) { + Left(a) => >::burn_held( + a, reason, who, amount, precision, force, + ), + Right(a) => >::burn_held( + a, reason, who, amount, precision, force, + ), + } + } + fn transfer_on_hold( + asset: Self::AssetId, + reason: &Self::Reason, + source: &AccountId, + dest: &AccountId, + amount: Self::Balance, + precision: Precision, + mode: Restriction, + force: Fortitude, + ) -> Result { + match Criterion::convert(asset) { + Left(a) => >::transfer_on_hold( + a, reason, source, dest, amount, precision, mode, force, + ), + Right(a) => >::transfer_on_hold( + a, reason, source, dest, amount, precision, mode, force, + ), + } + } + fn transfer_and_hold( + asset: Self::AssetId, + reason: &Self::Reason, + source: &AccountId, + dest: &AccountId, + amount: Self::Balance, + precision: Precision, + preservation: Preservation, + force: Fortitude, + ) -> Result { + match Criterion::convert(asset) { + Left(a) => >::transfer_and_hold( + a, + reason, + source, + dest, + amount, + precision, + preservation, + force, + ), + Right(a) => >::transfer_and_hold( + a, + reason, + source, + dest, + amount, + precision, + preservation, + force, + ), + } + } +} + +impl< + Left: fungibles::MutateFreeze, + Right: fungibles::MutateFreeze, + Criterion: Convert>, + AssetKind: AssetId, + AccountId, + > fungibles::MutateFreeze for UnionOf +{ + fn set_freeze( + asset: Self::AssetId, + id: &Self::Id, + who: &AccountId, + amount: Self::Balance, + ) -> DispatchResult { + match Criterion::convert(asset) { + Left(a) => >::set_freeze(a, id, who, amount), + Right(a) => + >::set_freeze(a, id, who, amount), + } + } + fn extend_freeze( + asset: Self::AssetId, + id: &Self::Id, + who: &AccountId, + amount: Self::Balance, + ) -> DispatchResult { + match Criterion::convert(asset) { + Left(a) => + >::extend_freeze(a, id, who, amount), + Right(a) => + >::extend_freeze(a, id, who, amount), + } + } + fn thaw(asset: Self::AssetId, id: &Self::Id, who: &AccountId) -> DispatchResult { + match Criterion::convert(asset) { + Left(a) => >::thaw(a, id, who), + Right(a) => >::thaw(a, id, who), + } + } +} + +pub struct ConvertImbalanceDropHandler< + Left, + Right, + LeftAssetId, + RightAssetId, + Criterion, + AssetKind, + Balance, + AccountId, +>( + sp_std::marker::PhantomData<( + Left, + Right, + LeftAssetId, + RightAssetId, + Criterion, + AssetKind, + Balance, + AccountId, + )>, +); + +impl< + Left: fungibles::HandleImbalanceDrop, + Right: fungibles::HandleImbalanceDrop, + LeftAssetId, + RightAssetId, + Criterion: Convert>, + AssetKind, + Balance, + AccountId, + > fungibles::HandleImbalanceDrop + for ConvertImbalanceDropHandler< + Left, + Right, + LeftAssetId, + RightAssetId, + Criterion, + AssetKind, + Balance, + AccountId, + > +{ + fn handle(asset: AssetKind, amount: Balance) { + match Criterion::convert(asset) { + Left(a) => Left::handle(a, amount), + Right(a) => Right::handle(a, amount), + } + } +} + +impl< + Left: fungibles::Balanced, + Right: fungibles::Balanced, + Criterion: Convert>, + AssetKind: AssetId, + AccountId, + > fungibles::Balanced for UnionOf +{ + type OnDropDebt = ConvertImbalanceDropHandler< + Left::OnDropDebt, + Right::OnDropDebt, + Left::AssetId, + Right::AssetId, + Criterion, + AssetKind, + Left::Balance, + AccountId, + >; + type OnDropCredit = ConvertImbalanceDropHandler< + Left::OnDropCredit, + Right::OnDropCredit, + Left::AssetId, + Right::AssetId, + Criterion, + AssetKind, + Left::Balance, + AccountId, + >; + + fn deposit( + asset: Self::AssetId, + who: &AccountId, + value: Self::Balance, + precision: Precision, + ) -> Result, DispatchError> { + match Criterion::convert(asset.clone()) { + Left(a) => >::deposit(a, who, value, precision) + .map(|inner_debt| { + let debt = fungibles::Imbalance::new(asset, inner_debt.peek()); + fungibles::Imbalance::forget(inner_debt); + debt + }), + Right(a) => >::deposit( + a, who, value, precision, + ) + .map(|inner_debt| { + let debt = fungibles::Imbalance::new(asset, inner_debt.peek()); + fungibles::Imbalance::forget(inner_debt); + debt + }), + } + } + fn issue(asset: Self::AssetId, amount: Self::Balance) -> fungibles::Credit { + match Criterion::convert(asset.clone()) { + Left(a) => { + let inner_credit = >::issue(a, amount); + let credit = fungibles::Imbalance::new(asset, inner_credit.peek()); + fungibles::Imbalance::forget(inner_credit); + credit + }, + Right(a) => { + let inner_credit = >::issue(a, amount); + let credit = fungibles::Imbalance::new(asset, inner_credit.peek()); + fungibles::Imbalance::forget(inner_credit); + credit + }, + } + } + fn pair( + asset: Self::AssetId, + amount: Self::Balance, + ) -> (fungibles::Debt, fungibles::Credit) { + match Criterion::convert(asset.clone()) { + Left(a) => { + let (inner_a, inner_b) = >::pair(a, amount); + let (a, b) = ( + fungibles::Imbalance::new(asset.clone(), inner_a.peek()), + fungibles::Imbalance::new(asset, inner_b.peek()), + ); + fungibles::Imbalance::forget(inner_a); + fungibles::Imbalance::forget(inner_b); + (a, b) + }, + Right(a) => { + let (inner_a, inner_b) = >::pair(a, amount); + let (a, b) = ( + fungibles::Imbalance::new(asset.clone(), inner_a.peek()), + fungibles::Imbalance::new(asset, inner_b.peek()), + ); + fungibles::Imbalance::forget(inner_a); + fungibles::Imbalance::forget(inner_b); + (a, b) + }, + } + } + fn rescind(asset: Self::AssetId, amount: Self::Balance) -> fungibles::Debt { + match Criterion::convert(asset.clone()) { + Left(a) => { + let inner_debt = >::rescind(a, amount); + let debt = fungibles::Imbalance::new(asset, inner_debt.peek()); + fungibles::Imbalance::forget(inner_debt); + debt + }, + Right(a) => { + let inner_debt = >::rescind(a, amount); + let debt = fungibles::Imbalance::new(asset, inner_debt.peek()); + fungibles::Imbalance::forget(inner_debt); + debt + }, + } + } + fn resolve( + who: &AccountId, + credit: fungibles::Credit, + ) -> Result<(), fungibles::Credit> { + let asset = credit.asset(); + match Criterion::convert(asset.clone()) { + Left(a) => { + let inner_credit = fungibles::Imbalance::new(a, credit.peek()); + fungibles::Imbalance::forget(credit); + >::resolve(who, inner_credit).map_err( + |inner_credit| { + let credit = fungibles::Imbalance::new(asset, inner_credit.peek()); + fungibles::Imbalance::forget(inner_credit); + credit + }, + ) + }, + Right(a) => { + let inner_credit = fungibles::Imbalance::new(a, credit.peek()); + fungibles::Imbalance::forget(credit); + >::resolve(who, inner_credit).map_err( + |inner_credit| { + let credit = fungibles::Imbalance::new(asset, inner_credit.peek()); + fungibles::Imbalance::forget(inner_credit); + credit + }, + ) + }, + } + } + fn settle( + who: &AccountId, + debt: fungibles::Debt, + preservation: Preservation, + ) -> Result, fungibles::Debt> { + let asset = debt.asset(); + match Criterion::convert(asset.clone()) { + Left(a) => { + let inner_debt = fungibles::Imbalance::new(a, debt.peek()); + fungibles::Imbalance::forget(debt); + match >::settle( + who, + inner_debt, + preservation, + ) { + Ok(inner_credit) => { + let credit = fungibles::Imbalance::new(asset, inner_credit.peek()); + fungibles::Imbalance::forget(inner_credit); + Ok(credit) + }, + Err(inner_debt) => { + let debt = fungibles::Imbalance::new(asset, inner_debt.peek()); + fungibles::Imbalance::forget(inner_debt); + Err(debt) + }, + } + }, + Right(a) => { + let inner_debt = fungibles::Imbalance::new(a, debt.peek()); + fungibles::Imbalance::forget(debt); + match >::settle( + who, + inner_debt, + preservation, + ) { + Ok(inner_credit) => { + let credit = fungibles::Imbalance::new(asset, inner_credit.peek()); + fungibles::Imbalance::forget(inner_credit); + Ok(credit) + }, + Err(inner_debt) => { + let debt = fungibles::Imbalance::new(asset, inner_debt.peek()); + fungibles::Imbalance::forget(inner_debt); + Err(debt) + }, + } + }, + } + } + fn withdraw( + asset: Self::AssetId, + who: &AccountId, + value: Self::Balance, + precision: Precision, + preservation: Preservation, + force: Fortitude, + ) -> Result, DispatchError> { + match Criterion::convert(asset.clone()) { + Left(a) => >::withdraw( + a, + who, + value, + precision, + preservation, + force, + ) + .map(|inner_credit| { + let credit = fungibles::Imbalance::new(asset, inner_credit.peek()); + fungibles::Imbalance::forget(inner_credit); + credit + }), + Right(a) => >::withdraw( + a, + who, + value, + precision, + preservation, + force, + ) + .map(|inner_credit| { + let credit = fungibles::Imbalance::new(asset, inner_credit.peek()); + fungibles::Imbalance::forget(inner_credit); + credit + }), + } + } +} + +impl< + Left: fungibles::BalancedHold, + Right: fungibles::BalancedHold, + Criterion: Convert>, + AssetKind: AssetId, + AccountId, + > fungibles::BalancedHold for UnionOf +{ + fn slash( + asset: Self::AssetId, + reason: &Self::Reason, + who: &AccountId, + amount: Self::Balance, + ) -> (fungibles::Credit, Self::Balance) { + match Criterion::convert(asset.clone()) { + Left(a) => { + let (inner_credit, amount) = + >::slash(a, reason, who, amount); + let credit = fungibles::Imbalance::new(asset, inner_credit.peek()); + fungibles::Imbalance::forget(inner_credit); + (credit, amount) + }, + Right(a) => { + let (inner_credit, amount) = + >::slash(a, reason, who, amount); + let credit = fungibles::Imbalance::new(asset, inner_credit.peek()); + fungibles::Imbalance::forget(inner_credit); + (credit, amount) + }, + } + } +} + +impl< + Left: fungibles::Inspect + fungibles::Create, + Right: fungibles::Inspect + fungibles::Create, + Criterion: Convert>, + AssetKind: AssetId, + AccountId, + > fungibles::Create for UnionOf +{ + fn create( + asset: AssetKind, + admin: AccountId, + is_sufficient: bool, + min_balance: Self::Balance, + ) -> DispatchResult { + match Criterion::convert(asset) { + Left(a) => + >::create(a, admin, is_sufficient, min_balance), + Right(a) => >::create( + a, + admin, + is_sufficient, + min_balance, + ), + } + } +} + +impl< + Left: fungibles::Inspect + AccountTouch, + Right: fungibles::Inspect + + AccountTouch< + Right::AssetId, + AccountId, + Balance = >::Balance, + >, + Criterion: Convert>, + AssetKind: AssetId, + AccountId, + > AccountTouch for UnionOf +{ + type Balance = >::Balance; + + fn deposit_required(asset: AssetKind) -> Self::Balance { + match Criterion::convert(asset) { + Left(a) => >::deposit_required(a), + Right(a) => >::deposit_required(a), + } + } + + fn touch(asset: AssetKind, who: AccountId, depositor: AccountId) -> DispatchResult { + match Criterion::convert(asset) { + Left(a) => >::touch(a, who, depositor), + Right(a) => + >::touch(a, who, depositor), + } + } +} + +impl< + Left: fungibles::Inspect + ContainsPair, + Right: fungibles::Inspect + + ContainsPair, + Criterion: Convert>, + AssetKind: AssetId, + AccountId, + > ContainsPair for UnionOf +{ + fn contains(asset: &AssetKind, who: &AccountId) -> bool { + match Criterion::convert(asset.clone()) { + Left(a) => >::contains(&a, who), + Right(a) => >::contains(&a, who), + } + } +} From 50cdf8203605fcb884e59d838155ca96b44a98e7 Mon Sep 17 00:00:00 2001 From: muharem Date: Wed, 25 Oct 2023 16:24:55 +0200 Subject: [PATCH 52/98] tests for fungible/s sets --- substrate/frame/assets/src/tests.rs | 333 ++++++++++++++++++++++++++++ 1 file changed, 333 insertions(+) diff --git a/substrate/frame/assets/src/tests.rs b/substrate/frame/assets/src/tests.rs index f1b116a0f4a0..10e50136ef65 100644 --- a/substrate/frame/assets/src/tests.rs +++ b/substrate/frame/assets/src/tests.rs @@ -1775,3 +1775,336 @@ fn asset_destroy_refund_existence_deposit() { assert_eq!(Balances::reserved_balance(&admin), 0); }); } + +mod sets { + use super::*; + use frame_support::{ + parameter_types, + traits::{ + fungible, + fungible::ItemOf, + fungibles, + tokens::{ + fungibles::{ + Balanced as FungiblesBalanced, Create as FungiblesCreate, + Inspect as FungiblesInspect, Mutate as FungiblesMutate, + }, + Fortitude, Precision, Preservation, + }, + }, + }; + use sp_runtime::{traits::ConvertToValue, Either}; + + const FIRST_ASSET: u32 = 0; + const UNKNOWN_ASSET: u32 = 10; + + parameter_types! { + pub const LeftAsset: Either<(), u32> = Either::Left(()); + pub const RightAsset: Either = Either::Right(()); + pub const RightUnitAsset: Either<(), ()> = Either::Right(()); + } + + /// Implementation of the `fungible` traits through the [`ItemOf`] type, specifically for a + /// single asset class from [`T`] identified by [`FIRST_ASSET`]. + type FirstFungible = ItemOf, u64>; + /// Implementation of the `fungible` traits through the [`ItemOf`] type, specifically for a + /// single asset class from [`T`] identified by [`UNKNOWN_ASSET`]. + type UnknownFungible = ItemOf, u64>; + /// Implementation of `fungibles` traits using [`fungibles::UnionOf`] that exclusively utilizes + /// the [`FirstFungible`] from the left. + type LeftFungible = + fungible::UnionOf, T, ConvertToValue, (), u64>; + /// Implementation of `fungibles` traits using [`fungibles::UnionOf`] that exclusively utilizes + /// the [`LeftFungible`] from the right. + type RightFungible = fungible::UnionOf< + UnknownFungible, + LeftFungible, + ConvertToValue, + (), + u64, + >; + /// Implementation of `fungibles` traits using [`fungibles::UnionOf`] that exclusively utilizes + /// the [`RightFungible`] from the left. + type LeftFungibles = + fungibles::UnionOf, T, ConvertToValue, (), u64>; + /// Implementation of `fungibles` traits using [`fungibles::UnionOf`] that exclusively utilizes + /// the [`LeftFungibles`] from the right. + /// + /// By using this type, we can navigate through each branch of [`fungible::UnionOf`], + /// [`fungibles::UnionOf`], and [`ItemOf`] to access the underlying `fungibles::*` + /// implementation provided by the pallet. + type First = fungibles::UnionOf, ConvertToValue, (), u64>; + + #[test] + fn deposit_from_set_types_works() { + new_test_ext().execute_with(|| { + let asset1 = 0; + let account1 = 1; + let account2 = 2; + + assert_ok!(>::create(asset1, account1, true, 1)); + assert_ok!(Assets::mint_into(asset1, &account1, 100)); + + assert_eq!(First::::total_issuance(()), 100); + assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); + + let imb = First::::deposit((), &account2, 50, Precision::Exact).unwrap(); + assert_eq!(First::::balance((), &account2), 50); + assert_eq!(First::::total_issuance(()), 100); + + assert_eq!(imb.peek(), 50); + + let (imb1, imb2) = imb.split(30); + assert_eq!(imb1.peek(), 30); + assert_eq!(imb2.peek(), 20); + + drop(imb2); + assert_eq!(First::::total_issuance(()), 120); + + assert!(First::::settle(&account1, imb1, Preservation::Preserve).is_ok()); + assert_eq!(First::::balance((), &account1), 70); + assert_eq!(First::::balance((), &account2), 50); + assert_eq!(First::::total_issuance(()), 120); + + assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); + }); + } + + #[test] + fn issue_from_set_types_works() { + new_test_ext().execute_with(|| { + let asset1: u32 = 0; + let account1: u64 = 1; + + assert_ok!(>::create(asset1, account1, true, 1)); + assert_ok!(Assets::mint_into(asset1, &account1, 100)); + + assert_eq!(First::::balance((), &account1), 100); + assert_eq!(First::::total_issuance(()), 100); + assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); + + let imb = First::::issue((), 100); + assert_eq!(First::::total_issuance(()), 200); + assert_eq!(imb.peek(), 100); + + let (imb1, imb2) = imb.split(30); + assert_eq!(imb1.peek(), 30); + assert_eq!(imb2.peek(), 70); + + drop(imb2); + assert_eq!(First::::total_issuance(()), 130); + + assert!(First::::resolve(&account1, imb1).is_ok()); + assert_eq!(First::::balance((), &account1), 130); + assert_eq!(First::::total_issuance(()), 130); + + assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); + }); + } + + #[test] + fn pair_from_set_types_works() { + new_test_ext().execute_with(|| { + let asset1: u32 = 0; + let account1: u64 = 1; + + assert_ok!(>::create(asset1, account1, true, 1)); + assert_ok!(Assets::mint_into(asset1, &account1, 100)); + + assert_eq!(First::::balance((), &account1), 100); + assert_eq!(First::::total_issuance(()), 100); + assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); + + let (debt, credit) = First::::pair((), 100); + assert_eq!(First::::total_issuance(()), 100); + assert_eq!(debt.peek(), 100); + assert_eq!(credit.peek(), 100); + + let (debt1, debt2) = debt.split(30); + assert_eq!(debt1.peek(), 30); + assert_eq!(debt2.peek(), 70); + + drop(debt2); + assert_eq!(First::::total_issuance(()), 170); + + assert!(First::::settle(&account1, debt1, Preservation::Preserve).is_ok()); + assert_eq!(First::::balance((), &account1), 70); + assert_eq!(First::::total_issuance(()), 170); + + let (credit1, credit2) = credit.split(40); + assert_eq!(credit1.peek(), 40); + assert_eq!(credit2.peek(), 60); + + drop(credit2); + assert_eq!(First::::total_issuance(()), 110); + + assert!(First::::resolve(&account1, credit1).is_ok()); + assert_eq!(First::::balance((), &account1), 110); + assert_eq!(First::::total_issuance(()), 110); + + assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); + }); + } + + #[test] + fn rescind_from_set_types_works() { + new_test_ext().execute_with(|| { + let asset1: u32 = 0; + let account1: u64 = 1; + + assert_ok!(>::create(asset1, account1, true, 1)); + assert_ok!(Assets::mint_into(asset1, &account1, 100)); + + assert_eq!(First::::total_issuance(()), 100); + assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); + + let imb = First::::rescind((), 20); + assert_eq!(First::::total_issuance(()), 80); + + assert_eq!(imb.peek(), 20); + + let (imb1, imb2) = imb.split(15); + assert_eq!(imb1.peek(), 15); + assert_eq!(imb2.peek(), 5); + + drop(imb2); + assert_eq!(First::::total_issuance(()), 85); + + assert!(First::::settle(&account1, imb1, Preservation::Preserve).is_ok()); + assert_eq!(First::::balance((), &account1), 85); + assert_eq!(First::::total_issuance(()), 85); + + assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); + }); + } + + #[test] + fn resolve_from_set_types_works() { + new_test_ext().execute_with(|| { + let asset1: u32 = 0; + let account1: u64 = 1; + let account2: u64 = 2; + let ed = 11; + + assert_ok!(>::create(asset1, account1, true, ed)); + assert_ok!(Assets::mint_into(asset1, &account1, 100)); + + assert_eq!(First::::balance((), &account1), 100); + assert_eq!(First::::total_issuance(()), 100); + assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); + + let imb = First::::issue((), 100); + assert_eq!(First::::total_issuance(()), 200); + assert_eq!(imb.peek(), 100); + + let (imb1, imb2) = imb.split(10); + assert_eq!(imb1.peek(), 10); + assert_eq!(imb2.peek(), 90); + assert_eq!(First::::total_issuance(()), 200); + + // ed requirements not met. + let imb1 = First::::resolve(&account2, imb1).unwrap_err(); + assert_eq!(imb1.peek(), 10); + drop(imb1); + assert_eq!(First::::total_issuance(()), 190); + assert_eq!(First::::balance((), &account2), 0); + + // resolve to new account `2`. + assert_ok!(First::::resolve(&account2, imb2)); + assert_eq!(First::::total_issuance(()), 190); + assert_eq!(First::::balance((), &account2), 90); + + assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); + }); + } + + #[test] + fn settle_from_set_types_works() { + new_test_ext().execute_with(|| { + let asset1: u32 = 0; + let account1: u64 = 1; + let account2: u64 = 2; + let ed = 11; + + assert_ok!(>::create(asset1, account1, true, ed)); + assert_ok!(Assets::mint_into(asset1, &account1, 100)); + assert_ok!(Assets::mint_into(asset1, &account2, 100)); + + assert_eq!(First::::balance((), &account2), 100); + assert_eq!(First::::total_issuance(()), 200); + assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); + + let imb = First::::rescind((), 100); + assert_eq!(First::::total_issuance(()), 100); + assert_eq!(imb.peek(), 100); + + let (imb1, imb2) = imb.split(10); + assert_eq!(imb1.peek(), 10); + assert_eq!(imb2.peek(), 90); + assert_eq!(First::::total_issuance(()), 100); + + // ed requirements not met. + let imb2 = + First::::settle(&account2, imb2, Preservation::Preserve).unwrap_err(); + assert_eq!(imb2.peek(), 90); + drop(imb2); + assert_eq!(First::::total_issuance(()), 190); + assert_eq!(First::::balance((), &account2), 100); + + // settle to account `1`. + assert_ok!(First::::settle(&account2, imb1, Preservation::Preserve)); + assert_eq!(First::::total_issuance(()), 190); + assert_eq!(First::::balance((), &account2), 90); + + let imb = First::::rescind((), 85); + assert_eq!(First::::total_issuance(()), 105); + assert_eq!(imb.peek(), 85); + + // settle to account `1` and expect some dust. + let imb = First::::settle(&account2, imb, Preservation::Expendable).unwrap(); + assert_eq!(imb.peek(), 5); + assert_eq!(First::::total_issuance(()), 105); + assert_eq!(First::::balance((), &account2), 0); + + drop(imb); + assert_eq!(First::::total_issuance(()), 100); + + assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); + }); + } + + #[test] + fn withdraw_from_set_types_works() { + new_test_ext().execute_with(|| { + let asset1 = 0; + let account1 = 1; + let account2 = 2; + + assert_ok!(>::create(asset1, account1, true, 1)); + assert_ok!(Assets::mint_into(asset1, &account1, 100)); + assert_ok!(Assets::mint_into(asset1, &account2, 100)); + + assert_eq!(First::::total_issuance(()), 200); + assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); + + let imb = First::::withdraw( + (), + &account2, + 50, + Precision::Exact, + Preservation::Preserve, + Fortitude::Polite, + ) + .unwrap(); + assert_eq!(First::::balance((), &account2), 50); + assert_eq!(First::::total_issuance(()), 200); + + assert_eq!(imb.peek(), 50); + drop(imb); + assert_eq!(First::::total_issuance(()), 150); + assert_eq!(First::::balance((), &account2), 50); + + assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); + }); + } +} From 5fbdc2adb4bab41a5dc024cbf20d349d202cf713 Mon Sep 17 00:00:00 2001 From: muharem Date: Wed, 25 Oct 2023 16:25:46 +0200 Subject: [PATCH 53/98] asset conversion --- .../asset-conversion/src/benchmarking.rs | 473 +++---- substrate/frame/asset-conversion/src/lib.rs | 510 +++---- substrate/frame/asset-conversion/src/mock.rs | 42 +- substrate/frame/asset-conversion/src/swap.rs | 32 +- substrate/frame/asset-conversion/src/tests.rs | 1246 +++++++++-------- substrate/frame/asset-conversion/src/types.rs | 276 +--- 6 files changed, 1176 insertions(+), 1403 deletions(-) diff --git a/substrate/frame/asset-conversion/src/benchmarking.rs b/substrate/frame/asset-conversion/src/benchmarking.rs index 628953d0558f..2892995830bc 100644 --- a/substrate/frame/asset-conversion/src/benchmarking.rs +++ b/substrate/frame/asset-conversion/src/benchmarking.rs @@ -18,25 +18,48 @@ //! Asset Conversion pallet benchmarking. use super::*; -use frame_benchmarking::{benchmarks, whitelisted_caller}; +use crate::Pallet as AssetConversion; +use frame_benchmarking::{v2::*, whitelisted_caller}; use frame_support::{ assert_ok, traits::{ - fungible::{Inspect as InspectFungible, Mutate as MutateFungible, Unbalanced}, + fungible::NativeOrWithId, fungibles::{Create, Inspect, Mutate}, }, }; use frame_system::RawOrigin as SystemOrigin; use sp_core::Get; -use sp_runtime::traits::{Bounded, StaticLookup}; -use sp_std::{ops::Div, prelude::*}; +use sp_runtime::traits::StaticLookup; +use sp_std::{marker::PhantomData, prelude::*}; -use crate::Pallet as AssetConversion; +/// Benchmark Helper +pub trait BenchmarkHelper { + /// Returns a valid assets pair for the pool creation. + fn create_pair(seed1: u32, seed2: u32) -> (AssetKind, AssetKind); +} + +impl BenchmarkHelper for () +where + AssetKind: From, +{ + fn create_pair(seed1: u32, seed2: u32) -> (AssetKind, AssetKind) { + (seed1.into(), seed2.into()) + } +} + +/// Factory for creating a valid asset pairs with [`NativeOrWithId::Native`] always leading in the +/// pair. +pub struct NativeOrWithIdFactory(PhantomData); +impl + Ord> BenchmarkHelper> + for NativeOrWithIdFactory +{ + fn create_pair(_seed1: u32, seed2: u32) -> (NativeOrWithId, NativeOrWithId) { + (NativeOrWithId::Native, NativeOrWithId::WithId(seed2.into())) + } +} const INITIAL_ASSET_BALANCE: u128 = 1_000_000_000_000; type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; -type BalanceOf = - <::Currency as InspectFungible<::AccountId>>::Balance; fn get_lp_token_id() -> T::PoolAssetId where @@ -46,37 +69,41 @@ where (next_id - 1).into() } -fn create_asset(asset: &T::MultiAssetId) -> (T::AccountId, AccountIdLookupOf) +fn create_asset(asset: &T::AssetKind) -> (T::AccountId, AccountIdLookupOf) where T::Balance: From, - T::Currency: Unbalanced, T::Assets: Create + Mutate, { let caller: T::AccountId = whitelisted_caller(); let caller_lookup = T::Lookup::unlookup(caller.clone()); - if let MultiAssetIdConversionResult::Converted(asset_id) = - T::MultiAssetIdConverter::try_convert(asset) - { - T::Currency::set_balance(&caller, BalanceOf::::max_value().div(1000u32.into())); - assert_ok!(T::Assets::create(asset_id.clone(), caller.clone(), true, 1.into())); - assert_ok!(T::Assets::mint_into(asset_id, &caller, INITIAL_ASSET_BALANCE.into())); + if !T::Assets::asset_exists(asset.clone()) { + assert_ok!(T::Assets::create(asset.clone(), caller.clone(), true, 1.into())); } + assert_ok!(T::Assets::mint_into(asset.clone(), &caller, INITIAL_ASSET_BALANCE.into())); + (caller, caller_lookup) } fn create_asset_and_pool( - asset1: &T::MultiAssetId, - asset2: &T::MultiAssetId, + asset1: &T::AssetKind, + asset2: &T::AssetKind, ) -> (T::PoolAssetId, T::AccountId, AccountIdLookupOf) where T::Balance: From, - T::Currency: Unbalanced, T::Assets: Create + Mutate, T::PoolAssetId: Into, { + let fee_asset = T::PoolSetupFeeAsset::get(); + let (_, _) = create_asset::(&fee_asset); let (_, _) = create_asset::(asset1); let (caller, caller_lookup) = create_asset::(asset2); + assert_ok!(T::Assets::mint_into( + fee_asset.clone(), + &caller, + T::PoolSetupFee::get() + T::Assets::minimum_balance(fee_asset) + )); + assert_ok!(AssetConversion::::create_pool( SystemOrigin::Signed(caller.clone()).into(), Box::new(asset1.clone()), @@ -95,65 +122,75 @@ fn assert_last_event(generic_event: ::RuntimeEvent) { assert_eq!(event, &system_event); } -benchmarks! { - where_clause { - where - T::Currency: Unbalanced, - T::Balance: From + Into, - T::Assets: Create + Mutate, - T::PoolAssetId: Into, - } +#[benchmarks(where T::Balance: From + Into, T::Assets: Create + Mutate, T::PoolAssetId: Into,)] +mod benchmarks { + use super::*; - create_pool { - let asset1 = T::MultiAssetIdConverter::get_native(); - let asset2 = T::BenchmarkHelper::multiasset_id(0); + #[benchmark] + fn create_pool() { + let (asset1, asset2) = T::BenchmarkHelper::create_pair(0, 1); + let (_, _) = create_asset::(&asset1); let (caller, _) = create_asset::(&asset2); - }: _(SystemOrigin::Signed(caller.clone()), Box::new(asset1.clone()), Box::new(asset2.clone())) - verify { + + assert_ok!(T::Assets::mint_into( + T::PoolSetupFeeAsset::get(), + &caller, + T::PoolSetupFee::get() + T::Assets::minimum_balance(T::PoolSetupFeeAsset::get()) + )); + + #[extrinsic_call] + _(SystemOrigin::Signed(caller.clone()), Box::new(asset1.clone()), Box::new(asset2.clone())); + let lp_token = get_lp_token_id::(); - let pool_id = (asset1.clone(), asset2.clone()); - assert_last_event::(Event::PoolCreated { - creator: caller.clone(), - pool_account: AssetConversion::::get_pool_account(&pool_id), - pool_id, - lp_token, - }.into()); + let pool_id = T::PoolLocator::pool_id(&asset1, &asset2).unwrap(); + let pool_account = T::PoolLocator::address(&pool_id).unwrap(); + assert_last_event::( + Event::PoolCreated { creator: caller.clone(), pool_account, pool_id, lp_token }.into(), + ); } - add_liquidity { - let asset1 = T::MultiAssetIdConverter::get_native(); - let asset2 = T::BenchmarkHelper::multiasset_id(0); + #[benchmark] + fn add_liquidity() { + let (asset1, asset2) = T::BenchmarkHelper::create_pair(0, 1); let (lp_token, caller, _) = create_asset_and_pool::(&asset1, &asset2); - let ed: u128 = T::Currency::minimum_balance().into(); + let ed: u128 = T::Assets::minimum_balance(asset1.clone()).into(); let add_amount = 1000 + ed; - }: _(SystemOrigin::Signed(caller.clone()), Box::new(asset1.clone()), Box::new(asset2.clone()), add_amount.into(), 1000.into(), 0.into(), 0.into(), caller.clone()) - verify { - let pool_id = (asset1.clone(), asset2.clone()); - let lp_minted = AssetConversion::::calc_lp_amount_for_zero_supply(&add_amount.into(), &1000.into()).unwrap().into(); - assert_eq!( - T::PoolAssets::balance(lp_token, &caller), - lp_minted.into() - ); - assert_eq!( - T::Currency::balance(&AssetConversion::::get_pool_account(&pool_id)), - add_amount.into() - ); - assert_eq!( - T::Assets::balance(T::BenchmarkHelper::asset_id(0), &AssetConversion::::get_pool_account(&pool_id)), - 1000.into() + + #[extrinsic_call] + _( + SystemOrigin::Signed(caller.clone()), + Box::new(asset1.clone()), + Box::new(asset2.clone()), + add_amount.into(), + 1000.into(), + 0.into(), + 0.into(), + caller.clone(), ); + + let pool_account = T::PoolLocator::pool_address(&asset1, &asset2).unwrap(); + let lp_minted = + AssetConversion::::calc_lp_amount_for_zero_supply(&add_amount.into(), &1000.into()) + .unwrap() + .into(); + assert_eq!(T::PoolAssets::balance(lp_token, &caller), lp_minted.into()); + assert_eq!(T::Assets::balance(asset1, &pool_account), add_amount.into()); + assert_eq!(T::Assets::balance(asset2, &pool_account), 1000.into()); } - remove_liquidity { - let asset1 = T::MultiAssetIdConverter::get_native(); - let asset2 = T::BenchmarkHelper::multiasset_id(0); + #[benchmark] + fn remove_liquidity() { + let (asset1, asset2) = T::BenchmarkHelper::create_pair(0, 1); let (lp_token, caller, _) = create_asset_and_pool::(&asset1, &asset2); - let ed: u128 = T::Currency::minimum_balance().into(); + let ed: u128 = T::Assets::minimum_balance(asset1.clone()).into(); let add_amount = 100 * ed; - let lp_minted = AssetConversion::::calc_lp_amount_for_zero_supply(&add_amount.into(), &1000.into()).unwrap().into(); + let lp_minted = + AssetConversion::::calc_lp_amount_for_zero_supply(&add_amount.into(), &1000.into()) + .unwrap() + .into(); let remove_lp_amount = lp_minted.checked_div(10).unwrap(); - AssetConversion::::add_liquidity( + assert_ok!(AssetConversion::::add_liquidity( SystemOrigin::Signed(caller.clone()).into(), Box::new(asset1.clone()), Box::new(asset2.clone()), @@ -162,213 +199,151 @@ benchmarks! { 0.into(), 0.into(), caller.clone(), - )?; - let total_supply = >::total_issuance(lp_token.clone()); - }: _(SystemOrigin::Signed(caller.clone()), Box::new(asset1), Box::new(asset2), remove_lp_amount.into(), 0.into(), 0.into(), caller.clone()) - verify { - let new_total_supply = >::total_issuance(lp_token.clone()); - assert_eq!( - new_total_supply, - total_supply - remove_lp_amount.into() + )); + let total_supply = + >::total_issuance(lp_token.clone()); + + #[extrinsic_call] + _( + SystemOrigin::Signed(caller.clone()), + Box::new(asset1), + Box::new(asset2), + remove_lp_amount.into(), + 0.into(), + 0.into(), + caller.clone(), ); + + let new_total_supply = + >::total_issuance(lp_token.clone()); + assert_eq!(new_total_supply, total_supply - remove_lp_amount.into()); } - swap_exact_tokens_for_tokens { - let native = T::MultiAssetIdConverter::get_native(); - let asset1 = T::BenchmarkHelper::multiasset_id(1); - let asset2 = T::BenchmarkHelper::multiasset_id(2); - let (_, caller, _) = create_asset_and_pool::(&native, &asset1); - let (_, _) = create_asset::(&asset2); - let ed: u128 = T::Currency::minimum_balance().into(); + #[benchmark] + fn swap_exact_tokens_for_tokens() { + let (asset1, asset2) = T::BenchmarkHelper::create_pair(0, 1); + let (_, asset3) = T::BenchmarkHelper::create_pair(1, 2); + let (_, asset4) = T::BenchmarkHelper::create_pair(2, 3); + + let (_, caller, _) = create_asset_and_pool::(&asset1, &asset2); + let ed: u128 = T::Assets::minimum_balance(asset1.clone()).into(); - AssetConversion::::add_liquidity( + assert_ok!(AssetConversion::::add_liquidity( SystemOrigin::Signed(caller.clone()).into(), - Box::new(native.clone()), Box::new(asset1.clone()), + Box::new(asset2.clone()), (100 * ed).into(), 200.into(), + 1.into(), 0.into(), + caller.clone(), + )); + + let (_, _, _) = create_asset_and_pool::(&asset2, &asset3); + assert_ok!(AssetConversion::::add_liquidity( + SystemOrigin::Signed(caller.clone()).into(), + Box::new(asset2.clone()), + Box::new(asset3.clone()), + 200.into(), + 2000.into(), + 1.into(), 0.into(), caller.clone(), - )?; - - let path; - let swap_amount; - // if we only allow the native-asset pools, then the worst case scenario would be to swap - // asset1-native-asset2 - if !T::AllowMultiAssetPools::get() { - AssetConversion::::create_pool( - SystemOrigin::Signed(caller.clone()).into(), - Box::new(native.clone()), - Box::new(asset2.clone()) - )?; - AssetConversion::::add_liquidity( - SystemOrigin::Signed(caller.clone()).into(), - Box::new(native.clone()), - Box::new(asset2.clone()), - (500 * ed).into(), - 1000.into(), - 0.into(), - 0.into(), - caller.clone(), - )?; - path = vec![ - Box::new(asset1.clone()), - Box::new(native.clone()), - Box::new(asset2.clone()) - ]; - swap_amount = 100.into(); - } else { - let asset3 = T::BenchmarkHelper::multiasset_id(3); - AssetConversion::::create_pool( - SystemOrigin::Signed(caller.clone()).into(), - Box::new(asset1.clone()), - Box::new(asset2.clone()) - )?; - let (_, _) = create_asset::(&asset3); - AssetConversion::::create_pool( - SystemOrigin::Signed(caller.clone()).into(), - Box::new(asset2.clone()), - Box::new(asset3.clone()) - )?; - - AssetConversion::::add_liquidity( - SystemOrigin::Signed(caller.clone()).into(), - Box::new(asset1.clone()), - Box::new(asset2.clone()), - 200.into(), - 2000.into(), - 0.into(), - 0.into(), - caller.clone(), - )?; - AssetConversion::::add_liquidity( - SystemOrigin::Signed(caller.clone()).into(), - Box::new(asset2.clone()), - Box::new(asset3.clone()), - 2000.into(), - 2000.into(), - 0.into(), - 0.into(), - caller.clone(), - )?; - path = vec![ - Box::new(native.clone()), - Box::new(asset1.clone()), - Box::new(asset2.clone()), - Box::new(asset3.clone()) - ]; - swap_amount = ed.into(); - } - let native_balance = T::Currency::balance(&caller); - let asset1_balance = T::Assets::balance(T::BenchmarkHelper::asset_id(1), &caller); - }: _(SystemOrigin::Signed(caller.clone()), path, swap_amount, 1.into(), caller.clone(), false) - verify { - if !T::AllowMultiAssetPools::get() { - let new_asset1_balance = T::Assets::balance(T::BenchmarkHelper::asset_id(1), &caller); - assert_eq!(new_asset1_balance, asset1_balance - 100.into()); - } else { - let new_native_balance = T::Currency::balance(&caller); - assert_eq!(new_native_balance, native_balance - ed.into()); - } + )); + + let (_, _, _) = create_asset_and_pool::(&asset3, &asset4); + assert_ok!(AssetConversion::::add_liquidity( + SystemOrigin::Signed(caller.clone()).into(), + Box::new(asset3.clone()), + Box::new(asset4.clone()), + 2000.into(), + 2000.into(), + 1.into(), + 1.into(), + caller.clone(), + )); + let path = vec![ + Box::new(asset1.clone()), + Box::new(asset2.clone()), + Box::new(asset3.clone()), + Box::new(asset4.clone()), + ]; + + let swap_amount = ed.into(); + let asset1_balance = T::Assets::balance(asset1.clone(), &caller); + + #[extrinsic_call] + _(SystemOrigin::Signed(caller.clone()), path, swap_amount, 1.into(), caller.clone(), false); + + let new_asset1_balance = T::Assets::balance(asset1, &caller); + assert_eq!(new_asset1_balance, asset1_balance - ed.into()); } - swap_tokens_for_exact_tokens { - let native = T::MultiAssetIdConverter::get_native(); - let asset1 = T::BenchmarkHelper::multiasset_id(1); - let asset2 = T::BenchmarkHelper::multiasset_id(2); - let (_, caller, _) = create_asset_and_pool::(&native, &asset1); - let (_, _) = create_asset::(&asset2); - let ed: u128 = T::Currency::minimum_balance().into(); + #[benchmark] + fn swap_tokens_for_exact_tokens() { + let (asset1, asset2) = T::BenchmarkHelper::create_pair(0, 1); + let (_, asset3) = T::BenchmarkHelper::create_pair(1, 2); + let (_, asset4) = T::BenchmarkHelper::create_pair(2, 3); - AssetConversion::::add_liquidity( + let (_, caller, _) = create_asset_and_pool::(&asset1, &asset2); + let ed: u128 = T::Assets::minimum_balance(asset1.clone()).into(); + + assert_ok!(AssetConversion::::add_liquidity( SystemOrigin::Signed(caller.clone()).into(), - Box::new(native.clone()), Box::new(asset1.clone()), + Box::new(asset2.clone()), (1000 * ed).into(), 500.into(), + 1.into(), 0.into(), + caller.clone(), + )); + + let (_, _, _) = create_asset_and_pool::(&asset2, &asset3); + assert_ok!(AssetConversion::::add_liquidity( + SystemOrigin::Signed(caller.clone()).into(), + Box::new(asset2.clone()), + Box::new(asset3.clone()), + 2000.into(), + 2000.into(), + 1.into(), 0.into(), caller.clone(), - )?; - - let path; - // if we only allow the native-asset pools, then the worst case scenario would be to swap - // asset1-native-asset2 - if !T::AllowMultiAssetPools::get() { - AssetConversion::::create_pool( - SystemOrigin::Signed(caller.clone()).into(), - Box::new(native.clone()), - Box::new(asset2.clone()) - )?; - AssetConversion::::add_liquidity( - SystemOrigin::Signed(caller.clone()).into(), - Box::new(native.clone()), - Box::new(asset2.clone()), - (500 * ed).into(), - 1000.into(), - 0.into(), - 0.into(), - caller.clone(), - )?; - path = vec![ - Box::new(asset1.clone()), - Box::new(native.clone()), - Box::new(asset2.clone()) - ]; - } else { - AssetConversion::::create_pool( - SystemOrigin::Signed(caller.clone()).into(), - Box::new(asset1.clone()), - Box::new(asset2.clone()) - )?; - let asset3 = T::BenchmarkHelper::multiasset_id(3); - let (_, _) = create_asset::(&asset3); - AssetConversion::::create_pool( - SystemOrigin::Signed(caller.clone()).into(), - Box::new(asset2.clone()), - Box::new(asset3.clone()) - )?; - - AssetConversion::::add_liquidity( - SystemOrigin::Signed(caller.clone()).into(), - Box::new(asset1.clone()), - Box::new(asset2.clone()), - 2000.into(), - 2000.into(), - 0.into(), - 0.into(), - caller.clone(), - )?; - AssetConversion::::add_liquidity( - SystemOrigin::Signed(caller.clone()).into(), - Box::new(asset2.clone()), - Box::new(asset3.clone()), - 2000.into(), - 2000.into(), - 0.into(), - 0.into(), - caller.clone(), - )?; - path = vec![ - Box::new(native.clone()), - Box::new(asset1.clone()), - Box::new(asset2.clone()), - Box::new(asset3.clone()) - ]; - } - - let asset2_balance = T::Assets::balance(T::BenchmarkHelper::asset_id(2), &caller); - let asset3_balance = T::Assets::balance(T::BenchmarkHelper::asset_id(3), &caller); - }: _(SystemOrigin::Signed(caller.clone()), path.clone(), 100.into(), (1000 * ed).into(), caller.clone(), false) - verify { - if !T::AllowMultiAssetPools::get() { - let new_asset2_balance = T::Assets::balance(T::BenchmarkHelper::asset_id(2), &caller); - assert_eq!(new_asset2_balance, asset2_balance + 100.into()); - } else { - let new_asset3_balance = T::Assets::balance(T::BenchmarkHelper::asset_id(3), &caller); - assert_eq!(new_asset3_balance, asset3_balance + 100.into()); - } + )); + + let (_, _, _) = create_asset_and_pool::(&asset3, &asset4); + assert_ok!(AssetConversion::::add_liquidity( + SystemOrigin::Signed(caller.clone()).into(), + Box::new(asset3.clone()), + Box::new(asset4.clone()), + 2000.into(), + 2000.into(), + 1.into(), + 0.into(), + caller.clone(), + )); + + let path = vec![ + Box::new(asset1.clone()), + Box::new(asset2.clone()), + Box::new(asset3.clone()), + Box::new(asset4.clone()), + ]; + + let asset4_balance = T::Assets::balance(asset4.clone(), &caller); + + #[extrinsic_call] + _( + SystemOrigin::Signed(caller.clone()), + path.clone(), + 100.into(), + (1000 * ed).into(), + caller.clone(), + false, + ); + + let new_asset4_balance = T::Assets::balance(asset4, &caller); + assert_eq!(new_asset4_balance, asset4_balance + 100.into()); } impl_benchmark_test_suite!(AssetConversion, crate::mock::new_test_ext(), crate::mock::Test); diff --git a/substrate/frame/asset-conversion/src/lib.rs b/substrate/frame/asset-conversion/src/lib.rs index e25725157c32..6cc73452b250 100644 --- a/substrate/frame/asset-conversion/src/lib.rs +++ b/substrate/frame/asset-conversion/src/lib.rs @@ -63,7 +63,8 @@ mod swap; mod tests; mod types; pub mod weights; - +#[cfg(feature = "runtime-benchmarks")] +pub use benchmarking::{BenchmarkHelper, NativeOrWithIdFactory}; pub use pallet::*; pub use swap::*; pub use types::*; @@ -73,18 +74,14 @@ use codec::Codec; use frame_support::{ storage::{with_storage_layer, with_transaction}, traits::{ - fungible::{ - Balanced as BalancedFungible, Credit as CreditFungible, Inspect as InspectFungible, - Mutate as MutateFungible, - }, - fungibles::{Balanced, Create, Credit as CreditFungibles, Inspect, Mutate}, + fungibles::{Balanced, Create, Credit, Inspect, Mutate}, tokens::{ AssetId, Balance, Fortitude::Polite, Precision::Exact, Preservation::{Expendable, Preserve}, }, - AccountTouch, ContainsPair, Imbalance, Incrementable, + AccountTouch, ContainsPair, Incrementable, OnUnbalanced, }, PalletId, }; @@ -94,7 +91,7 @@ use sp_runtime::{ CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Ensure, IntegerSquareRoot, MaybeDisplay, One, TrailingZeroInput, Zero, }, - DispatchError, RuntimeDebug, Saturating, TokenError, TransactionOutcome, + DispatchError, Saturating, TokenError, TransactionOutcome, }; use sp_std::{boxed::Box, collections::btree_set::BTreeSet, vec::Vec}; @@ -113,11 +110,6 @@ pub mod pallet { /// Overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; - /// Currency type that this works on. - type Currency: InspectFungible - + MutateFungible - + BalancedFungible; - /// The type in which the assets for swapping are measured. type Balance: Balance; @@ -130,30 +122,28 @@ pub mod pallet { + From + TryInto; - /// Identifier for the class of non-native asset. - /// Note: A `From` bound here would prevent `MultiLocation` from being used as an - /// `AssetId`. - type AssetId: AssetId; + /// Type of asset class, sourced from [`Config::Assets`], utilized to offer liquidity to a + /// pool. + type AssetKind: Parameter + MaxEncodedLen; - /// Type that identifies either the native currency or a token class from `Assets`. - /// `Ord` is added because of `get_pool_id`. - /// - /// The pool's `AccountId` is derived from this type. Any changes to the type may - /// necessitate a migration. - type MultiAssetId: AssetId + Ord + From; + /// Registry of assets utilized for providing liquidity to pools. + type Assets: Inspect + + Mutate + + AccountTouch + + ContainsPair + + Balanced; - /// Type to convert an `AssetId` into `MultiAssetId`. - type MultiAssetIdConverter: MultiAssetIdConverter; + /// Liquidity pool identifier. + type PoolId: Parameter + MaxEncodedLen + Ord; - /// `AssetId` to address the lp tokens by. - type PoolAssetId: AssetId + PartialOrd + Incrementable + From; + /// Provides means to resolve the [`Config::PoolId`] and it's `AccountId` from a pair + /// of [`Config::AssetKind`]s. + /// + /// Examples: [`crate::types::WithFirstAsset`], [`crate::types::Ascending`]. + type PoolLocator: PoolLocator; - /// Registry for the assets. - type Assets: Inspect - + Mutate - + AccountTouch - + ContainsPair - + Balanced; + /// Asset class for the lp tokens from [`Self::PoolAssets`]. + type PoolAssetId: AssetId + PartialOrd + Incrementable + From; /// Registry for the lp tokens. Ideally only this pallet should have create permissions on /// the assets. @@ -170,8 +160,12 @@ pub mod pallet { #[pallet::constant] type PoolSetupFee: Get; - /// An account that receives the pool setup fee. - type PoolSetupFeeReceiver: Get; + /// Asset class from [`Config::Assets`] used to pay the [`Config::PoolSetupFee`]. + #[pallet::constant] + type PoolSetupFeeAsset: Get; + + /// Handler for the [`Config::PoolSetupFee`]. + type PoolSetupFeeTarget: OnUnbalanced>; /// A fee to withdraw the liquidity. #[pallet::constant] @@ -189,23 +183,19 @@ pub mod pallet { #[pallet::constant] type PalletId: Get; - /// A setting to allow creating pools with both non-native assets. - #[pallet::constant] - type AllowMultiAssetPools: Get; - /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; /// The benchmarks need a way to create asset ids from u32s. #[cfg(feature = "runtime-benchmarks")] - type BenchmarkHelper: BenchmarkHelper; + type BenchmarkHelper: BenchmarkHelper; } /// Map from `PoolAssetId` to `PoolInfo`. This establishes whether a pool has been officially /// created rather than people sending tokens directly to a pool's public account. #[pallet::storage] pub type Pools = - StorageMap<_, Blake2_128Concat, PoolIdOf, PoolInfo, OptionQuery>; + StorageMap<_, Blake2_128Concat, T::PoolId, PoolInfo, OptionQuery>; /// Stores the `PoolAssetId` that is going to be used for the next lp token. /// This gets incremented whenever a new lp pool is created. @@ -222,7 +212,7 @@ pub mod pallet { creator: T::AccountId, /// The pool id associated with the pool. Note that the order of the assets may not be /// the same as the order specified in the create pool extrinsic. - pool_id: PoolIdOf, + pool_id: T::PoolId, /// The account ID of the pool. pool_account: T::AccountId, /// The id of the liquidity tokens that will be minted when assets are added to this @@ -237,7 +227,7 @@ pub mod pallet { /// The account that the liquidity tokens were minted to. mint_to: T::AccountId, /// The pool id of the pool that the liquidity was added to. - pool_id: PoolIdOf, + pool_id: T::PoolId, /// The amount of the first asset that was added to the pool. amount1_provided: T::Balance, /// The amount of the second asset that was added to the pool. @@ -255,7 +245,7 @@ pub mod pallet { /// The account that the assets were transferred to. withdraw_to: T::AccountId, /// The pool id that the liquidity was removed from. - pool_id: PoolIdOf, + pool_id: T::PoolId, /// The amount of the first asset that was removed from the pool. amount1: T::Balance, /// The amount of the second asset that was removed from the pool. @@ -296,10 +286,8 @@ pub mod pallet { #[pallet::error] pub enum Error { - /// Provided assets are equal. - EqualAssets, - /// Provided asset is not supported for pool. - UnsupportedAsset, + /// Provided asset pair is not supported for pool. + InvalidAssetPair, /// Pool already exists. PoolExists, /// Desired amount can't be zero. @@ -335,18 +323,12 @@ pub mod pallet { ZeroLiquidity, /// Amount can't be zero. ZeroAmount, - /// Insufficient liquidity in the pool. - InsufficientLiquidity, /// Calculated amount out is less than provided minimum amount. ProvidedMinimumNotSufficientForSwap, /// Provided maximum amount is not sufficient for swap. ProvidedMaximumNotSufficientForSwap, - /// Only pools with native on one side are valid. - PoolMustContainNativeCurrency, /// The provided path must consists of 2 assets at least. InvalidPath, - /// It was not possible to calculate path data. - PathError, /// The provided path must consists of unique assets. NonUniquePath, /// It was not possible to get or increment the Id of the pool. @@ -376,48 +358,33 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::create_pool())] pub fn create_pool( origin: OriginFor, - asset1: Box, - asset2: Box, + asset1: Box, + asset2: Box, ) -> DispatchResult { let sender = ensure_signed(origin)?; - ensure!(asset1 != asset2, Error::::EqualAssets); + ensure!(asset1 != asset2, Error::::InvalidAssetPair); // prepare pool_id - let pool_id = Self::get_pool_id(*asset1, *asset2); + let pool_id = T::PoolLocator::pool_id(&asset1, &asset2) + .map_err(|_| Error::::InvalidAssetPair)?; ensure!(!Pools::::contains_key(&pool_id), Error::::PoolExists); - let (asset1, asset2) = &pool_id; - if !T::AllowMultiAssetPools::get() && !T::MultiAssetIdConverter::is_native(asset1) { - Err(Error::::PoolMustContainNativeCurrency)?; - } - let pool_account = Self::get_pool_account(&pool_id); + let pool_account = + T::PoolLocator::address(&pool_id).map_err(|_| Error::::InvalidAssetPair)?; frame_system::Pallet::::inc_providers(&pool_account); // pay the setup fee - T::Currency::transfer( - &sender, - &T::PoolSetupFeeReceiver::get(), - T::PoolSetupFee::get(), - Preserve, - )?; + let fee = + Self::withdraw(T::PoolSetupFeeAsset::get(), &sender, T::PoolSetupFee::get(), true)?; + T::PoolSetupFeeTarget::on_unbalanced(fee); - // try to convert both assets - match T::MultiAssetIdConverter::try_convert(asset1) { - MultiAssetIdConversionResult::Converted(asset) => - if !T::Assets::contains(&asset, &pool_account) { - T::Assets::touch(asset, pool_account.clone(), sender.clone())? - }, - MultiAssetIdConversionResult::Unsupported(_) => Err(Error::::UnsupportedAsset)?, - MultiAssetIdConversionResult::Native => (), - } - match T::MultiAssetIdConverter::try_convert(asset2) { - MultiAssetIdConversionResult::Converted(asset) => - if !T::Assets::contains(&asset, &pool_account) { - T::Assets::touch(asset, pool_account.clone(), sender.clone())? - }, - MultiAssetIdConversionResult::Unsupported(_) => Err(Error::::UnsupportedAsset)?, - MultiAssetIdConversionResult::Native => (), - } + if !T::Assets::contains(&asset1, &pool_account) { + T::Assets::touch(*asset1, pool_account.clone(), sender.clone())? + }; + + if !T::Assets::contains(&asset2, &pool_account) { + T::Assets::touch(*asset2, pool_account.clone(), sender.clone())? + }; let lp_token = NextPoolAssetId::::get() .or(T::PoolAssetId::initial_value()) @@ -454,8 +421,8 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::add_liquidity())] pub fn add_liquidity( origin: OriginFor, - asset1: Box, - asset2: Box, + asset1: Box, + asset2: Box, amount1_desired: T::Balance, amount2_desired: T::Balance, amount1_min: T::Balance, @@ -464,26 +431,20 @@ pub mod pallet { ) -> DispatchResult { let sender = ensure_signed(origin)?; - let pool_id = Self::get_pool_id(*asset1.clone(), *asset2); - // swap params if needed - let (amount1_desired, amount2_desired, amount1_min, amount2_min) = - if pool_id.0 == *asset1 { - (amount1_desired, amount2_desired, amount1_min, amount2_min) - } else { - (amount2_desired, amount1_desired, amount2_min, amount1_min) - }; + let pool_id = T::PoolLocator::pool_id(&asset1, &asset2) + .map_err(|_| Error::::InvalidAssetPair)?; + ensure!( amount1_desired > Zero::zero() && amount2_desired > Zero::zero(), Error::::WrongDesiredAmount ); - let maybe_pool = Pools::::get(&pool_id); - let pool = maybe_pool.as_ref().ok_or(Error::::PoolNotFound)?; - let pool_account = Self::get_pool_account(&pool_id); + let pool = Pools::::get(&pool_id).ok_or(Error::::PoolNotFound)?; + let pool_account = + T::PoolLocator::address(&pool_id).map_err(|_| Error::::InvalidAssetPair)?; - let (asset1, asset2) = &pool_id; - let reserve1 = Self::get_balance(&pool_account, asset1)?; - let reserve2 = Self::get_balance(&pool_account, asset2)?; + let reserve1 = Self::get_balance(&pool_account, *asset1.clone()); + let reserve2 = Self::get_balance(&pool_account, *asset2.clone()); let amount1: T::Balance; let amount2: T::Balance; @@ -515,13 +476,17 @@ pub mod pallet { } } - Self::validate_minimal_amount(amount1.saturating_add(reserve1), asset1) - .map_err(|_| Error::::AmountOneLessThanMinimal)?; - Self::validate_minimal_amount(amount2.saturating_add(reserve2), asset2) - .map_err(|_| Error::::AmountTwoLessThanMinimal)?; + ensure!( + amount1.saturating_add(reserve1) >= T::Assets::minimum_balance(*asset1.clone()), + Error::::AmountOneLessThanMinimal + ); + ensure!( + amount2.saturating_add(reserve2) >= T::Assets::minimum_balance(*asset2.clone()), + Error::::AmountTwoLessThanMinimal + ); - Self::transfer(asset1, &sender, &pool_account, amount1, true)?; - Self::transfer(asset2, &sender, &pool_account, amount2, true)?; + T::Assets::transfer(*asset1, &sender, &pool_account, amount1, Preserve)?; + T::Assets::transfer(*asset2, &sender, &pool_account, amount2, Preserve)?; let total_supply = T::PoolAssets::total_issuance(pool.lp_token.clone()); @@ -552,7 +517,7 @@ pub mod pallet { pool_id, amount1_provided: amount1, amount2_provided: amount2, - lp_token: pool.lp_token.clone(), + lp_token: pool.lp_token, lp_token_minted: lp_token_amount, }); @@ -566,8 +531,8 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::remove_liquidity())] pub fn remove_liquidity( origin: OriginFor, - asset1: Box, - asset2: Box, + asset1: Box, + asset2: Box, lp_token_burn: T::Balance, amount1_min_receive: T::Balance, amount2_min_receive: T::Balance, @@ -575,23 +540,17 @@ pub mod pallet { ) -> DispatchResult { let sender = ensure_signed(origin)?; - let pool_id = Self::get_pool_id(*asset1.clone(), *asset2); - // swap params if needed - let (amount1_min_receive, amount2_min_receive) = if pool_id.0 == *asset1 { - (amount1_min_receive, amount2_min_receive) - } else { - (amount2_min_receive, amount1_min_receive) - }; - let (asset1, asset2) = pool_id.clone(); + let pool_id = T::PoolLocator::pool_id(&asset1, &asset2) + .map_err(|_| Error::::InvalidAssetPair)?; ensure!(lp_token_burn > Zero::zero(), Error::::ZeroLiquidity); - let maybe_pool = Pools::::get(&pool_id); - let pool = maybe_pool.as_ref().ok_or(Error::::PoolNotFound)?; + let pool = Pools::::get(&pool_id).ok_or(Error::::PoolNotFound)?; - let pool_account = Self::get_pool_account(&pool_id); - let reserve1 = Self::get_balance(&pool_account, &asset1)?; - let reserve2 = Self::get_balance(&pool_account, &asset2)?; + let pool_account = + T::PoolLocator::address(&pool_id).map_err(|_| Error::::InvalidAssetPair)?; + let reserve1 = Self::get_balance(&pool_account, *asset1.clone()); + let reserve2 = Self::get_balance(&pool_account, *asset2.clone()); let total_supply = T::PoolAssets::total_issuance(pool.lp_token.clone()); let withdrawal_fee_amount = T::LiquidityWithdrawalFee::get() * lp_token_burn; @@ -610,16 +569,20 @@ pub mod pallet { ); let reserve1_left = reserve1.saturating_sub(amount1); let reserve2_left = reserve2.saturating_sub(amount2); - Self::validate_minimal_amount(reserve1_left, &asset1) - .map_err(|_| Error::::ReserveLeftLessThanMinimal)?; - Self::validate_minimal_amount(reserve2_left, &asset2) - .map_err(|_| Error::::ReserveLeftLessThanMinimal)?; + ensure!( + reserve1_left >= T::Assets::minimum_balance(*asset1.clone()), + Error::::ReserveLeftLessThanMinimal + ); + ensure!( + reserve2_left >= T::Assets::minimum_balance(*asset2.clone()), + Error::::ReserveLeftLessThanMinimal + ); // burn the provided lp token amount that includes the fee T::PoolAssets::burn_from(pool.lp_token.clone(), &sender, lp_token_burn, Exact, Polite)?; - Self::transfer(&asset1, &pool_account, &withdraw_to, amount1, false)?; - Self::transfer(&asset2, &pool_account, &withdraw_to, amount2, false)?; + T::Assets::transfer(*asset1, &pool_account, &withdraw_to, amount1, Expendable)?; + T::Assets::transfer(*asset2, &pool_account, &withdraw_to, amount2, Expendable)?; Self::deposit_event(Event::LiquidityRemoved { who: sender, @@ -627,7 +590,7 @@ pub mod pallet { pool_id, amount1, amount2, - lp_token: pool.lp_token.clone(), + lp_token: pool.lp_token, lp_token_burned: lp_token_burn, withdrawal_fee: T::LiquidityWithdrawalFee::get(), }); @@ -645,7 +608,7 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::swap_exact_tokens_for_tokens())] pub fn swap_exact_tokens_for_tokens( origin: OriginFor, - path: Vec>, + path: Vec>, amount_in: T::Balance, amount_out_min: T::Balance, send_to: T::AccountId, @@ -673,7 +636,7 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::swap_tokens_for_exact_tokens())] pub fn swap_tokens_for_exact_tokens( origin: OriginFor, - path: Vec>, + path: Vec>, amount_out: T::Balance, amount_in_max: T::Balance, send_to: T::AccountId, @@ -707,7 +670,7 @@ pub mod pallet { /// rollback. pub(crate) fn do_swap_exact_tokens_for_tokens( sender: T::AccountId, - path: Vec, + path: Vec, amount_in: T::Balance, amount_out_min: Option, send_to: T::AccountId, @@ -755,7 +718,7 @@ pub mod pallet { /// rollback. pub(crate) fn do_swap_tokens_for_exact_tokens( sender: T::AccountId, - path: Vec, + path: Vec, amount_out: T::Balance, amount_in_max: Option, send_to: T::AccountId, @@ -801,10 +764,10 @@ pub mod pallet { /// only inside a transactional storage context and an Err result must imply a storage /// rollback. pub(crate) fn do_swap_exact_credit_tokens_for_tokens( - path: Vec, - credit_in: Credit, + path: Vec, + credit_in: CreditOf, amount_out_min: Option, - ) -> Result, (Credit, DispatchError)> { + ) -> Result, (CreditOf, DispatchError)> { let amount_in = credit_in.peek(); let inspect_path = |credit_asset| { ensure!(path.get(0).map_or(false, |a| *a == credit_asset), Error::::InvalidPath); @@ -846,10 +809,10 @@ pub mod pallet { /// only inside a transactional storage context and an Err result must imply a storage /// rollback. pub(crate) fn do_swap_credit_tokens_for_exact_tokens( - path: Vec, - credit_in: Credit, + path: Vec, + credit_in: CreditOf, amount_out: T::Balance, - ) -> Result<(Credit, Credit), (Credit, DispatchError)> { + ) -> Result<(CreditOf, CreditOf), (CreditOf, DispatchError)> { let amount_in_max = credit_in.peek(); let inspect_path = |credit_asset| { ensure!(path.get(0).map_or(false, |a| a == &credit_asset), Error::::InvalidPath); @@ -880,75 +843,6 @@ pub mod pallet { Ok((credit_out, credit_change)) } - /// Transfer an `amount` of `asset_id`, respecting the `keep_alive` requirements. - fn transfer( - asset_id: &T::MultiAssetId, - from: &T::AccountId, - to: &T::AccountId, - amount: T::Balance, - keep_alive: bool, - ) -> Result { - let preservation = match keep_alive { - true => Preserve, - false => Expendable, - }; - match T::MultiAssetIdConverter::try_convert(asset_id) { - MultiAssetIdConversionResult::Converted(asset_id) => - T::Assets::transfer(asset_id, from, to, amount, preservation), - MultiAssetIdConversionResult::Native => - Ok(T::Currency::transfer(from, to, amount, preservation)?), - MultiAssetIdConversionResult::Unsupported(_) => - Err(Error::::UnsupportedAsset.into()), - } - } - - /// The balance of `who` is increased in order to counter `credit`. If the whole of `credit` - /// cannot be countered, then nothing is changed and the original `credit` is returned in an - /// `Err`. - fn resolve(who: &T::AccountId, credit: Credit) -> Result<(), Credit> { - match credit { - Credit::Native(c) => T::Currency::resolve(who, c).map_err(|c| c.into()), - Credit::Asset(c) => T::Assets::resolve(who, c).map_err(|c| c.into()), - } - } - - /// Removes `value` balance of `asset` from `who` account if possible. - fn withdraw( - asset: &T::MultiAssetId, - who: &T::AccountId, - value: T::Balance, - keep_alive: bool, - ) -> Result, DispatchError> { - let preservation = match keep_alive { - true => Preserve, - false => Expendable, - }; - match T::MultiAssetIdConverter::try_convert(asset) { - MultiAssetIdConversionResult::Converted(asset) => { - if preservation == Preserve { - // TODO drop the ensure! when this issue addressed - // https://github.com/paritytech/polkadot-sdk/issues/1698 - let free = - T::Assets::reducible_balance(asset.clone(), who, preservation, Polite); - ensure!(free >= value, TokenError::NotExpendable); - } - T::Assets::withdraw(asset, who, value, Exact, preservation, Polite) - .map(|c| c.into()) - }, - MultiAssetIdConversionResult::Native => { - if preservation == Preserve { - // TODO drop the ensure! when this issue addressed - // https://github.com/paritytech/polkadot-sdk/issues/1698 - let free = T::Currency::reducible_balance(who, preservation, Polite); - ensure!(free >= value, TokenError::NotExpendable); - } - T::Currency::withdraw(who, value, Exact, preservation, Polite).map(|c| c.into()) - }, - MultiAssetIdConversionResult::Unsupported(_) => - Err(Error::::UnsupportedAsset.into()), - } - } - /// Swap assets along the `path`, withdrawing from `sender` and depositing in `send_to`. /// /// Note: It's assumed that the provided `path` is valid. @@ -963,10 +857,10 @@ pub mod pallet { keep_alive: bool, ) -> Result<(), DispatchError> { let (asset_in, amount_in) = path.first().ok_or(Error::::InvalidPath)?; - let credit_in = Self::withdraw(asset_in, sender, *amount_in, keep_alive)?; + let credit_in = Self::withdraw(asset_in.clone(), sender, *amount_in, keep_alive)?; let credit_out = Self::credit_swap(credit_in, path).map_err(|(_, e)| e)?; - Self::resolve(send_to, credit_out).map_err(|_| Error::::BelowMinimum)?; + T::Assets::resolve(send_to, credit_out).map_err(|_| Error::::BelowMinimum)?; Ok(()) } @@ -983,24 +877,29 @@ pub mod pallet { /// only inside a transactional storage context and an Err result must imply a storage /// rollback. fn credit_swap( - credit_in: Credit, + credit_in: CreditOf, path: &BalancePath, - ) -> Result, (Credit, DispatchError)> { - let resolve_path = || -> Result, DispatchError> { + ) -> Result, (CreditOf, DispatchError)> { + let resolve_path = || -> Result, DispatchError> { for pos in 0..=path.len() { if let Some([(asset1, _), (asset2, amount_out)]) = path.get(pos..=pos + 1) { - let pool_from = Self::get_pool_account(&Self::get_pool_id( - asset1.clone(), - asset2.clone(), - )); + let pool_from = T::PoolLocator::pool_address(&asset1, &asset2) + .map_err(|_| Error::::InvalidAssetPair)?; + if let Some((asset3, _)) = path.get(pos + 2) { - let pool_to = Self::get_pool_account(&Self::get_pool_id( + let pool_to = T::PoolLocator::pool_address(&asset2, &asset3) + .map_err(|_| Error::::InvalidAssetPair)?; + + T::Assets::transfer( asset2.clone(), - asset3.clone(), - )); - Self::transfer(asset2, &pool_from, &pool_to, *amount_out, true)?; + &pool_from, + &pool_to, + *amount_out, + Preserve, + )?; } else { - let credit_out = Self::withdraw(asset2, &pool_from, *amount_out, true)?; + let credit_out = + Self::withdraw(asset2.clone(), &pool_from, *amount_out, true)?; return Ok(credit_out) } } @@ -1014,79 +913,57 @@ pub mod pallet { }; let pool_to = if let Some([(asset1, _), (asset2, _)]) = path.get(0..2) { - Self::get_pool_account(&Self::get_pool_id(asset1.clone(), asset2.clone())) + match T::PoolLocator::pool_address(&asset1, &asset2) { + Ok(address) => address, + Err(_) => return Err((credit_in, Error::::InvalidAssetPair.into())), + } } else { return Err((credit_in, Error::::InvalidPath.into())) }; - Self::resolve(&pool_to, credit_in).map_err(|c| (c, Error::::BelowMinimum.into()))?; + T::Assets::resolve(&pool_to, credit_in) + .map_err(|c| (c, Error::::BelowMinimum.into()))?; Ok(credit_out) } - /// The account ID of the pool. - /// - /// This actually does computation. If you need to keep using it, then make sure you cache - /// the value and only call this once. - pub fn get_pool_account(pool_id: &PoolIdOf) -> T::AccountId { - let encoded_pool_id = sp_io::hashing::blake2_256(&Encode::encode(pool_id)[..]); - - Decode::decode(&mut TrailingZeroInput::new(encoded_pool_id.as_ref())) - .expect("infinite length input; no invalid inputs for type; qed") + /// Removes `value` balance of `asset` from `who` account if possible. + fn withdraw( + asset: T::AssetKind, + who: &T::AccountId, + value: T::Balance, + keep_alive: bool, + ) -> Result, DispatchError> { + let preservation = match keep_alive { + true => Preserve, + false => Expendable, + }; + if preservation == Preserve { + // TODO drop the ensure! when this issue addressed + // https://github.com/paritytech/polkadot-sdk/issues/1698 + let free = T::Assets::reducible_balance(asset.clone(), who, preservation, Polite); + ensure!(free >= value, TokenError::NotExpendable); + } + T::Assets::withdraw(asset, who, value, Exact, preservation, Polite) } /// Get the `owner`'s balance of `asset`, which could be the chain's native asset or another /// fungible. Returns a value in the form of an `Balance`. - fn get_balance( - owner: &T::AccountId, - asset: &T::MultiAssetId, - ) -> Result> { - match T::MultiAssetIdConverter::try_convert(asset) { - MultiAssetIdConversionResult::Converted(asset_id) => Ok( - <::Assets>::reducible_balance(asset_id, owner, Expendable, Polite), - ), - MultiAssetIdConversionResult::Native => - Ok(<::Currency>::reducible_balance(owner, Expendable, Polite)), - MultiAssetIdConversionResult::Unsupported(_) => - Err(Error::::UnsupportedAsset.into()), - } - } - - /// Returns a pool id constructed from 2 assets. - /// 1. Native asset should be lower than the other asset ids. - /// 2. Two native or two non-native assets are compared by their `Ord` implementation. - /// - /// We expect deterministic order, so (asset1, asset2) or (asset2, asset1) returns the same - /// result. - pub fn get_pool_id(asset1: T::MultiAssetId, asset2: T::MultiAssetId) -> PoolIdOf { - match ( - T::MultiAssetIdConverter::is_native(&asset1), - T::MultiAssetIdConverter::is_native(&asset2), - ) { - (true, false) => return (asset1, asset2), - (false, true) => return (asset2, asset1), - _ => { - // else we want to be deterministic based on `Ord` implementation - if asset1 <= asset2 { - (asset1, asset2) - } else { - (asset2, asset1) - } - }, - } + fn get_balance(owner: &T::AccountId, asset: T::AssetKind) -> T::Balance { + T::Assets::reducible_balance(asset, owner, Expendable, Polite) } /// Returns the balance of each asset in the pool. /// The tuple result is in the order requested (not necessarily the same as pool order). pub fn get_reserves( - asset1: &T::MultiAssetId, - asset2: &T::MultiAssetId, + asset1: T::AssetKind, + asset2: T::AssetKind, ) -> Result<(T::Balance, T::Balance), Error> { - let pool_id = Self::get_pool_id(asset1.clone(), asset2.clone()); - let pool_account = Self::get_pool_account(&pool_id); + let pool_account = T::PoolLocator::pool_address(&asset1, &asset2) + .map_err(|_| Error::::InvalidAssetPair)?; - let balance1 = Self::get_balance(&pool_account, asset1)?; - let balance2 = Self::get_balance(&pool_account, asset2)?; + let balance1 = Self::get_balance(&pool_account, asset1); + let balance2 = Self::get_balance(&pool_account, asset2); if balance1.is_zero() || balance2.is_zero() { Err(Error::::PoolNotFound)?; @@ -1098,7 +975,7 @@ pub mod pallet { /// Leading to an amount at the end of a `path`, get the required amounts in. pub(crate) fn balance_path_from_amount_out( amount_out: T::Balance, - path: Vec, + path: Vec, ) -> Result, DispatchError> { let mut balance_path: BalancePath = Vec::with_capacity(path.len()); let mut amount_in: T::Balance = amount_out; @@ -1112,7 +989,7 @@ pub mod pallet { break }, }; - let (reserve_in, reserve_out) = Self::get_reserves(asset1, &asset2)?; + let (reserve_in, reserve_out) = Self::get_reserves(asset1.clone(), asset2.clone())?; balance_path.push((asset2, amount_in)); amount_in = Self::get_amount_in(&amount_in, &reserve_in, &reserve_out)?; } @@ -1124,7 +1001,7 @@ pub mod pallet { /// Following an amount into a `path`, get the corresponding amounts out. pub(crate) fn balance_path_from_amount_in( amount_in: T::Balance, - path: Vec, + path: Vec, ) -> Result, DispatchError> { let mut balance_path: BalancePath = Vec::with_capacity(path.len()); let mut amount_out: T::Balance = amount_in; @@ -1138,7 +1015,7 @@ pub mod pallet { break }, }; - let (reserve_in, reserve_out) = Self::get_reserves(&asset1, asset2)?; + let (reserve_in, reserve_out) = Self::get_reserves(asset1.clone(), asset2.clone())?; balance_path.push((asset1, amount_out)); amount_out = Self::get_amount_out(&amount_out, &reserve_in, &reserve_out)?; } @@ -1147,16 +1024,15 @@ pub mod pallet { /// Used by the RPC service to provide current prices. pub fn quote_price_exact_tokens_for_tokens( - asset1: T::MultiAssetId, - asset2: T::MultiAssetId, + asset1: T::AssetKind, + asset2: T::AssetKind, amount: T::Balance, include_fee: bool, ) -> Option { - let pool_id = Self::get_pool_id(asset1.clone(), asset2.clone()); - let pool_account = Self::get_pool_account(&pool_id); + let pool_account = T::PoolLocator::pool_address(&asset1, &asset2).ok()?; - let balance1 = Self::get_balance(&pool_account, &asset1).ok()?; - let balance2 = Self::get_balance(&pool_account, &asset2).ok()?; + let balance1 = Self::get_balance(&pool_account, asset1); + let balance2 = Self::get_balance(&pool_account, asset2); if !balance1.is_zero() { if include_fee { Self::get_amount_out(&amount, &balance1, &balance2).ok() @@ -1170,16 +1046,15 @@ pub mod pallet { /// Used by the RPC service to provide current prices. pub fn quote_price_tokens_for_exact_tokens( - asset1: T::MultiAssetId, - asset2: T::MultiAssetId, + asset1: T::AssetKind, + asset2: T::AssetKind, amount: T::Balance, include_fee: bool, ) -> Option { - let pool_id = Self::get_pool_id(asset1.clone(), asset2.clone()); - let pool_account = Self::get_pool_account(&pool_id); + let pool_account = T::PoolLocator::pool_address(&asset1, &asset2).ok()?; - let balance1 = Self::get_balance(&pool_account, &asset1).ok()?; - let balance2 = Self::get_balance(&pool_account, &asset2).ok()?; + let balance1 = Self::get_balance(&pool_account, asset1); + let balance2 = Self::get_balance(&pool_account, asset2); if !balance1.is_zero() { if include_fee { Self::get_amount_in(&amount, &balance1, &balance2).ok() @@ -1309,36 +1184,18 @@ pub mod pallet { result.try_into().map_err(|_| Error::::Overflow) } - /// Ensure that a `value` meets the minimum balance requirements of an `asset` class. - fn validate_minimal_amount(value: T::Balance, asset: &T::MultiAssetId) -> Result<(), ()> { - if T::MultiAssetIdConverter::is_native(asset) { - let ed = T::Currency::minimum_balance(); - ensure!( - T::HigherPrecisionBalance::from(value) >= T::HigherPrecisionBalance::from(ed), - () - ); - } else { - let MultiAssetIdConversionResult::Converted(asset_id) = - T::MultiAssetIdConverter::try_convert(asset) - else { - return Err(()) - }; - let minimal = T::Assets::minimum_balance(asset_id); - ensure!(value >= minimal, ()); - } - Ok(()) - } - /// Ensure that a path is valid. - fn validate_swap_path(path: &Vec) -> Result<(), DispatchError> { + fn validate_swap_path(path: &Vec) -> Result<(), DispatchError> { ensure!(path.len() >= 2, Error::::InvalidPath); ensure!(path.len() as u32 <= T::MaxSwapPathLength::get(), Error::::InvalidPath); // validate all the pools in the path are unique - let mut pools = BTreeSet::>::new(); + let mut pools = BTreeSet::::new(); for assets_pair in path.windows(2) { if let [asset1, asset2] = assets_pair { - let pool_id = Self::get_pool_id(asset1.clone(), asset2.clone()); + let pool_id = T::PoolLocator::pool_id(&asset1, &asset2) + .map_err(|_| Error::::InvalidAssetPair)?; + let new_element = pools.insert(pool_id); if !new_element { return Err(Error::::NonUniquePath.into()) @@ -1361,21 +1218,32 @@ pub mod pallet { sp_api::decl_runtime_apis! { /// This runtime api allows people to query the size of the liquidity pools /// and quote prices for swaps. - pub trait AssetConversionApi where + pub trait AssetConversionApi + where Balance: frame_support::traits::tokens::Balance + MaybeDisplay, - AssetId: Codec + AssetId: Codec, { /// Provides a quote for [`Pallet::swap_tokens_for_exact_tokens`]. /// /// Note that the price may have changed by the time the transaction is executed. /// (Use `amount_in_max` to control slippage.) - fn quote_price_tokens_for_exact_tokens(asset1: AssetId, asset2: AssetId, amount: Balance, include_fee: bool) -> Option; + fn quote_price_tokens_for_exact_tokens( + asset1: AssetId, + asset2: AssetId, + amount: Balance, + include_fee: bool, + ) -> Option; /// Provides a quote for [`Pallet::swap_exact_tokens_for_tokens`]. /// /// Note that the price may have changed by the time the transaction is executed. /// (Use `amount_out_min` to control slippage.) - fn quote_price_exact_tokens_for_tokens(asset1: AssetId, asset2: AssetId, amount: Balance, include_fee: bool) -> Option; + fn quote_price_exact_tokens_for_tokens( + asset1: AssetId, + asset2: AssetId, + amount: Balance, + include_fee: bool, + ) -> Option; /// Returns the size of the liquidity pool for the given asset pair. fn get_reserves(asset1: AssetId, asset2: AssetId) -> Option<(Balance, Balance)>; diff --git a/substrate/frame/asset-conversion/src/mock.rs b/substrate/frame/asset-conversion/src/mock.rs index 35d5182a3db2..05de1b3d90f7 100644 --- a/substrate/frame/asset-conversion/src/mock.rs +++ b/substrate/frame/asset-conversion/src/mock.rs @@ -19,12 +19,17 @@ use super::*; use crate as pallet_asset_conversion; - use frame_support::{ construct_runtime, instances::{Instance1, Instance2}, ord_parameter_types, parameter_types, - traits::{AsEnsureOriginWithArg, ConstU128, ConstU32, ConstU64}, + traits::{ + tokens::{ + fungible::{NativeFromLeft, NativeOrWithId, UnionOf}, + imbalance::ResolveAssetTo, + }, + AsEnsureOriginWithArg, ConstU128, ConstU32, ConstU64, + }, PalletId, }; use frame_system::{EnsureSigned, EnsureSignedBy}; @@ -34,6 +39,7 @@ use sp_runtime::{ traits::{AccountIdConversion, BlakeTwo256, IdentityLookup}, BuildStorage, }; +use sp_std::default::Default; type Block = frame_system::mocking::MockBlock; @@ -142,39 +148,39 @@ impl pallet_assets::Config for Test { parameter_types! { pub const AssetConversionPalletId: PalletId = PalletId(*b"py/ascon"); - pub storage AllowMultiAssetPools: bool = true; - pub storage LiquidityWithdrawalFee: Permill = Permill::from_percent(0); // should be non-zero if AllowMultiAssetPools is true, otherwise can be zero + pub const Native: NativeOrWithId = NativeOrWithId::Native; + pub storage LiquidityWithdrawalFee: Permill = Permill::from_percent(0); } ord_parameter_types! { pub const AssetConversionOrigin: u128 = AccountIdConversion::::into_account_truncating(&AssetConversionPalletId::get()); } +pub type NativeAndAssets = UnionOf, u128>; +pub type AscendingLocator = Ascending>; +pub type WithFirstAssetLocator = WithFirstAsset>; + impl Config for Test { type RuntimeEvent = RuntimeEvent; - type Currency = Balances; - type AssetId = u32; + type Balance = ::Balance; + type HigherPrecisionBalance = sp_core::U256; + type AssetKind = NativeOrWithId; + type Assets = NativeAndAssets; + type PoolId = (Self::AssetKind, Self::AssetKind); + type PoolLocator = Chain; type PoolAssetId = u32; - type Assets = Assets; type PoolAssets = PoolAssets; + type PoolSetupFee = ConstU128<100>; // should be more or equal to the existential deposit + type PoolSetupFeeAsset = Native; + type PoolSetupFeeTarget = ResolveAssetTo; type PalletId = AssetConversionPalletId; type WeightInfo = (); type LPFee = ConstU32<3>; // means 0.3% - type PoolSetupFee = ConstU128<100>; // should be more or equal to the existential deposit - type PoolSetupFeeReceiver = AssetConversionOrigin; type LiquidityWithdrawalFee = LiquidityWithdrawalFee; - type AllowMultiAssetPools = AllowMultiAssetPools; type MaxSwapPathLength = ConstU32<4>; type MintMinLiquidity = ConstU128<100>; // 100 is good enough when the main currency has 12 decimals. - - type Balance = ::Balance; - type HigherPrecisionBalance = sp_core::U256; - - type MultiAssetId = NativeOrAssetId; - type MultiAssetIdConverter = NativeOrAssetIdConverter; - #[cfg(feature = "runtime-benchmarks")] - type BenchmarkHelper = (); + type BenchmarkHelper = NativeOrWithIdFactory; } pub(crate) fn new_test_ext() -> sp_io::TestExternalities { diff --git a/substrate/frame/asset-conversion/src/swap.rs b/substrate/frame/asset-conversion/src/swap.rs index 7f59b8f9b804..f4f88e3e0695 100644 --- a/substrate/frame/asset-conversion/src/swap.rs +++ b/substrate/frame/asset-conversion/src/swap.rs @@ -24,7 +24,7 @@ pub trait Swap { /// Measure units of the asset classes for swapping. type Balance: Balance; /// Kind of assets that are going to be swapped. - type MultiAssetId; + type AssetKind; /// Returns the upper limit on the length of the swap path. fn max_path_len() -> u32; @@ -41,7 +41,7 @@ pub trait Swap { /// This operation is expected to be atomic. fn swap_exact_tokens_for_tokens( sender: AccountId, - path: Vec, + path: Vec, amount_in: Self::Balance, amount_out_min: Option, send_to: AccountId, @@ -60,7 +60,7 @@ pub trait Swap { /// This operation is expected to be atomic. fn swap_tokens_for_exact_tokens( sender: AccountId, - path: Vec, + path: Vec, amount_out: Self::Balance, amount_in_max: Option, send_to: AccountId, @@ -73,7 +73,7 @@ pub trait SwapCredit { /// Measure units of the asset classes for swapping. type Balance: Balance; /// Kind of assets that are going to be swapped. - type MultiAssetId; + type AssetKind; /// Credit implying a negative imbalance in the system that can be placed into an account or /// alter the total supply. type Credit; @@ -90,7 +90,7 @@ pub trait SwapCredit { /// /// This operation is expected to be atomic. fn swap_exact_tokens_for_tokens( - path: Vec, + path: Vec, credit_in: Self::Credit, amount_out_min: Option, ) -> Result; @@ -106,7 +106,7 @@ pub trait SwapCredit { /// /// This operation is expected to be atomic. fn swap_tokens_for_exact_tokens( - path: Vec, + path: Vec, credit_in: Self::Credit, amount_out: Self::Balance, ) -> Result<(Self::Credit, Self::Credit), (Self::Credit, DispatchError)>; @@ -114,7 +114,7 @@ pub trait SwapCredit { impl Swap for Pallet { type Balance = T::Balance; - type MultiAssetId = T::MultiAssetId; + type AssetKind = T::AssetKind; fn max_path_len() -> u32 { T::MaxSwapPathLength::get() @@ -122,7 +122,7 @@ impl Swap for Pallet { fn swap_exact_tokens_for_tokens( sender: T::AccountId, - path: Vec, + path: Vec, amount_in: Self::Balance, amount_out_min: Option, send_to: T::AccountId, @@ -143,7 +143,7 @@ impl Swap for Pallet { fn swap_tokens_for_exact_tokens( sender: T::AccountId, - path: Vec, + path: Vec, amount_out: Self::Balance, amount_in_max: Option, send_to: T::AccountId, @@ -165,18 +165,19 @@ impl Swap for Pallet { impl SwapCredit for Pallet { type Balance = T::Balance; - type MultiAssetId = T::MultiAssetId; - type Credit = Credit; + type AssetKind = T::AssetKind; + type Credit = CreditOf; fn max_path_len() -> u32 { T::MaxSwapPathLength::get() } fn swap_exact_tokens_for_tokens( - path: Vec, + path: Vec, credit_in: Self::Credit, amount_out_min: Option, ) -> Result { + let credit_asset = credit_in.asset(); with_transaction(|| -> TransactionOutcome> { let res = Self::do_swap_exact_credit_tokens_for_tokens(path, credit_in, amount_out_min); match &res { @@ -187,14 +188,15 @@ impl SwapCredit for Pallet { } }) // should never map an error since `with_transaction` above never returns it. - .map_err(|_| (Self::Credit::native_zero(), DispatchError::Corruption))? + .map_err(|_| (Self::Credit::zero(credit_asset), DispatchError::Corruption))? } fn swap_tokens_for_exact_tokens( - path: Vec, + path: Vec, credit_in: Self::Credit, amount_out: Self::Balance, ) -> Result<(Self::Credit, Self::Credit), (Self::Credit, DispatchError)> { + let credit_asset = credit_in.asset(); with_transaction(|| -> TransactionOutcome> { let res = Self::do_swap_credit_tokens_for_exact_tokens(path, credit_in, amount_out); match &res { @@ -205,6 +207,6 @@ impl SwapCredit for Pallet { } }) // should never map an error since `with_transaction` above never returns it. - .map_err(|_| (Self::Credit::native_zero(), DispatchError::Corruption))? + .map_err(|_| (Self::Credit::zero(credit_asset), DispatchError::Corruption))? } } diff --git a/substrate/frame/asset-conversion/src/tests.rs b/substrate/frame/asset-conversion/src/tests.rs index db6aca01dece..5700622d426b 100644 --- a/substrate/frame/asset-conversion/src/tests.rs +++ b/substrate/frame/asset-conversion/src/tests.rs @@ -19,7 +19,13 @@ use crate::{mock::*, *}; use frame_support::{ assert_noop, assert_ok, assert_storage_noop, instances::Instance1, - traits::{fungible, fungibles, fungibles::InspectEnumerable, Get}, + traits::{ + fungible, + fungible::{Inspect as FungibleInspect, NativeOrWithId}, + fungibles, + fungibles::{Inspect, InspectEnumerable}, + Get, + }, }; use sp_arithmetic::Permill; use sp_runtime::{DispatchError, TokenError}; @@ -42,18 +48,14 @@ fn events() -> Vec> { result } -fn pools() -> Vec> { +fn pools() -> Vec<::PoolId> { let mut s: Vec<_> = Pools::::iter().map(|x| x.0).collect(); s.sort(); s } -fn assets() -> Vec> { - // if the storage would be public: - // let mut s: Vec<_> = pallet_assets::pallet::Asset::::iter().map(|x| x.0).collect(); - let mut s: Vec<_> = <::Assets>::asset_ids() - .map(|id| NativeOrAssetId::Asset(id)) - .collect(); +fn assets() -> Vec> { + let mut s: Vec<_> = Assets::asset_ids().map(|id| NativeOrWithId::WithId(id)).collect(); s.sort(); s } @@ -64,40 +66,71 @@ fn pool_assets() -> Vec { s } -fn create_tokens(owner: u128, tokens: Vec>) { +fn create_tokens(owner: u128, tokens: Vec>) { create_tokens_with_ed(owner, tokens, 1) } -fn create_tokens_with_ed(owner: u128, tokens: Vec>, ed: u128) { +fn create_tokens_with_ed(owner: u128, tokens: Vec>, ed: u128) { for token_id in tokens { - let MultiAssetIdConversionResult::Converted(asset_id) = - NativeOrAssetIdConverter::try_convert(&token_id) - else { - unreachable!("invalid token") + let asset_id = match token_id { + NativeOrWithId::WithId(id) => id, + _ => unreachable!("invalid token"), }; assert_ok!(Assets::force_create(RuntimeOrigin::root(), asset_id, owner, false, ed)); } } -fn balance(owner: u128, token_id: NativeOrAssetId) -> u128 { - match token_id { - NativeOrAssetId::Native => <::Currency>::free_balance(owner), - NativeOrAssetId::Asset(token_id) => <::Assets>::balance(token_id, owner), - } +fn balance(owner: u128, token_id: NativeOrWithId) -> u128 { + <::Assets>::balance(token_id, &owner) } fn pool_balance(owner: u128, token_id: u32) -> u128 { - <::PoolAssets>::balance(token_id, owner) + <::PoolAssets>::balance(token_id, &owner) } -fn get_ed() -> u128 { - <::Currency>::minimum_balance() +fn get_native_ed() -> u128 { + <::Assets>::minimum_balance(NativeOrWithId::Native) } macro_rules! bvec { - ($( $x:ident ),*) => { + ($($x:expr),+ $(,)?) => ( vec![$( Box::new( $x ), )*] - } + ) +} + +#[test] +fn validate_with_first_asset_pool_id_locator() { + new_test_ext().execute_with(|| { + use NativeOrWithId::{Native, WithId}; + assert_eq!(WithFirstAssetLocator::pool_id(&Native, &WithId(2)), Ok((Native, WithId(2)))); + assert_eq!(WithFirstAssetLocator::pool_id(&WithId(2), &Native), Ok((Native, WithId(2)))); + assert_noop!(WithFirstAssetLocator::pool_id(&Native, &Native), ()); + assert_noop!(WithFirstAssetLocator::pool_id(&WithId(2), &WithId(1)), ()); + }); +} + +#[test] +fn validate_ascending_pool_id_locator() { + new_test_ext().execute_with(|| { + use NativeOrWithId::{Native, WithId}; + assert_eq!(AscendingLocator::pool_id(&Native, &WithId(2)), Ok((Native, WithId(2)))); + assert_eq!(AscendingLocator::pool_id(&WithId(2), &Native), Ok((Native, WithId(2)))); + assert_eq!(AscendingLocator::pool_id(&WithId(2), &WithId(1)), Ok((WithId(1), WithId(2)))); + assert_eq!(AscendingLocator::pool_id(&Native, &Native), Err(())); + assert_eq!(AscendingLocator::pool_id(&WithId(1), &WithId(1)), Err(())); + }); +} + +#[test] +fn validate_native_or_with_id_sorting() { + new_test_ext().execute_with(|| { + use NativeOrWithId::{Native, WithId}; + assert!(WithId(2) > WithId(1)); + assert!(WithId(1) <= WithId(1)); + assert_eq!(WithId(1), WithId(1)); + assert_eq!(Native::, Native::); + assert!(Native < WithId(1)); + }); } #[test] @@ -106,10 +139,11 @@ fn check_pool_accounts_dont_collide() { let mut map = HashSet::new(); for i in 0..1_000_000u32 { - let account = AssetConversion::get_pool_account(&( - NativeOrAssetId::Native, - NativeOrAssetId::Asset(i), - )); + let account: u128 = ::PoolLocator::address(&( + NativeOrWithId::Native, + NativeOrWithId::WithId(i), + )) + .unwrap(); if map.contains(&account) { panic!("Collision at {}", i); } @@ -141,79 +175,67 @@ fn can_create_pool() { let asset_account_deposit: u128 = >::AssetAccountDeposit::get(); let user = 1; - let token_1 = NativeOrAssetId::Native; - let token_2 = NativeOrAssetId::Asset(2); - let pool_id = (token_1, token_2); + let token_1 = NativeOrWithId::Native; + let token_2 = NativeOrWithId::WithId(2); + let pool_id = (token_1.clone(), token_2.clone()); - create_tokens(user, vec![token_2]); + create_tokens(user, vec![token_2.clone()]); let lp_token = AssetConversion::get_next_pool_asset_id(); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 1000)); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(user), - Box::new(token_2), - Box::new(token_1) + Box::new(token_2.clone()), + Box::new(token_1.clone()) )); let setup_fee = <::PoolSetupFee as Get<::Balance>>::get(); - let pool_account = <::PoolSetupFeeReceiver as Get>::get(); + let pool_account = AssetConversionOrigin::get(); assert_eq!( - balance(user, NativeOrAssetId::Native), + balance(user, NativeOrWithId::Native), 1000 - (setup_fee + asset_account_deposit) ); - assert_eq!(balance(pool_account, NativeOrAssetId::Native), setup_fee); + assert_eq!(balance(pool_account, NativeOrWithId::Native), setup_fee); assert_eq!(lp_token + 1, AssetConversion::get_next_pool_asset_id()); assert_eq!( events(), [Event::::PoolCreated { creator: user, - pool_id, - pool_account: AssetConversion::get_pool_account(&pool_id), + pool_id: pool_id.clone(), + pool_account: ::PoolLocator::address(&pool_id).unwrap(), lp_token }] ); assert_eq!(pools(), vec![pool_id]); - assert_eq!(assets(), vec![token_2]); + assert_eq!(assets(), vec![token_2.clone()]); assert_eq!(pool_assets(), vec![lp_token]); assert_noop!( AssetConversion::create_pool( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_1) + Box::new(token_1.clone()), + Box::new(token_1.clone()) ), - Error::::EqualAssets + Error::::InvalidAssetPair ); assert_noop!( AssetConversion::create_pool( RuntimeOrigin::signed(user), - Box::new(token_2), - Box::new(token_2) + Box::new(token_2.clone()), + Box::new(token_2.clone()) ), - Error::::EqualAssets + Error::::InvalidAssetPair ); - // validate we can create Asset(1)/Asset(2) pool - let token_1 = NativeOrAssetId::Asset(1); - create_tokens(user, vec![token_1]); + // validate we cannot create WithId(1)/WithId(2) pool + let token_1 = NativeOrWithId::WithId(1); + create_tokens(user, vec![token_1.clone()]); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2) + Box::new(token_1.clone()), + Box::new(token_2.clone()) )); - - // validate we can force the first asset to be the Native currency only - AllowMultiAssetPools::set(&false); - let token_1 = NativeOrAssetId::Asset(3); - assert_noop!( - AssetConversion::create_pool( - RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2) - ), - Error::::PoolMustContainNativeCurrency - ); }); } @@ -221,16 +243,16 @@ fn can_create_pool() { fn create_same_pool_twice_should_fail() { new_test_ext().execute_with(|| { let user = 1; - let token_1 = NativeOrAssetId::Native; - let token_2 = NativeOrAssetId::Asset(2); + let token_1 = NativeOrWithId::Native; + let token_2 = NativeOrWithId::WithId(2); - create_tokens(user, vec![token_2]); + create_tokens(user, vec![token_2.clone()]); let lp_token = AssetConversion::get_next_pool_asset_id(); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(user), - Box::new(token_2), - Box::new(token_1) + Box::new(token_2.clone()), + Box::new(token_1.clone()) )); let expected_free = lp_token + 1; assert_eq!(expected_free, AssetConversion::get_next_pool_asset_id()); @@ -238,8 +260,8 @@ fn create_same_pool_twice_should_fail() { assert_noop!( AssetConversion::create_pool( RuntimeOrigin::signed(user), - Box::new(token_2), - Box::new(token_1) + Box::new(token_2.clone()), + Box::new(token_1.clone()) ), Error::::PoolExists ); @@ -249,8 +271,8 @@ fn create_same_pool_twice_should_fail() { assert_noop!( AssetConversion::create_pool( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2) + Box::new(token_1.clone()), + Box::new(token_2.clone()) ), Error::::PoolExists ); @@ -262,19 +284,19 @@ fn create_same_pool_twice_should_fail() { fn different_pools_should_have_different_lp_tokens() { new_test_ext().execute_with(|| { let user = 1; - let token_1 = NativeOrAssetId::Native; - let token_2 = NativeOrAssetId::Asset(2); - let token_3 = NativeOrAssetId::Asset(3); - let pool_id_1_2 = (token_1, token_2); - let pool_id_1_3 = (token_1, token_3); + let token_1 = NativeOrWithId::Native; + let token_2 = NativeOrWithId::WithId(2); + let token_3 = NativeOrWithId::WithId(3); + let pool_id_1_2 = (token_1.clone(), token_2.clone()); + let pool_id_1_3 = (token_1.clone(), token_3.clone()); - create_tokens(user, vec![token_2, token_3]); + create_tokens(user, vec![token_2.clone(), token_3.clone()]); let lp_token2_1 = AssetConversion::get_next_pool_asset_id(); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(user), - Box::new(token_2), - Box::new(token_1) + Box::new(token_2.clone()), + Box::new(token_1.clone()) )); let lp_token3_1 = AssetConversion::get_next_pool_asset_id(); @@ -282,23 +304,23 @@ fn different_pools_should_have_different_lp_tokens() { events(), [Event::::PoolCreated { creator: user, - pool_id: pool_id_1_2, - pool_account: AssetConversion::get_pool_account(&pool_id_1_2), + pool_id: pool_id_1_2.clone(), + pool_account: ::PoolLocator::address(&pool_id_1_2).unwrap(), lp_token: lp_token2_1 }] ); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(user), - Box::new(token_3), - Box::new(token_1) + Box::new(token_3.clone()), + Box::new(token_1.clone()) )); assert_eq!( events(), [Event::::PoolCreated { creator: user, - pool_id: pool_id_1_3, - pool_account: AssetConversion::get_pool_account(&pool_id_1_3), + pool_id: pool_id_1_3.clone(), + pool_account: ::PoolLocator::address(&pool_id_1_3).unwrap(), lp_token: lp_token3_1, }] ); @@ -311,33 +333,33 @@ fn different_pools_should_have_different_lp_tokens() { fn can_add_liquidity() { new_test_ext().execute_with(|| { let user = 1; - let token_1 = NativeOrAssetId::Native; - let token_2 = NativeOrAssetId::Asset(2); - let token_3 = NativeOrAssetId::Asset(3); + let token_1 = NativeOrWithId::Native; + let token_2 = NativeOrWithId::WithId(2); + let token_3 = NativeOrWithId::WithId(3); - create_tokens(user, vec![token_2, token_3]); + create_tokens(user, vec![token_2.clone(), token_3.clone()]); let lp_token1 = AssetConversion::get_next_pool_asset_id(); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2) + Box::new(token_1.clone()), + Box::new(token_2.clone()) )); let lp_token2 = AssetConversion::get_next_pool_asset_id(); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_3) + Box::new(token_1.clone()), + Box::new(token_3.clone()) )); - let ed = get_ed(); + let ed = get_native_ed(); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 10000 * 2 + ed)); assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user, 1000)); assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 3, user, 1000)); assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2), + Box::new(token_1.clone()), + Box::new(token_2.clone()), 10000, 10, 10000, @@ -345,28 +367,28 @@ fn can_add_liquidity() { user, )); - let pool_id = (token_1, token_2); + let pool_id = (token_1.clone(), token_2.clone()); assert!(events().contains(&Event::::LiquidityAdded { who: user, mint_to: user, - pool_id, + pool_id: pool_id.clone(), amount1_provided: 10000, amount2_provided: 10, lp_token: lp_token1, lp_token_minted: 216, })); - let pallet_account = AssetConversion::get_pool_account(&pool_id); - assert_eq!(balance(pallet_account, token_1), 10000); - assert_eq!(balance(pallet_account, token_2), 10); - assert_eq!(balance(user, token_1), 10000 + ed); - assert_eq!(balance(user, token_2), 1000 - 10); + let pallet_account = ::PoolLocator::address(&pool_id).unwrap(); + assert_eq!(balance(pallet_account, token_1.clone()), 10000); + assert_eq!(balance(pallet_account, token_2.clone()), 10); + assert_eq!(balance(user, token_1.clone()), 10000 + ed); + assert_eq!(balance(user, token_2.clone()), 1000 - 10); assert_eq!(pool_balance(user, lp_token1), 216); // try to pass the non-native - native assets, the result should be the same assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - Box::new(token_3), - Box::new(token_1), + Box::new(token_3.clone()), + Box::new(token_1.clone()), 10, 10000, 10, @@ -374,21 +396,21 @@ fn can_add_liquidity() { user, )); - let pool_id = (token_1, token_3); + let pool_id = (token_1.clone(), token_3.clone()); assert!(events().contains(&Event::::LiquidityAdded { who: user, mint_to: user, - pool_id, - amount1_provided: 10000, - amount2_provided: 10, + pool_id: pool_id.clone(), + amount1_provided: 10, + amount2_provided: 10000, lp_token: lp_token2, lp_token_minted: 216, })); - let pallet_account = AssetConversion::get_pool_account(&pool_id); - assert_eq!(balance(pallet_account, token_1), 10000); - assert_eq!(balance(pallet_account, token_3), 10); - assert_eq!(balance(user, token_1), ed); - assert_eq!(balance(user, token_3), 1000 - 10); + let pallet_account = ::PoolLocator::address(&pool_id).unwrap(); + assert_eq!(balance(pallet_account, token_1.clone()), 10000); + assert_eq!(balance(pallet_account, token_3.clone()), 10); + assert_eq!(balance(user, token_1.clone()), ed); + assert_eq!(balance(user, token_3.clone()), 1000 - 10); assert_eq!(pool_balance(user, lp_token2), 216); }); } @@ -397,14 +419,14 @@ fn can_add_liquidity() { fn add_tiny_liquidity_leads_to_insufficient_liquidity_minted_error() { new_test_ext().execute_with(|| { let user = 1; - let token_1 = NativeOrAssetId::Native; - let token_2 = NativeOrAssetId::Asset(2); + let token_1 = NativeOrWithId::Native; + let token_2 = NativeOrWithId::WithId(2); - create_tokens(user, vec![token_2]); + create_tokens(user, vec![token_2.clone()]); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2) + Box::new(token_1.clone()), + Box::new(token_2.clone()) )); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 1000)); @@ -413,8 +435,8 @@ fn add_tiny_liquidity_leads_to_insufficient_liquidity_minted_error() { assert_noop!( AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2), + Box::new(token_1.clone()), + Box::new(token_2.clone()), 1, 1, 1, @@ -427,9 +449,9 @@ fn add_tiny_liquidity_leads_to_insufficient_liquidity_minted_error() { assert_noop!( AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2), - get_ed(), + Box::new(token_1.clone()), + Box::new(token_2.clone()), + get_native_ed(), 1, 1, 1, @@ -444,35 +466,37 @@ fn add_tiny_liquidity_leads_to_insufficient_liquidity_minted_error() { fn add_tiny_liquidity_directly_to_pool_address() { new_test_ext().execute_with(|| { let user = 1; - let token_1 = NativeOrAssetId::Native; - let token_2 = NativeOrAssetId::Asset(2); - let token_3 = NativeOrAssetId::Asset(3); + let token_1 = NativeOrWithId::Native; + let token_2 = NativeOrWithId::WithId(2); + let token_3 = NativeOrWithId::WithId(3); - create_tokens(user, vec![token_2, token_3]); + create_tokens(user, vec![token_2.clone(), token_3.clone()]); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2) + Box::new(token_1.clone()), + Box::new(token_2.clone()) )); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_3) + Box::new(token_1.clone()), + Box::new(token_3.clone()) )); - let ed = get_ed(); + let ed = get_native_ed(); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 10000 * 2 + ed)); assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user, 1000)); assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 3, user, 1000)); - // check we're still able to add the liquidity even when the pool already has some token_1 - let pallet_account = AssetConversion::get_pool_account(&(token_1, token_2)); + // check we're still able to add the liquidity even when the pool already has some + // token_1.clone() + let pallet_account = + ::PoolLocator::address(&(token_1.clone(), token_2.clone())).unwrap(); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), pallet_account, 1000)); assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2), + Box::new(token_1.clone()), + Box::new(token_2.clone()), 10000, 10, 10000, @@ -480,13 +504,14 @@ fn add_tiny_liquidity_directly_to_pool_address() { user, )); - // check the same but for token_3 (non-native token) - let pallet_account = AssetConversion::get_pool_account(&(token_1, token_3)); + // check the same but for token_3.clone() (non-native token) + let pallet_account = + ::PoolLocator::address(&(token_1.clone(), token_3.clone())).unwrap(); assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, pallet_account, 1)); assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_3), + Box::new(token_1.clone()), + Box::new(token_3.clone()), 10000, 10, 10000, @@ -500,16 +525,16 @@ fn add_tiny_liquidity_directly_to_pool_address() { fn can_remove_liquidity() { new_test_ext().execute_with(|| { let user = 1; - let token_1 = NativeOrAssetId::Native; - let token_2 = NativeOrAssetId::Asset(2); - let pool_id = (token_1, token_2); + let token_1 = NativeOrWithId::Native; + let token_2 = NativeOrWithId::WithId(2); + let pool_id = (token_1.clone(), token_2.clone()); - create_tokens(user, vec![token_2]); + create_tokens(user, vec![token_2.clone()]); let lp_token = AssetConversion::get_next_pool_asset_id(); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2) + Box::new(token_1.clone()), + Box::new(token_2.clone()) )); let ed_token_1 = >::minimum_balance(); @@ -517,14 +542,14 @@ fn can_remove_liquidity() { assert_ok!(Balances::force_set_balance( RuntimeOrigin::root(), user, - 10000000000 + ed_token_1 + 10000000000 + ed_token_1.clone() )); - assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user, 100000 + ed_token_2)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user, 100000 + ed_token_2.clone())); assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2), + Box::new(token_1.clone()), + Box::new(token_2.clone()), 1000000000, 100000, 1000000000, @@ -537,8 +562,8 @@ fn can_remove_liquidity() { assert_ok!(AssetConversion::remove_liquidity( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2), + Box::new(token_1.clone()), + Box::new(token_2.clone()), total_lp_received, 0, 0, @@ -548,7 +573,7 @@ fn can_remove_liquidity() { assert!(events().contains(&Event::::LiquidityRemoved { who: user, withdraw_to: user, - pool_id, + pool_id: pool_id.clone(), amount1: 899991000, amount2: 89999, lp_token, @@ -556,13 +581,16 @@ fn can_remove_liquidity() { withdrawal_fee: ::LiquidityWithdrawalFee::get() })); - let pool_account = AssetConversion::get_pool_account(&pool_id); - assert_eq!(balance(pool_account, token_1), 100009000); - assert_eq!(balance(pool_account, token_2), 10001); + let pool_account = ::PoolLocator::address(&pool_id).unwrap(); + assert_eq!(balance(pool_account, token_1.clone()), 100009000); + assert_eq!(balance(pool_account, token_2.clone()), 10001); assert_eq!(pool_balance(pool_account, lp_token), 100); - assert_eq!(balance(user, token_1), 10000000000 - 1000000000 + 899991000 + ed_token_1); - assert_eq!(balance(user, token_2), 89999 + ed_token_2); + assert_eq!( + balance(user, token_1.clone()), + 10000000000 - 1000000000 + 899991000 + ed_token_1.clone() + ); + assert_eq!(balance(user, token_2.clone()), 89999 + ed_token_2.clone()); assert_eq!(pool_balance(user, lp_token), 0); }); } @@ -571,24 +599,28 @@ fn can_remove_liquidity() { fn can_not_redeem_more_lp_tokens_than_were_minted() { new_test_ext().execute_with(|| { let user = 1; - let token_1 = NativeOrAssetId::Native; - let token_2 = NativeOrAssetId::Asset(2); + let token_1 = NativeOrWithId::Native; + let token_2 = NativeOrWithId::WithId(2); let lp_token = AssetConversion::get_next_pool_asset_id(); - create_tokens(user, vec![token_2]); + create_tokens(user, vec![token_2.clone()]); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2) + Box::new(token_1.clone()), + Box::new(token_2.clone()) )); - assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 10000 + get_ed())); + assert_ok!(Balances::force_set_balance( + RuntimeOrigin::root(), + user, + 10000 + get_native_ed() + )); assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user, 1000)); assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2), + Box::new(token_1.clone()), + Box::new(token_2.clone()), 10000, 10, 10000, @@ -602,8 +634,8 @@ fn can_not_redeem_more_lp_tokens_than_were_minted() { assert_noop!( AssetConversion::remove_liquidity( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2), + Box::new(token_1.clone()), + Box::new(token_2.clone()), 216 + 1, // Try and redeem 10 lp tokens while only 9 minted. 0, 0, @@ -618,14 +650,14 @@ fn can_not_redeem_more_lp_tokens_than_were_minted() { fn can_quote_price() { new_test_ext().execute_with(|| { let user = 1; - let token_1 = NativeOrAssetId::Native; - let token_2 = NativeOrAssetId::Asset(2); + let token_1 = NativeOrWithId::Native; + let token_2 = NativeOrWithId::WithId(2); - create_tokens(user, vec![token_2]); + create_tokens(user, vec![token_2.clone()]); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2) + Box::new(token_1.clone()), + Box::new(token_2.clone()) )); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 100000)); @@ -633,8 +665,8 @@ fn can_quote_price() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2), + Box::new(token_1.clone()), + Box::new(token_2.clone()), 10000, 200, 1, @@ -644,8 +676,8 @@ fn can_quote_price() { assert_eq!( AssetConversion::quote_price_exact_tokens_for_tokens( - NativeOrAssetId::Native, - NativeOrAssetId::Asset(2), + NativeOrWithId::Native, + NativeOrWithId::WithId(2), 3000, false, ), @@ -654,8 +686,8 @@ fn can_quote_price() { // including fee so should get less out... assert_eq!( AssetConversion::quote_price_exact_tokens_for_tokens( - NativeOrAssetId::Native, - NativeOrAssetId::Asset(2), + NativeOrWithId::Native, + NativeOrWithId::WithId(2), 3000, true, ), @@ -665,8 +697,8 @@ fn can_quote_price() { // (if the above accidentally exchanged then it would not give same quote as before) assert_eq!( AssetConversion::quote_price_exact_tokens_for_tokens( - NativeOrAssetId::Native, - NativeOrAssetId::Asset(2), + NativeOrWithId::Native, + NativeOrWithId::WithId(2), 3000, false, ), @@ -675,8 +707,8 @@ fn can_quote_price() { // including fee so should get less out... assert_eq!( AssetConversion::quote_price_exact_tokens_for_tokens( - NativeOrAssetId::Native, - NativeOrAssetId::Asset(2), + NativeOrWithId::Native, + NativeOrWithId::WithId(2), 3000, true, ), @@ -686,8 +718,8 @@ fn can_quote_price() { // Check inverse: assert_eq!( AssetConversion::quote_price_exact_tokens_for_tokens( - NativeOrAssetId::Asset(2), - NativeOrAssetId::Native, + NativeOrWithId::WithId(2), + NativeOrWithId::Native, 60, false, ), @@ -696,8 +728,8 @@ fn can_quote_price() { // including fee so should get less out... assert_eq!( AssetConversion::quote_price_exact_tokens_for_tokens( - NativeOrAssetId::Asset(2), - NativeOrAssetId::Native, + NativeOrWithId::WithId(2), + NativeOrWithId::Native, 60, true, ), @@ -709,8 +741,8 @@ fn can_quote_price() { // assert_eq!( AssetConversion::quote_price_tokens_for_exact_tokens( - NativeOrAssetId::Native, - NativeOrAssetId::Asset(2), + NativeOrWithId::Native, + NativeOrWithId::WithId(2), 60, false, ), @@ -719,8 +751,8 @@ fn can_quote_price() { // including fee so should need to put more in... assert_eq!( AssetConversion::quote_price_tokens_for_exact_tokens( - NativeOrAssetId::Native, - NativeOrAssetId::Asset(2), + NativeOrWithId::Native, + NativeOrWithId::WithId(2), 60, true, ), @@ -730,8 +762,8 @@ fn can_quote_price() { // (if the above accidentally exchanged then it would not give same quote as before) assert_eq!( AssetConversion::quote_price_tokens_for_exact_tokens( - NativeOrAssetId::Native, - NativeOrAssetId::Asset(2), + NativeOrWithId::Native, + NativeOrWithId::WithId(2), 60, false, ), @@ -740,8 +772,8 @@ fn can_quote_price() { // including fee so should need to put more in... assert_eq!( AssetConversion::quote_price_tokens_for_exact_tokens( - NativeOrAssetId::Native, - NativeOrAssetId::Asset(2), + NativeOrWithId::Native, + NativeOrWithId::WithId(2), 60, true, ), @@ -751,8 +783,8 @@ fn can_quote_price() { // Check inverse: assert_eq!( AssetConversion::quote_price_tokens_for_exact_tokens( - NativeOrAssetId::Asset(2), - NativeOrAssetId::Native, + NativeOrWithId::WithId(2), + NativeOrWithId::Native, 3000, false, ), @@ -761,8 +793,8 @@ fn can_quote_price() { // including fee so should need to put more in... assert_eq!( AssetConversion::quote_price_tokens_for_exact_tokens( - NativeOrAssetId::Asset(2), - NativeOrAssetId::Native, + NativeOrWithId::WithId(2), + NativeOrWithId::Native, 3000, true, ), @@ -776,14 +808,14 @@ fn can_quote_price() { assert_eq!( AssetConversion::quote_price_exact_tokens_for_tokens( - NativeOrAssetId::Asset(2), - NativeOrAssetId::Native, + NativeOrWithId::WithId(2), + NativeOrWithId::Native, amount_in, false, ) .and_then(|amount| AssetConversion::quote_price_exact_tokens_for_tokens( - NativeOrAssetId::Native, - NativeOrAssetId::Asset(2), + NativeOrWithId::Native, + NativeOrWithId::WithId(2), amount, false, )), @@ -791,14 +823,14 @@ fn can_quote_price() { ); assert_eq!( AssetConversion::quote_price_exact_tokens_for_tokens( - NativeOrAssetId::Native, - NativeOrAssetId::Asset(2), + NativeOrWithId::Native, + NativeOrWithId::WithId(2), amount_in, false, ) .and_then(|amount| AssetConversion::quote_price_exact_tokens_for_tokens( - NativeOrAssetId::Asset(2), - NativeOrAssetId::Native, + NativeOrWithId::WithId(2), + NativeOrWithId::Native, amount, false, )), @@ -807,14 +839,14 @@ fn can_quote_price() { assert_eq!( AssetConversion::quote_price_tokens_for_exact_tokens( - NativeOrAssetId::Asset(2), - NativeOrAssetId::Native, + NativeOrWithId::WithId(2), + NativeOrWithId::Native, amount_in, false, ) .and_then(|amount| AssetConversion::quote_price_tokens_for_exact_tokens( - NativeOrAssetId::Native, - NativeOrAssetId::Asset(2), + NativeOrWithId::Native, + NativeOrWithId::WithId(2), amount, false, )), @@ -822,14 +854,14 @@ fn can_quote_price() { ); assert_eq!( AssetConversion::quote_price_tokens_for_exact_tokens( - NativeOrAssetId::Native, - NativeOrAssetId::Asset(2), + NativeOrWithId::Native, + NativeOrWithId::WithId(2), amount_in, false, ) .and_then(|amount| AssetConversion::quote_price_tokens_for_exact_tokens( - NativeOrAssetId::Asset(2), - NativeOrAssetId::Native, + NativeOrWithId::WithId(2), + NativeOrWithId::Native, amount, false, )), @@ -843,14 +875,14 @@ fn quote_price_exact_tokens_for_tokens_matches_execution() { new_test_ext().execute_with(|| { let user = 1; let user2 = 2; - let token_1 = NativeOrAssetId::Native; - let token_2 = NativeOrAssetId::Asset(2); + let token_1 = NativeOrWithId::Native; + let token_2 = NativeOrWithId::WithId(2); - create_tokens(user, vec![token_2]); + create_tokens(user, vec![token_2.clone()]); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2) + Box::new(token_1.clone()), + Box::new(token_2.clone()) )); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 100000)); @@ -858,8 +890,8 @@ fn quote_price_exact_tokens_for_tokens_matches_execution() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2), + Box::new(token_1.clone()), + Box::new(token_2.clone()), 10000, 200, 1, @@ -870,23 +902,28 @@ fn quote_price_exact_tokens_for_tokens_matches_execution() { let amount = 1; let quoted_price = 49; assert_eq!( - AssetConversion::quote_price_exact_tokens_for_tokens(token_2, token_1, amount, true,), + AssetConversion::quote_price_exact_tokens_for_tokens( + token_2.clone(), + token_1.clone(), + amount, + true, + ), Some(quoted_price) ); assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user2, amount)); let prior_dot_balance = 20000; - assert_eq!(prior_dot_balance, balance(user2, token_1)); + assert_eq!(prior_dot_balance, balance(user2, token_1.clone())); assert_ok!(AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user2), - bvec![token_2, token_1], + bvec![token_2.clone(), token_1.clone()], amount, 1, user2, false, )); - assert_eq!(prior_dot_balance + quoted_price, balance(user2, token_1)); + assert_eq!(prior_dot_balance + quoted_price, balance(user2, token_1.clone())); }); } @@ -895,14 +932,14 @@ fn quote_price_tokens_for_exact_tokens_matches_execution() { new_test_ext().execute_with(|| { let user = 1; let user2 = 2; - let token_1 = NativeOrAssetId::Native; - let token_2 = NativeOrAssetId::Asset(2); + let token_1 = NativeOrWithId::Native; + let token_2 = NativeOrWithId::WithId(2); - create_tokens(user, vec![token_2]); + create_tokens(user, vec![token_2.clone()]); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2) + Box::new(token_1.clone()), + Box::new(token_2.clone()) )); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 100000)); @@ -910,8 +947,8 @@ fn quote_price_tokens_for_exact_tokens_matches_execution() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2), + Box::new(token_1.clone()), + Box::new(token_2.clone()), 10000, 200, 1, @@ -922,26 +959,31 @@ fn quote_price_tokens_for_exact_tokens_matches_execution() { let amount = 49; let quoted_price = 1; assert_eq!( - AssetConversion::quote_price_tokens_for_exact_tokens(token_2, token_1, amount, true,), + AssetConversion::quote_price_tokens_for_exact_tokens( + token_2.clone(), + token_1.clone(), + amount, + true, + ), Some(quoted_price) ); assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user2, amount)); let prior_dot_balance = 20000; - assert_eq!(prior_dot_balance, balance(user2, token_1)); + assert_eq!(prior_dot_balance, balance(user2, token_1.clone())); let prior_asset_balance = 49; - assert_eq!(prior_asset_balance, balance(user2, token_2)); + assert_eq!(prior_asset_balance, balance(user2, token_2.clone())); assert_ok!(AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user2), - bvec![token_2, token_1], + bvec![token_2.clone(), token_1.clone()], amount, 1, user2, false, )); - assert_eq!(prior_dot_balance + amount, balance(user2, token_1)); - assert_eq!(prior_asset_balance - quoted_price, balance(user2, token_2)); + assert_eq!(prior_dot_balance + amount, balance(user2, token_1.clone())); + assert_eq!(prior_asset_balance - quoted_price, balance(user2, token_2.clone())); }); } @@ -949,18 +991,18 @@ fn quote_price_tokens_for_exact_tokens_matches_execution() { fn can_swap_with_native() { new_test_ext().execute_with(|| { let user = 1; - let token_1 = NativeOrAssetId::Native; - let token_2 = NativeOrAssetId::Asset(2); - let pool_id = (token_1, token_2); + let token_1 = NativeOrWithId::Native; + let token_2 = NativeOrWithId::WithId(2); + let pool_id = (token_1.clone(), token_2.clone()); - create_tokens(user, vec![token_2]); + create_tokens(user, vec![token_2.clone()]); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2) + Box::new(token_1.clone()), + Box::new(token_2.clone()) )); - let ed = get_ed(); + let ed = get_native_ed(); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 10000 + ed)); assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user, 1000)); @@ -969,8 +1011,8 @@ fn can_swap_with_native() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2), + Box::new(token_1.clone()), + Box::new(token_2.clone()), liquidity1, liquidity2, 1, @@ -986,18 +1028,18 @@ fn can_swap_with_native() { assert_ok!(AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bvec![token_2, token_1], + bvec![token_2.clone(), token_1.clone()], input_amount, 1, user, false, )); - let pallet_account = AssetConversion::get_pool_account(&pool_id); - assert_eq!(balance(user, token_1), expect_receive + ed); - assert_eq!(balance(user, token_2), 1000 - liquidity2 - input_amount); - assert_eq!(balance(pallet_account, token_1), liquidity1 - expect_receive); - assert_eq!(balance(pallet_account, token_2), liquidity2 + input_amount); + let pallet_account = ::PoolLocator::address(&pool_id).unwrap(); + assert_eq!(balance(user, token_1.clone()), expect_receive + ed); + assert_eq!(balance(user, token_2.clone()), 1000 - liquidity2 - input_amount); + assert_eq!(balance(pallet_account, token_1.clone()), liquidity1 - expect_receive); + assert_eq!(balance(pallet_account, token_2.clone()), liquidity2 + input_amount); }); } @@ -1005,13 +1047,13 @@ fn can_swap_with_native() { fn can_swap_with_realistic_values() { new_test_ext().execute_with(|| { let user = 1; - let dot = NativeOrAssetId::Native; - let usd = NativeOrAssetId::Asset(2); - create_tokens(user, vec![usd]); + let dot = NativeOrWithId::Native; + let usd = NativeOrWithId::WithId(2); + create_tokens(user, vec![usd.clone()]); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(user), - Box::new(dot), - Box::new(usd) + Box::new(dot.clone()), + Box::new(usd.clone()) )); const UNIT: u128 = 1_000_000_000; @@ -1023,8 +1065,8 @@ fn can_swap_with_realistic_values() { let liquidity_usd = 1_000_000 * UNIT; assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - Box::new(dot), - Box::new(usd), + Box::new(dot.clone()), + Box::new(usd.clone()), liquidity_dot, liquidity_usd, 1, @@ -1036,7 +1078,7 @@ fn can_swap_with_realistic_values() { assert_ok!(AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bvec![usd, dot], + bvec![usd.clone(), dot.clone()], input_amount, 1, user, @@ -1057,21 +1099,21 @@ fn can_swap_with_realistic_values() { fn can_not_swap_in_pool_with_no_liquidity_added_yet() { new_test_ext().execute_with(|| { let user = 1; - let token_1 = NativeOrAssetId::Native; - let token_2 = NativeOrAssetId::Asset(2); + let token_1 = NativeOrWithId::Native; + let token_2 = NativeOrWithId::WithId(2); - create_tokens(user, vec![token_2]); + create_tokens(user, vec![token_2.clone()]); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2) + Box::new(token_1.clone()), + Box::new(token_2.clone()) )); // Check can't swap an empty pool assert_noop!( AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bvec![token_2, token_1], + bvec![token_2.clone(), token_1.clone()], 10, 1, user, @@ -1086,19 +1128,19 @@ fn can_not_swap_in_pool_with_no_liquidity_added_yet() { fn check_no_panic_when_try_swap_close_to_empty_pool() { new_test_ext().execute_with(|| { let user = 1; - let token_1 = NativeOrAssetId::Native; - let token_2 = NativeOrAssetId::Asset(2); - let pool_id = (token_1, token_2); + let token_1 = NativeOrWithId::Native; + let token_2 = NativeOrWithId::WithId(2); + let pool_id = (token_1.clone(), token_2.clone()); let lp_token = AssetConversion::get_next_pool_asset_id(); - create_tokens(user, vec![token_2]); + create_tokens(user, vec![token_2.clone()]); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2) + Box::new(token_1.clone()), + Box::new(token_2.clone()) )); - let ed = get_ed(); + let ed = get_native_ed(); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 10000 + ed)); assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user, 1000)); @@ -1107,8 +1149,8 @@ fn check_no_panic_when_try_swap_close_to_empty_pool() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2), + Box::new(token_1.clone()), + Box::new(token_2.clone()), liquidity1, liquidity2, 1, @@ -1120,21 +1162,21 @@ fn check_no_panic_when_try_swap_close_to_empty_pool() { assert!(events().contains(&Event::::LiquidityAdded { who: user, mint_to: user, - pool_id, + pool_id: pool_id.clone(), amount1_provided: liquidity1, amount2_provided: liquidity2, lp_token, lp_token_minted, })); - let pallet_account = AssetConversion::get_pool_account(&pool_id); - assert_eq!(balance(pallet_account, token_1), liquidity1); - assert_eq!(balance(pallet_account, token_2), liquidity2); + let pallet_account = ::PoolLocator::address(&pool_id).unwrap(); + assert_eq!(balance(pallet_account, token_1.clone()), liquidity1); + assert_eq!(balance(pallet_account, token_2.clone()), liquidity2); assert_ok!(AssetConversion::remove_liquidity( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2), + Box::new(token_1.clone()), + Box::new(token_2.clone()), lp_token_minted, 1, 1, @@ -1143,14 +1185,14 @@ fn check_no_panic_when_try_swap_close_to_empty_pool() { // Now, the pool should exist but be almost empty. // Let's try and drain it. - assert_eq!(balance(pallet_account, token_1), 708); - assert_eq!(balance(pallet_account, token_2), 15); + assert_eq!(balance(pallet_account, token_1.clone()), 708); + assert_eq!(balance(pallet_account, token_2.clone()), 15); // validate the reserve should always stay above the ED assert_noop!( AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user), - bvec![token_2, token_1], + bvec![token_2.clone(), token_1.clone()], 708 - ed + 1, // amount_out 500, // amount_in_max user, @@ -1161,15 +1203,15 @@ fn check_no_panic_when_try_swap_close_to_empty_pool() { assert_ok!(AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user), - bvec![token_2, token_1], + bvec![token_2.clone(), token_1.clone()], 608, // amount_out 500, // amount_in_max user, false, )); - let token_1_left = balance(pallet_account, token_1); - let token_2_left = balance(pallet_account, token_2); + let token_1_left = balance(pallet_account, token_1.clone()); + let token_2_left = balance(pallet_account, token_2.clone()); assert_eq!(token_1_left, 708 - 608); // The price for the last tokens should be very high @@ -1183,7 +1225,7 @@ fn check_no_panic_when_try_swap_close_to_empty_pool() { assert_noop!( AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user), - bvec![token_2, token_1], + bvec![token_2.clone(), token_1.clone()], token_1_left - 1, // amount_out 1000, // amount_in_max user, @@ -1196,7 +1238,7 @@ fn check_no_panic_when_try_swap_close_to_empty_pool() { assert_noop!( AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user), - bvec![token_2, token_1], + bvec![token_2.clone(), token_1.clone()], token_1_left, // amount_out 1000, // amount_in_max user, @@ -1211,17 +1253,21 @@ fn check_no_panic_when_try_swap_close_to_empty_pool() { fn swap_should_not_work_if_too_much_slippage() { new_test_ext().execute_with(|| { let user = 1; - let token_1 = NativeOrAssetId::Native; - let token_2 = NativeOrAssetId::Asset(2); + let token_1 = NativeOrWithId::Native; + let token_2 = NativeOrWithId::WithId(2); - create_tokens(user, vec![token_2]); + create_tokens(user, vec![token_2.clone()]); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2) + Box::new(token_1.clone()), + Box::new(token_2.clone()) )); - assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 10000 + get_ed())); + assert_ok!(Balances::force_set_balance( + RuntimeOrigin::root(), + user, + 10000 + get_native_ed() + )); assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user, 1000)); let liquidity1 = 10000; @@ -1229,8 +1275,8 @@ fn swap_should_not_work_if_too_much_slippage() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2), + Box::new(token_1.clone()), + Box::new(token_2.clone()), liquidity1, liquidity2, 1, @@ -1243,7 +1289,7 @@ fn swap_should_not_work_if_too_much_slippage() { assert_noop!( AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bvec![token_2, token_1], + bvec![token_2.clone(), token_1.clone()], exchange_amount, // amount_in 4000, // amount_out_min user, @@ -1258,32 +1304,32 @@ fn swap_should_not_work_if_too_much_slippage() { fn can_swap_tokens_for_exact_tokens() { new_test_ext().execute_with(|| { let user = 1; - let token_1 = NativeOrAssetId::Native; - let token_2 = NativeOrAssetId::Asset(2); - let pool_id = (token_1, token_2); + let token_1 = NativeOrWithId::Native; + let token_2 = NativeOrWithId::WithId(2); + let pool_id = (token_1.clone(), token_2.clone()); - create_tokens(user, vec![token_2]); + create_tokens(user, vec![token_2.clone()]); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2) + Box::new(token_1.clone()), + Box::new(token_2.clone()) )); - let ed = get_ed(); + let ed = get_native_ed(); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 20000 + ed)); assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user, 1000)); - let pallet_account = AssetConversion::get_pool_account(&pool_id); - let before1 = balance(pallet_account, token_1) + balance(user, token_1); - let before2 = balance(pallet_account, token_2) + balance(user, token_2); + let pallet_account = ::PoolLocator::address(&pool_id).unwrap(); + let before1 = balance(pallet_account, token_1.clone()) + balance(user, token_1.clone()); + let before2 = balance(pallet_account, token_2.clone()) + balance(user, token_2.clone()); let liquidity1 = 10000; let liquidity2 = 200; assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2), + Box::new(token_1.clone()), + Box::new(token_2.clone()), liquidity1, liquidity2, 1, @@ -1298,23 +1344,29 @@ fn can_swap_tokens_for_exact_tokens() { assert_ok!(AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user), - bvec![token_1, token_2], + bvec![token_1.clone(), token_2.clone()], exchange_out, // amount_out 3500, // amount_in_max user, true, )); - assert_eq!(balance(user, token_1), 10000 + ed - expect_in); - assert_eq!(balance(user, token_2), 1000 - liquidity2 + exchange_out); - assert_eq!(balance(pallet_account, token_1), liquidity1 + expect_in); - assert_eq!(balance(pallet_account, token_2), liquidity2 - exchange_out); + assert_eq!(balance(user, token_1.clone()), 10000 + ed - expect_in); + assert_eq!(balance(user, token_2.clone()), 1000 - liquidity2 + exchange_out); + assert_eq!(balance(pallet_account, token_1.clone()), liquidity1 + expect_in); + assert_eq!(balance(pallet_account, token_2.clone()), liquidity2 - exchange_out); // check invariants: // native and asset totals should be preserved. - assert_eq!(before1, balance(pallet_account, token_1) + balance(user, token_1)); - assert_eq!(before2, balance(pallet_account, token_2) + balance(user, token_2)); + assert_eq!( + before1, + balance(pallet_account, token_1.clone()) + balance(user, token_1.clone()) + ); + assert_eq!( + before2, + balance(pallet_account, token_2.clone()) + balance(user, token_2.clone()) + ); }); } @@ -1323,38 +1375,40 @@ fn can_swap_tokens_for_exact_tokens_when_not_liquidity_provider() { new_test_ext().execute_with(|| { let user = 1; let user2 = 2; - let token_1 = NativeOrAssetId::Native; - let token_2 = NativeOrAssetId::Asset(2); - let pool_id = (token_1, token_2); + let token_1 = NativeOrWithId::Native; + let token_2 = NativeOrWithId::WithId(2); + let pool_id = (token_1.clone(), token_2.clone()); let lp_token = AssetConversion::get_next_pool_asset_id(); - create_tokens(user2, vec![token_2]); + create_tokens(user2, vec![token_2.clone()]); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(user2), - Box::new(token_1), - Box::new(token_2) + Box::new(token_1.clone()), + Box::new(token_2.clone()) )); - let ed = get_ed(); + let ed = get_native_ed(); let base1 = 10000; let base2 = 1000; assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, base1 + ed)); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user2, base1 + ed)); assert_ok!(Assets::mint(RuntimeOrigin::signed(user2), 2, user2, base2)); - let pallet_account = AssetConversion::get_pool_account(&pool_id); - let before1 = - balance(pallet_account, token_1) + balance(user, token_1) + balance(user2, token_1); - let before2 = - balance(pallet_account, token_2) + balance(user, token_2) + balance(user2, token_2); + let pallet_account = ::PoolLocator::address(&pool_id).unwrap(); + let before1 = balance(pallet_account, token_1.clone()) + + balance(user, token_1.clone()) + + balance(user2, token_1.clone()); + let before2 = balance(pallet_account, token_2.clone()) + + balance(user, token_2.clone()) + + balance(user2, token_2.clone()); let liquidity1 = 10000; let liquidity2 = 200; assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user2), - Box::new(token_1), - Box::new(token_2), + Box::new(token_1.clone()), + Box::new(token_2.clone()), liquidity1, liquidity2, 1, @@ -1362,8 +1416,8 @@ fn can_swap_tokens_for_exact_tokens_when_not_liquidity_provider() { user2, )); - assert_eq!(balance(user, token_1), base1 + ed); - assert_eq!(balance(user, token_2), 0); + assert_eq!(balance(user, token_1.clone()), base1 + ed); + assert_eq!(balance(user, token_2.clone()), 0); let exchange_out = 50; let expect_in = AssetConversion::get_amount_in(&exchange_out, &liquidity1, &liquidity2) @@ -1372,28 +1426,32 @@ fn can_swap_tokens_for_exact_tokens_when_not_liquidity_provider() { assert_ok!(AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user), - bvec![token_1, token_2], + bvec![token_1.clone(), token_2.clone()], exchange_out, // amount_out 3500, // amount_in_max user, true, )); - assert_eq!(balance(user, token_1), base1 + ed - expect_in); - assert_eq!(balance(pallet_account, token_1), liquidity1 + expect_in); - assert_eq!(balance(user, token_2), exchange_out); - assert_eq!(balance(pallet_account, token_2), liquidity2 - exchange_out); + assert_eq!(balance(user, token_1.clone()), base1 + ed - expect_in); + assert_eq!(balance(pallet_account, token_1.clone()), liquidity1 + expect_in); + assert_eq!(balance(user, token_2.clone()), exchange_out); + assert_eq!(balance(pallet_account, token_2.clone()), liquidity2 - exchange_out); // check invariants: // native and asset totals should be preserved. assert_eq!( before1, - balance(pallet_account, token_1) + balance(user, token_1) + balance(user2, token_1) + balance(pallet_account, token_1.clone()) + + balance(user, token_1.clone()) + + balance(user2, token_1.clone()) ); assert_eq!( before2, - balance(pallet_account, token_2) + balance(user, token_2) + balance(user2, token_2) + balance(pallet_account, token_2.clone()) + + balance(user, token_2.clone()) + + balance(user2, token_2.clone()) ); let lp_token_minted = pool_balance(user2, lp_token); @@ -1401,8 +1459,8 @@ fn can_swap_tokens_for_exact_tokens_when_not_liquidity_provider() { assert_ok!(AssetConversion::remove_liquidity( RuntimeOrigin::signed(user2), - Box::new(token_1), - Box::new(token_2), + Box::new(token_1.clone()), + Box::new(token_2.clone()), lp_token_minted, 0, 0, @@ -1416,17 +1474,17 @@ fn swap_when_existential_deposit_would_cause_reaping_but_keep_alive_set() { new_test_ext().execute_with(|| { let user = 1; let user2 = 2; - let token_1 = NativeOrAssetId::Native; - let token_2 = NativeOrAssetId::Asset(2); + let token_1 = NativeOrWithId::Native; + let token_2 = NativeOrWithId::WithId(2); - create_tokens(user2, vec![token_2]); + create_tokens(user2, vec![token_2.clone()]); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(user2), - Box::new(token_1), - Box::new(token_2) + Box::new(token_1.clone()), + Box::new(token_2.clone()) )); - let ed = get_ed(); + let ed = get_native_ed(); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 101)); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user2, 10000 + ed)); assert_ok!(Assets::mint(RuntimeOrigin::signed(user2), 2, user2, 1000)); @@ -1434,8 +1492,8 @@ fn swap_when_existential_deposit_would_cause_reaping_but_keep_alive_set() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user2), - Box::new(token_1), - Box::new(token_2), + Box::new(token_1.clone()), + Box::new(token_2.clone()), 10000, 200, 1, @@ -1446,7 +1504,7 @@ fn swap_when_existential_deposit_would_cause_reaping_but_keep_alive_set() { assert_noop!( AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user), - bvec![token_1, token_2], + bvec![token_1.clone(), token_2.clone()], 1, // amount_out 101, // amount_in_max user, @@ -1458,7 +1516,7 @@ fn swap_when_existential_deposit_would_cause_reaping_but_keep_alive_set() { assert_noop!( AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bvec![token_1, token_2], + bvec![token_1.clone(), token_2.clone()], 51, // amount_in 1, // amount_out_min user, @@ -1470,7 +1528,7 @@ fn swap_when_existential_deposit_would_cause_reaping_but_keep_alive_set() { assert_noop!( AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user), - bvec![token_2, token_1], + bvec![token_2.clone(), token_1.clone()], 51, // amount_out 2, // amount_in_max user, @@ -1482,7 +1540,7 @@ fn swap_when_existential_deposit_would_cause_reaping_but_keep_alive_set() { assert_noop!( AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bvec![token_2, token_1], + bvec![token_2.clone(), token_1.clone()], 2, // amount_in 1, // amount_out_min user, @@ -1498,29 +1556,29 @@ fn swap_when_existential_deposit_would_cause_reaping_pool_account() { new_test_ext().execute_with(|| { let user = 1; let user2 = 2; - let token_1 = NativeOrAssetId::Native; - let token_2 = NativeOrAssetId::Asset(2); - let token_3 = NativeOrAssetId::Asset(3); + let token_1 = NativeOrWithId::Native; + let token_2 = NativeOrWithId::WithId(2); + let token_3 = NativeOrWithId::WithId(3); let ed_assets = 100; - create_tokens_with_ed(user2, vec![token_2, token_3], ed_assets); + create_tokens_with_ed(user2, vec![token_2.clone(), token_3.clone()], ed_assets); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(user2), - Box::new(token_1), - Box::new(token_2) + Box::new(token_1.clone()), + Box::new(token_2.clone()) )); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(user2), - Box::new(token_1), - Box::new(token_3) + Box::new(token_1.clone()), + Box::new(token_3.clone()) )); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(user2), - Box::new(token_2), - Box::new(token_3) + Box::new(token_2.clone()), + Box::new(token_3.clone()) )); - let ed = get_ed(); + let ed = get_native_ed(); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 20000 + ed)); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user2, 20000 + ed)); assert_ok!(Assets::mint(RuntimeOrigin::signed(user2), 2, user2, 400 + ed_assets)); @@ -1531,8 +1589,8 @@ fn swap_when_existential_deposit_would_cause_reaping_pool_account() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user2), - Box::new(token_1), - Box::new(token_2), + Box::new(token_1.clone()), + Box::new(token_2.clone()), 10000, 200, 1, @@ -1542,8 +1600,8 @@ fn swap_when_existential_deposit_would_cause_reaping_pool_account() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user2), - Box::new(token_1), - Box::new(token_3), + Box::new(token_1.clone()), + Box::new(token_3.clone()), 200, 10000, 1, @@ -1553,8 +1611,8 @@ fn swap_when_existential_deposit_would_cause_reaping_pool_account() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user2), - Box::new(token_2), - Box::new(token_3), + Box::new(token_2.clone()), + Box::new(token_3.clone()), 200, 10000, 1, @@ -1566,7 +1624,7 @@ fn swap_when_existential_deposit_would_cause_reaping_pool_account() { assert_noop!( AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user), - bvec![token_1, token_2], + bvec![token_1.clone(), token_2.clone()], 110, // amount_out 20000, // amount_in_max user, @@ -1579,7 +1637,7 @@ fn swap_when_existential_deposit_would_cause_reaping_pool_account() { assert_noop!( AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bvec![token_1, token_2], + bvec![token_1.clone(), token_2.clone()], 15000, // amount_in 110, // amount_out_min user, @@ -1592,7 +1650,7 @@ fn swap_when_existential_deposit_would_cause_reaping_pool_account() { assert_noop!( AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user), - bvec![token_3, token_1], + bvec![token_3.clone(), token_1.clone()], 110, // amount_out 20000, // amount_in_max user, @@ -1605,7 +1663,7 @@ fn swap_when_existential_deposit_would_cause_reaping_pool_account() { assert_noop!( AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bvec![token_3, token_1], + bvec![token_3.clone(), token_1.clone()], 15000, // amount_in 110, // amount_out_min user, @@ -1617,7 +1675,7 @@ fn swap_when_existential_deposit_would_cause_reaping_pool_account() { // causes an account removal for native token 1 locate in the middle of a swap path let amount_in = AssetConversion::balance_path_from_amount_out( 110, - vec![token_3, token_1].try_into().unwrap(), + vec![token_3.clone(), token_1.clone()].try_into().unwrap(), ) .unwrap() .first() @@ -1627,7 +1685,7 @@ fn swap_when_existential_deposit_would_cause_reaping_pool_account() { assert_noop!( AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bvec![token_3, token_1, token_2], + bvec![token_3.clone(), token_1.clone(), token_2.clone()], amount_in, // amount_in 1, // amount_out_min user, @@ -1639,7 +1697,7 @@ fn swap_when_existential_deposit_would_cause_reaping_pool_account() { // causes an account removal for asset token 2 locate in the middle of a swap path let amount_in = AssetConversion::balance_path_from_amount_out( 110, - vec![token_1, token_2].try_into().unwrap(), + vec![token_1.clone(), token_2.clone()].try_into().unwrap(), ) .unwrap() .first() @@ -1649,7 +1707,7 @@ fn swap_when_existential_deposit_would_cause_reaping_pool_account() { assert_noop!( AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bvec![token_1, token_2, token_3], + bvec![token_1.clone(), token_2.clone(), token_3.clone()], amount_in, // amount_in 1, // amount_out_min user, @@ -1664,17 +1722,21 @@ fn swap_when_existential_deposit_would_cause_reaping_pool_account() { fn swap_tokens_for_exact_tokens_should_not_work_if_too_much_slippage() { new_test_ext().execute_with(|| { let user = 1; - let token_1 = NativeOrAssetId::Native; - let token_2 = NativeOrAssetId::Asset(2); + let token_1 = NativeOrWithId::Native; + let token_2 = NativeOrWithId::WithId(2); - create_tokens(user, vec![token_2]); + create_tokens(user, vec![token_2.clone()]); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2) + Box::new(token_1.clone()), + Box::new(token_2.clone()) )); - assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 20000 + get_ed())); + assert_ok!(Balances::force_set_balance( + RuntimeOrigin::root(), + user, + 20000 + get_native_ed() + )); assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user, 1000)); let liquidity1 = 10000; @@ -1682,8 +1744,8 @@ fn swap_tokens_for_exact_tokens_should_not_work_if_too_much_slippage() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2), + Box::new(token_1.clone()), + Box::new(token_2.clone()), liquidity1, liquidity2, 1, @@ -1696,7 +1758,7 @@ fn swap_tokens_for_exact_tokens_should_not_work_if_too_much_slippage() { assert_noop!( AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user), - bvec![token_1, token_2], + bvec![token_1.clone(), token_2.clone()], exchange_out, // amount_out 50, // amount_in_max just greater than slippage. user, @@ -1711,23 +1773,23 @@ fn swap_tokens_for_exact_tokens_should_not_work_if_too_much_slippage() { fn swap_exact_tokens_for_tokens_in_multi_hops() { new_test_ext().execute_with(|| { let user = 1; - let token_1 = NativeOrAssetId::Native; - let token_2 = NativeOrAssetId::Asset(2); - let token_3 = NativeOrAssetId::Asset(3); + let token_1 = NativeOrWithId::Native; + let token_2 = NativeOrWithId::WithId(2); + let token_3 = NativeOrWithId::WithId(3); - create_tokens(user, vec![token_2, token_3]); + create_tokens(user, vec![token_2.clone(), token_3.clone()]); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2) + Box::new(token_1.clone()), + Box::new(token_2.clone()) )); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(user), - Box::new(token_2), - Box::new(token_3) + Box::new(token_2.clone()), + Box::new(token_3.clone()) )); - let ed = get_ed(); + let ed = get_native_ed(); let base1 = 10000; let base2 = 10000; assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, base1 * 2 + ed)); @@ -1740,8 +1802,8 @@ fn swap_exact_tokens_for_tokens_in_multi_hops() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2), + Box::new(token_1.clone()), + Box::new(token_2.clone()), liquidity1, liquidity2, 1, @@ -1750,8 +1812,8 @@ fn swap_exact_tokens_for_tokens_in_multi_hops() { )); assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - Box::new(token_2), - Box::new(token_3), + Box::new(token_2.clone()), + Box::new(token_3.clone()), liquidity2, liquidity3, 1, @@ -1770,7 +1832,7 @@ fn swap_exact_tokens_for_tokens_in_multi_hops() { assert_noop!( AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bvec![token_1], + bvec![token_1.clone()], input_amount, 80, user, @@ -1782,7 +1844,7 @@ fn swap_exact_tokens_for_tokens_in_multi_hops() { assert_noop!( AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bvec![token_1, token_2, token_3, token_2], + bvec![token_1.clone(), token_2.clone(), token_3.clone(), token_2.clone()], input_amount, 80, user, @@ -1793,24 +1855,24 @@ fn swap_exact_tokens_for_tokens_in_multi_hops() { assert_ok!(AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bvec![token_1, token_2, token_3], + bvec![token_1.clone(), token_2.clone(), token_3.clone()], input_amount, // amount_in 80, // amount_out_min user, true, )); - let pool_id1 = (token_1, token_2); - let pool_id2 = (token_2, token_3); - let pallet_account1 = AssetConversion::get_pool_account(&pool_id1); - let pallet_account2 = AssetConversion::get_pool_account(&pool_id2); + let pool_id1 = (token_1.clone(), token_2.clone()); + let pool_id2 = (token_2.clone(), token_3.clone()); + let pallet_account1 = ::PoolLocator::address(&pool_id1).unwrap(); + let pallet_account2 = ::PoolLocator::address(&pool_id2).unwrap(); - assert_eq!(balance(user, token_1), base1 + ed - input_amount); - assert_eq!(balance(pallet_account1, token_1), liquidity1 + input_amount); - assert_eq!(balance(pallet_account1, token_2), liquidity2 - expect_out2); - assert_eq!(balance(pallet_account2, token_2), liquidity2 + expect_out2); - assert_eq!(balance(pallet_account2, token_3), liquidity3 - expect_out3); - assert_eq!(balance(user, token_3), 10000 - liquidity3 + expect_out3); + assert_eq!(balance(user, token_1.clone()), base1 + ed - input_amount); + assert_eq!(balance(pallet_account1, token_1.clone()), liquidity1 + input_amount); + assert_eq!(balance(pallet_account1, token_2.clone()), liquidity2 - expect_out2); + assert_eq!(balance(pallet_account2, token_2.clone()), liquidity2 + expect_out2); + assert_eq!(balance(pallet_account2, token_3.clone()), liquidity3 - expect_out3); + assert_eq!(balance(user, token_3.clone()), 10000 - liquidity3 + expect_out3); }); } @@ -1818,23 +1880,23 @@ fn swap_exact_tokens_for_tokens_in_multi_hops() { fn swap_tokens_for_exact_tokens_in_multi_hops() { new_test_ext().execute_with(|| { let user = 1; - let token_1 = NativeOrAssetId::Native; - let token_2 = NativeOrAssetId::Asset(2); - let token_3 = NativeOrAssetId::Asset(3); + let token_1 = NativeOrWithId::Native; + let token_2 = NativeOrWithId::WithId(2); + let token_3 = NativeOrWithId::WithId(3); - create_tokens(user, vec![token_2, token_3]); + create_tokens(user, vec![token_2.clone(), token_3.clone()]); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2) + Box::new(token_1.clone()), + Box::new(token_2.clone()) )); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(user), - Box::new(token_2), - Box::new(token_3) + Box::new(token_2.clone()), + Box::new(token_3.clone()) )); - let ed = get_ed(); + let ed = get_native_ed(); let base1 = 10000; let base2 = 10000; assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, base1 * 2 + ed)); @@ -1847,8 +1909,8 @@ fn swap_tokens_for_exact_tokens_in_multi_hops() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2), + Box::new(token_1.clone()), + Box::new(token_2.clone()), liquidity1, liquidity2, 1, @@ -1857,8 +1919,8 @@ fn swap_tokens_for_exact_tokens_in_multi_hops() { )); assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - Box::new(token_2), - Box::new(token_3), + Box::new(token_2.clone()), + Box::new(token_3.clone()), liquidity2, liquidity3, 1, @@ -1876,24 +1938,24 @@ fn swap_tokens_for_exact_tokens_in_multi_hops() { assert_ok!(AssetConversion::swap_tokens_for_exact_tokens( RuntimeOrigin::signed(user), - bvec![token_1, token_2, token_3], + bvec![token_1.clone(), token_2.clone(), token_3.clone()], exchange_out3, // amount_out 1000, // amount_in_max user, true, )); - let pool_id1 = (token_1, token_2); - let pool_id2 = (token_2, token_3); - let pallet_account1 = AssetConversion::get_pool_account(&pool_id1); - let pallet_account2 = AssetConversion::get_pool_account(&pool_id2); + let pool_id1 = (token_1.clone(), token_2.clone()); + let pool_id2 = (token_2.clone(), token_3.clone()); + let pallet_account1 = ::PoolLocator::address(&pool_id1).unwrap(); + let pallet_account2 = ::PoolLocator::address(&pool_id2).unwrap(); - assert_eq!(balance(user, token_1), base1 + ed - expect_in1); - assert_eq!(balance(pallet_account1, token_1), liquidity1 + expect_in1); - assert_eq!(balance(pallet_account1, token_2), liquidity2 - expect_in2); - assert_eq!(balance(pallet_account2, token_2), liquidity2 + expect_in2); - assert_eq!(balance(pallet_account2, token_3), liquidity3 - exchange_out3); - assert_eq!(balance(user, token_3), 10000 - liquidity3 + exchange_out3); + assert_eq!(balance(user, token_1.clone()), base1 + ed - expect_in1); + assert_eq!(balance(pallet_account1, token_1.clone()), liquidity1 + expect_in1); + assert_eq!(balance(pallet_account1, token_2.clone()), liquidity2 - expect_in2); + assert_eq!(balance(pallet_account2, token_2.clone()), liquidity2 + expect_in2); + assert_eq!(balance(pallet_account2, token_3.clone()), liquidity3 - exchange_out3); + assert_eq!(balance(user, token_3.clone()), 10000 - liquidity3 + exchange_out3); }); } @@ -1901,10 +1963,10 @@ fn swap_tokens_for_exact_tokens_in_multi_hops() { fn can_not_swap_same_asset() { new_test_ext().execute_with(|| { let user = 1; - let token_1 = NativeOrAssetId::Asset(1); - let token_2 = NativeOrAssetId::Native; + let token_1 = NativeOrWithId::WithId(1); + let token_2 = NativeOrWithId::Native; - create_tokens(user, vec![token_1]); + create_tokens(user, vec![token_1.clone()]); assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 1, user, 1000)); let liquidity1 = 1000; @@ -1912,60 +1974,44 @@ fn can_not_swap_same_asset() { assert_noop!( AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_1), + Box::new(token_1.clone()), + Box::new(token_1.clone()), liquidity1, liquidity2, 1, 1, user, ), - Error::::PoolNotFound + Error::::InvalidAssetPair ); let exchange_amount = 10; assert_noop!( AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bvec![token_1, token_1], + bvec![token_1.clone(), token_1.clone()], exchange_amount, 1, user, true, ), - Error::::PoolNotFound + Error::::InvalidAssetPair ); assert_noop!( AssetConversion::swap_exact_tokens_for_tokens( RuntimeOrigin::signed(user), - bvec![token_2, token_2], + bvec![token_2.clone(), token_2.clone()], exchange_amount, 1, user, true, ), - Error::::PoolNotFound + Error::::InvalidAssetPair ); }); } -#[test] -fn validate_pool_id_sorting() { - new_test_ext().execute_with(|| { - use crate::NativeOrAssetId::{Asset, Native}; - assert_eq!(AssetConversion::get_pool_id(Native, Asset(2)), (Native, Asset(2))); - assert_eq!(AssetConversion::get_pool_id(Asset(2), Native), (Native, Asset(2))); - assert_eq!(AssetConversion::get_pool_id(Native, Native), (Native, Native)); - assert_eq!(AssetConversion::get_pool_id(Asset(2), Asset(1)), (Asset(1), Asset(2))); - assert!(Asset(2) > Asset(1)); - assert!(Asset(1) <= Asset(1)); - assert_eq!(Asset(1), Asset(1)); - assert_eq!(Native::, Native::); - assert!(Native < Asset(1)); - }); -} - #[test] fn cannot_block_pool_creation() { new_test_ext().execute_with(|| { @@ -1974,16 +2020,16 @@ fn cannot_block_pool_creation() { // User 2 is the attacker let attacker = 2; - let ed = get_ed(); + let ed = get_native_ed(); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), attacker, 10000 + ed)); - // The target pool the user wants to create is Native <=> Asset(2) - let token_1 = NativeOrAssetId::Native; - let token_2 = NativeOrAssetId::Asset(2); + // The target pool the user wants to create is Native <=> WithId(2) + let token_1 = NativeOrWithId::Native; + let token_2 = NativeOrWithId::WithId(2); // Attacker computes the still non-existing pool account for the target pair let pool_account = - AssetConversion::get_pool_account(&AssetConversion::get_pool_id(token_2, token_1)); + ::PoolLocator::address(&(token_1.clone(), token_2.clone())).unwrap(); // And transfers the ED to that pool account assert_ok!(Balances::transfer_allow_death( RuntimeOrigin::signed(attacker), @@ -1992,21 +2038,21 @@ fn cannot_block_pool_creation() { )); // Then, the attacker creates 14 tokens and sends one of each to the pool account for i in 10..25 { - create_tokens(attacker, vec![NativeOrAssetId::Asset(i)]); + create_tokens(attacker, vec![NativeOrWithId::WithId(i)]); assert_ok!(Assets::mint(RuntimeOrigin::signed(attacker), i, attacker, 1000)); assert_ok!(Assets::transfer(RuntimeOrigin::signed(attacker), i, pool_account, 1)); } // User can still create the pool - create_tokens(user, vec![token_2]); + create_tokens(user, vec![token_2.clone()]); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2) + Box::new(token_1.clone()), + Box::new(token_2.clone()) )); - // User has to transfer one Asset(2) token to the pool account (otherwise add_liquidity will - // fail with `AssetTwoDepositDidNotMeetMinimum`) + // User has to transfer one WithId(2) token to the pool account (otherwise add_liquidity + // will fail with `AssetTwoDepositDidNotMeetMinimum`) assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 10000 + ed)); assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user, 10000)); assert_ok!(Assets::transfer(RuntimeOrigin::signed(user), 2, pool_account, 1)); @@ -2014,8 +2060,8 @@ fn cannot_block_pool_creation() { // add_liquidity shouldn't fail because of the number of consumers assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2), + Box::new(token_1.clone()), + Box::new(token_2.clone()), 10000, 100, 10000, @@ -2030,24 +2076,24 @@ fn swap_transactional() { new_test_ext().execute_with(|| { let user = 1; let user2 = 2; - let token_1 = NativeOrAssetId::Native; - let token_2 = NativeOrAssetId::Asset(2); - let token_3 = NativeOrAssetId::Asset(3); + let token_1 = NativeOrWithId::Native; + let token_2 = NativeOrWithId::WithId(2); + let token_3 = NativeOrWithId::WithId(3); let asset_ed = 150; - create_tokens_with_ed(user, vec![token_2, token_3], asset_ed); + create_tokens_with_ed(user, vec![token_2.clone(), token_3.clone()], asset_ed); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2) + Box::new(token_1.clone()), + Box::new(token_2.clone()) )); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_3) + Box::new(token_1.clone()), + Box::new(token_3.clone()) )); - let ed = get_ed(); + let ed = get_native_ed(); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 20000 + ed)); assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user, 1000)); assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 3, user, 1000)); @@ -2061,8 +2107,8 @@ fn swap_transactional() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2), + Box::new(token_1.clone()), + Box::new(token_2.clone()), liquidity1, liquidity2, 1, @@ -2072,8 +2118,8 @@ fn swap_transactional() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_3), + Box::new(token_1.clone()), + Box::new(token_3.clone()), liquidity1, liquidity2, 1, @@ -2081,8 +2127,10 @@ fn swap_transactional() { user, )); - let pool_1 = AssetConversion::get_pool_account(&(token_1, token_2)); - let pool_2 = AssetConversion::get_pool_account(&(token_1, token_3)); + let pool_1 = + ::PoolLocator::address(&(token_1.clone(), token_2.clone())).unwrap(); + let pool_2 = + ::PoolLocator::address(&(token_1.clone(), token_3.clone())).unwrap(); assert_eq!(Balances::balance(&pool_1), liquidity1); assert_eq!(Assets::balance(2, &pool_1), liquidity2); @@ -2093,7 +2141,7 @@ fn swap_transactional() { let expected_out = liquidity2 - asset_ed + 1; let amount_in = AssetConversion::balance_path_from_amount_out( expected_out, - vec![token_2, token_1, token_3].try_into().unwrap(), + vec![token_2.clone(), token_1.clone(), token_3.clone()].try_into().unwrap(), ) .unwrap() .first() @@ -2101,42 +2149,42 @@ fn swap_transactional() { .unwrap(); // swap credit with `swap_tokens_for_exact_tokens` transactional - let credit_in = Assets::issue(2, amount_in); - let credit_in_err_expected = Assets::issue(2, amount_in); + let credit_in = NativeAndAssets::issue(token_2.clone(), amount_in); + let credit_in_err_expected = NativeAndAssets::issue(token_2.clone(), amount_in); // avoiding drop of any credit, to assert any storage mutation from an actual call. let error; assert_storage_noop!( error = >::swap_tokens_for_exact_tokens( - vec![token_2, token_1, token_3], - credit_in.into(), + vec![token_2.clone(), token_1.clone(), token_3.clone()], + credit_in, expected_out, ) .unwrap_err() ); - assert_eq!(error, (credit_in_err_expected.into(), TokenError::NotExpendable.into())); + assert_eq!(error, (credit_in_err_expected, TokenError::NotExpendable.into())); // swap credit with `swap_exact_tokens_for_tokens` transactional - let credit_in = Assets::issue(2, amount_in); - let credit_in_err_expected = Assets::issue(2, amount_in); + let credit_in = NativeAndAssets::issue(token_2.clone(), amount_in); + let credit_in_err_expected = NativeAndAssets::issue(token_2.clone(), amount_in); // avoiding drop of any credit, to assert any storage mutation from an actual call. let error; assert_storage_noop!( error = >::swap_exact_tokens_for_tokens( - vec![token_2, token_1, token_3], - credit_in.into(), + vec![token_2.clone(), token_1.clone(), token_3.clone()], + credit_in, Some(expected_out), ) .unwrap_err() ); - assert_eq!(error, (credit_in_err_expected.into(), TokenError::NotExpendable.into())); + assert_eq!(error, (credit_in_err_expected, TokenError::NotExpendable.into())); // swap with `swap_exact_tokens_for_tokens` transactional assert_noop!( >::swap_exact_tokens_for_tokens( user2, - vec![token_2, token_1, token_3], - amount_in.into(), - Some(expected_out.into()), + vec![token_2.clone(), token_1.clone(), token_3.clone()], + amount_in, + Some(expected_out), user2, true, ), @@ -2147,9 +2195,9 @@ fn swap_transactional() { assert_noop!( >::swap_tokens_for_exact_tokens( user2, - vec![token_2, token_1, token_3], - expected_out.into(), - Some(amount_in.into()), + vec![token_2.clone(), token_1.clone(), token_3.clone()], + expected_out, + Some(amount_in), user2, true, ), @@ -2167,17 +2215,17 @@ fn swap_transactional() { fn swap_credit_returns_change() { new_test_ext().execute_with(|| { let user = 1; - let token_1 = NativeOrAssetId::Native; - let token_2 = NativeOrAssetId::Asset(2); + let token_1 = NativeOrWithId::Native; + let token_2 = NativeOrWithId::WithId(2); - create_tokens(user, vec![token_2]); + create_tokens(user, vec![token_2.clone()]); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2) + Box::new(token_1.clone()), + Box::new(token_2.clone()) )); - let ed = get_ed(); + let ed = get_native_ed(); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 20000 + ed)); assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user, 1000)); @@ -2186,8 +2234,8 @@ fn swap_credit_returns_change() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2), + Box::new(token_1.clone()), + Box::new(token_2.clone()), liquidity1, liquidity2, 1, @@ -2195,21 +2243,22 @@ fn swap_credit_returns_change() { user, )); - let expected_change = Balances::issue(100); - let expected_credit_out = Assets::issue(2, 20); + let expected_change = NativeAndAssets::issue(token_1.clone(), 100); + let expected_credit_out = NativeAndAssets::issue(token_2.clone(), 20); let amount_in_max = AssetConversion::get_amount_in(&expected_credit_out.peek(), &liquidity1, &liquidity2) .unwrap(); - let credit_in = Balances::issue(amount_in_max + expected_change.peek()); + let credit_in = + NativeAndAssets::issue(token_1.clone(), amount_in_max + expected_change.peek()); assert_ok!( >::swap_tokens_for_exact_tokens( - vec![token_1, token_2], - credit_in.into(), + vec![token_1.clone(), token_2.clone()], + credit_in, expected_credit_out.peek(), ), - (expected_credit_out.into(), expected_change.into()) + (expected_credit_out, expected_change) ); }) } @@ -2219,17 +2268,17 @@ fn swap_credit_insufficient_amount_bounds() { new_test_ext().execute_with(|| { let user = 1; let user2 = 2; - let token_1 = NativeOrAssetId::Native; - let token_2 = NativeOrAssetId::Asset(2); + let token_1 = NativeOrWithId::Native; + let token_2 = NativeOrWithId::WithId(2); - create_tokens(user, vec![token_2]); + create_tokens(user, vec![token_2.clone()]); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2) + Box::new(token_1.clone()), + Box::new(token_2.clone()) )); - let ed = get_ed(); + let ed = get_native_ed(); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 20000 + ed)); assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user, 1000)); @@ -2241,8 +2290,8 @@ fn swap_credit_insufficient_amount_bounds() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2), + Box::new(token_1.clone()), + Box::new(token_2.clone()), liquidity1, liquidity2, 1, @@ -2255,11 +2304,11 @@ fn swap_credit_insufficient_amount_bounds() { let amount_in = AssetConversion::get_amount_in(&(amount_out_min - 1), &liquidity2, &liquidity1) .unwrap(); - let credit_in = Balances::issue(amount_in); - let expected_credit_in = Balances::issue(amount_in); + let credit_in = NativeAndAssets::issue(token_1.clone(), amount_in); + let expected_credit_in = NativeAndAssets::issue(token_1.clone(), amount_in); let error = >::swap_exact_tokens_for_tokens( - vec![token_1, token_2], - credit_in.into(), + vec![token_1.clone(), token_2.clone()], + credit_in, Some(amount_out_min), ) .unwrap_err(); @@ -2272,17 +2321,17 @@ fn swap_credit_insufficient_amount_bounds() { let amount_out = 20; let amount_in_max = AssetConversion::get_amount_in(&(amount_out - 1), &liquidity2, &liquidity1).unwrap(); - let credit_in = Balances::issue(amount_in_max); - let expected_credit_in = Balances::issue(amount_in_max); + let credit_in = NativeAndAssets::issue(token_1.clone(), amount_in_max); + let expected_credit_in = NativeAndAssets::issue(token_1.clone(), amount_in_max); let error = >::swap_tokens_for_exact_tokens( - vec![token_1, token_2], - credit_in.into(), + vec![token_1.clone(), token_2.clone()], + credit_in, amount_out, ) .unwrap_err(); assert_eq!( error, - (expected_credit_in.into(), Error::::ProvidedMaximumNotSufficientForSwap.into()) + (expected_credit_in, Error::::ProvidedMaximumNotSufficientForSwap.into()) ); }) } @@ -2292,17 +2341,17 @@ fn swap_credit_zero_amount() { new_test_ext().execute_with(|| { let user = 1; let user2 = 2; - let token_1 = NativeOrAssetId::Native; - let token_2 = NativeOrAssetId::Asset(2); + let token_1 = NativeOrWithId::Native; + let token_2 = NativeOrWithId::WithId(2); - create_tokens(user, vec![token_2]); + create_tokens(user, vec![token_2.clone()]); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2) + Box::new(token_1.clone()), + Box::new(token_2.clone()) )); - let ed = get_ed(); + let ed = get_native_ed(); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 20000 + ed)); assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user, 1000)); @@ -2314,8 +2363,8 @@ fn swap_credit_zero_amount() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2), + Box::new(token_1.clone()), + Box::new(token_2.clone()), liquidity1, liquidity2, 1, @@ -2324,10 +2373,10 @@ fn swap_credit_zero_amount() { )); // swap with zero credit fails for `swap_exact_tokens_for_tokens` - let credit_in = Credit::native_zero(); - let expected_credit_in = Credit::native_zero(); + let credit_in = CreditOf::::zero(token_1.clone()); + let expected_credit_in = CreditOf::::zero(token_1.clone()); let error = >::swap_exact_tokens_for_tokens( - vec![token_1, token_2], + vec![token_1.clone(), token_2.clone()], credit_in, None, ) @@ -2335,10 +2384,10 @@ fn swap_credit_zero_amount() { assert_eq!(error, (expected_credit_in, Error::::ZeroAmount.into())); // swap with zero credit fails for `swap_tokens_for_exact_tokens` - let credit_in = Credit::native_zero(); - let expected_credit_in = Credit::native_zero(); + let credit_in = CreditOf::::zero(token_1.clone()); + let expected_credit_in = CreditOf::::zero(token_1.clone()); let error = >::swap_tokens_for_exact_tokens( - vec![token_1, token_2], + vec![token_1.clone(), token_2.clone()], credit_in, 10, ) @@ -2346,26 +2395,26 @@ fn swap_credit_zero_amount() { assert_eq!(error, (expected_credit_in, Error::::ZeroAmount.into())); // swap with zero amount_out_min fails for `swap_exact_tokens_for_tokens` - let credit_in = Balances::issue(10); - let expected_credit_in = Balances::issue(10); + let credit_in = NativeAndAssets::issue(token_1.clone(), 10); + let expected_credit_in = NativeAndAssets::issue(token_1.clone(), 10); let error = >::swap_exact_tokens_for_tokens( - vec![token_1, token_2], - credit_in.into(), + vec![token_1.clone(), token_2.clone()], + credit_in, Some(0), ) .unwrap_err(); - assert_eq!(error, (expected_credit_in.into(), Error::::ZeroAmount.into())); + assert_eq!(error, (expected_credit_in, Error::::ZeroAmount.into())); // swap with zero amount_out fails with `swap_tokens_for_exact_tokens` fails - let credit_in = Balances::issue(10); - let expected_credit_in = Balances::issue(10); + let credit_in = NativeAndAssets::issue(token_1.clone(), 10); + let expected_credit_in = NativeAndAssets::issue(token_1.clone(), 10); let error = >::swap_tokens_for_exact_tokens( - vec![token_1, token_2], - credit_in.into(), + vec![token_1.clone(), token_2.clone()], + credit_in, 0, ) .unwrap_err(); - assert_eq!(error, (expected_credit_in.into(), Error::::ZeroAmount.into())); + assert_eq!(error, (expected_credit_in, Error::::ZeroAmount.into())); }); } @@ -2374,17 +2423,17 @@ fn swap_credit_invalid_path() { new_test_ext().execute_with(|| { let user = 1; let user2 = 2; - let token_1 = NativeOrAssetId::Native; - let token_2 = NativeOrAssetId::Asset(2); + let token_1 = NativeOrWithId::Native; + let token_2 = NativeOrWithId::WithId(2); - create_tokens(user, vec![token_2]); + create_tokens(user, vec![token_2.clone()]); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2) + Box::new(token_1.clone()), + Box::new(token_2.clone()) )); - let ed = get_ed(); + let ed = get_native_ed(); assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), user, 20000 + ed)); assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user, 1000)); @@ -2396,8 +2445,8 @@ fn swap_credit_invalid_path() { assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), - Box::new(token_1), - Box::new(token_2), + Box::new(token_1.clone()), + Box::new(token_2.clone()), liquidity1, liquidity2, 1, @@ -2406,47 +2455,44 @@ fn swap_credit_invalid_path() { )); // swap with credit_in.asset different from path[0] asset fails - let credit_in = Balances::issue(10); - let expected_credit_in = Balances::issue(10); + let credit_in = NativeAndAssets::issue(token_1.clone(), 10); + let expected_credit_in = NativeAndAssets::issue(token_1.clone(), 10); let error = >::swap_exact_tokens_for_tokens( - vec![token_2, token_1], - credit_in.into(), + vec![token_2.clone(), token_1.clone()], + credit_in, None, ) .unwrap_err(); - assert_eq!(error, (expected_credit_in.into(), Error::::InvalidPath.into())); + assert_eq!(error, (expected_credit_in, Error::::InvalidPath.into())); // swap with credit_in.asset different from path[0] asset fails - let credit_in = Assets::issue(2, 10); - let expected_credit_in = Assets::issue(2, 10); + let credit_in = NativeAndAssets::issue(token_2.clone(), 10); + let expected_credit_in = NativeAndAssets::issue(token_2.clone(), 10); let error = >::swap_tokens_for_exact_tokens( - vec![token_1, token_2], - credit_in.into(), + vec![token_1.clone(), token_2.clone()], + credit_in, 10, ) .unwrap_err(); - assert_eq!(error, (expected_credit_in.into(), Error::::InvalidPath.into())); + assert_eq!(error, (expected_credit_in, Error::::InvalidPath.into())); // swap with path.len < 2 fails - let credit_in = Balances::issue(10); - let expected_credit_in = Balances::issue(10); + let credit_in = NativeAndAssets::issue(token_1.clone(), 10); + let expected_credit_in = NativeAndAssets::issue(token_1.clone(), 10); let error = >::swap_exact_tokens_for_tokens( - vec![token_2], - credit_in.into(), + vec![token_2.clone()], + credit_in, None, ) .unwrap_err(); - assert_eq!(error, (expected_credit_in.into(), Error::::InvalidPath.into())); + assert_eq!(error, (expected_credit_in, Error::::InvalidPath.into())); // swap with path.len < 2 fails - let credit_in = Assets::issue(2, 10); - let expected_credit_in = Assets::issue(2, 10); - let error = >::swap_tokens_for_exact_tokens( - vec![], - credit_in.into(), - 10, - ) - .unwrap_err(); - assert_eq!(error, (expected_credit_in.into(), Error::::InvalidPath.into())); + let credit_in = NativeAndAssets::issue(token_2.clone(), 10); + let expected_credit_in = NativeAndAssets::issue(token_2.clone(), 10); + let error = + >::swap_tokens_for_exact_tokens(vec![], credit_in, 10) + .unwrap_err(); + assert_eq!(error, (expected_credit_in, Error::::InvalidPath.into())); }); } diff --git a/substrate/frame/asset-conversion/src/types.rs b/substrate/frame/asset-conversion/src/types.rs index d5ad8f590653..049a028be9ca 100644 --- a/substrate/frame/asset-conversion/src/types.rs +++ b/substrate/frame/asset-conversion/src/types.rs @@ -16,16 +16,9 @@ // limitations under the License. use super::*; - use codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; -use sp_std::{cmp::Ordering, marker::PhantomData}; - -/// Pool ID. -/// -/// The pool's `AccountId` is derived from this type. Any changes to the type may necessitate a -/// migration. -pub(super) type PoolIdOf = (::MultiAssetId, ::MultiAssetId); +use sp_std::marker::PhantomData; /// Represents a swap path with associated asset amounts indicating how much of the asset needs to /// be deposited to get the following asset's amount withdrawn (this is inclusive of fees). @@ -35,7 +28,10 @@ pub(super) type PoolIdOf = (::MultiAssetId, ::Multi /// 1. `asset(asset1, amount_in)` take from `user` and move to the pool(asset1, asset2); /// 2. `asset(asset2, amount_out2)` transfer from pool(asset1, asset2) to pool(asset2, asset3); /// 3. `asset(asset3, amount_out3)` move from pool(asset2, asset3) to `user`. -pub(super) type BalancePath = Vec<(::MultiAssetId, ::Balance)>; +pub(super) type BalancePath = Vec<(::AssetKind, ::Balance)>; + +/// Credit of [Config::Assets]. +pub type CreditOf = Credit<::AccountId, ::Assets>; /// Stores the lp_token asset id a particular pool has been assigned. #[derive(Decode, Encode, Default, PartialEq, Eq, MaxEncodedLen, TypeInfo)] @@ -44,214 +40,94 @@ pub struct PoolInfo { pub lp_token: PoolAssetId, } -/// A trait that converts between a MultiAssetId and either the native currency or an AssetId. -pub trait MultiAssetIdConverter { - /// Returns the MultiAssetId representing the native currency of the chain. - fn get_native() -> MultiAssetId; - - /// Returns true if the given MultiAssetId is the native currency. - fn is_native(asset: &MultiAssetId) -> bool; - - /// If it's not native, returns the AssetId for the given MultiAssetId. - fn try_convert(asset: &MultiAssetId) -> MultiAssetIdConversionResult; -} - -/// Result of `MultiAssetIdConverter::try_convert`. -#[cfg_attr(feature = "std", derive(PartialEq, Debug))] -pub enum MultiAssetIdConversionResult { - /// Input asset is successfully converted. Means that converted asset is supported. - Converted(AssetId), - /// Means that input asset is the chain's native asset, if it has one, so no conversion (see - /// `MultiAssetIdConverter::get_native`). - Native, - /// Means input asset is not supported for pool. - Unsupported(MultiAssetId), -} - -/// Benchmark Helper -#[cfg(feature = "runtime-benchmarks")] -pub trait BenchmarkHelper { - /// Returns an `AssetId` from a given integer. - fn asset_id(asset_id: u32) -> AssetId; - - /// Returns a `MultiAssetId` from a given integer. - fn multiasset_id(asset_id: u32) -> MultiAssetId; -} - -#[cfg(feature = "runtime-benchmarks")] -impl BenchmarkHelper for () -where - AssetId: From, - MultiAssetId: From, -{ - fn asset_id(asset_id: u32) -> AssetId { - asset_id.into() - } - - fn multiasset_id(asset_id: u32) -> MultiAssetId { - asset_id.into() +/// Provides means to resolve the `PoolId` and `AccountId` from a pair of assets. +/// +/// Resulting `PoolId` remains consistent whether the asset pair is presented as (asset1, asset2) +/// or (asset2, asset1). The derived `AccountId` may serve as an address for liquidity provider +/// tokens. +pub trait PoolLocator { + /// Retrieves the account address associated with a valid `PoolId`. + fn address(id: &PoolId) -> Result; + /// Identifies the `PoolId` for a given pair of assets. + /// + /// Returns an error if the asset pair isn't supported. + fn pool_id(asset1: &AssetKind, asset2: &AssetKind) -> Result; + /// Retrieves the account address associated with a given asset pair. + /// + /// Returns an error if the asset pair isn't supported. + fn pool_address(asset1: &AssetKind, asset2: &AssetKind) -> Result { + if let Ok(id) = Self::pool_id(asset1, asset2) { + Self::address(&id) + } else { + Err(()) + } } } -/// An implementation of MultiAssetId that can be either Native or an asset. -#[derive(Decode, Encode, Default, MaxEncodedLen, TypeInfo, Clone, Copy, Debug)] -pub enum NativeOrAssetId +/// Pool locator that mandates the inclusion of the specified `FirstAsset` in every asset pair. +/// +/// The `PoolId` is represented as a tuple of `AssetKind`s with `FirstAsset` always positioned as +/// the first element. +pub struct WithFirstAsset( + PhantomData<(FirstAsset, AccountId, AssetKind)>, +); +impl PoolLocator + for WithFirstAsset where - AssetId: Ord, + AssetKind: PartialEq + Clone + Encode, + AccountId: Decode, + FirstAsset: Get, { - /// Native asset. For example, on the Polkadot Asset Hub this would be DOT. - #[default] - Native, - /// A non-native asset id. - Asset(AssetId), -} - -impl From for NativeOrAssetId { - fn from(asset: AssetId) -> Self { - Self::Asset(asset) - } -} - -impl Ord for NativeOrAssetId { - fn cmp(&self, other: &Self) -> Ordering { - match (self, other) { - (Self::Native, Self::Native) => Ordering::Equal, - (Self::Native, Self::Asset(_)) => Ordering::Less, - (Self::Asset(_), Self::Native) => Ordering::Greater, - (Self::Asset(id1), Self::Asset(id2)) => ::cmp(id1, id2), + fn pool_id(asset1: &AssetKind, asset2: &AssetKind) -> Result<(AssetKind, AssetKind), ()> { + let first = FirstAsset::get(); + match true { + _ if asset1 == asset2 => Err(()), + _ if first == *asset1 => Ok((first, asset2.clone())), + _ if first == *asset2 => Ok((first, asset1.clone())), + _ => Err(()), } } -} -impl PartialOrd for NativeOrAssetId { - fn partial_cmp(&self, other: &Self) -> Option { - Some(::cmp(self, other)) - } -} -impl PartialEq for NativeOrAssetId { - fn eq(&self, other: &Self) -> bool { - self.cmp(other) == Ordering::Equal + fn address(id: &(AssetKind, AssetKind)) -> Result { + let encoded = sp_io::hashing::blake2_256(&Encode::encode(id)[..]); + Decode::decode(&mut TrailingZeroInput::new(encoded.as_ref())).map_err(|_| ()) } } -impl Eq for NativeOrAssetId {} - -/// Converts between a MultiAssetId and an AssetId (or the native currency). -pub struct NativeOrAssetIdConverter { - _phantom: PhantomData, -} -impl MultiAssetIdConverter, AssetId> - for NativeOrAssetIdConverter +/// Pool locator where the `PoolId` is a tuple of `AssetKind`s arranged in ascending order. +pub struct Ascending(PhantomData<(AccountId, AssetKind)>); +impl PoolLocator + for Ascending +where + AssetKind: PartialOrd + Clone + Encode, + AccountId: Decode, { - fn get_native() -> NativeOrAssetId { - NativeOrAssetId::Native - } - - fn is_native(asset: &NativeOrAssetId) -> bool { - *asset == Self::get_native() - } - - fn try_convert( - asset: &NativeOrAssetId, - ) -> MultiAssetIdConversionResult, AssetId> { - match asset { - NativeOrAssetId::Asset(asset) => MultiAssetIdConversionResult::Converted(asset.clone()), - NativeOrAssetId::Native => MultiAssetIdConversionResult::Native, - } - } -} - -/// Credit of [Config::Currency]. -/// -/// Implies a negative imbalance in the system that can be placed into an account or alter the total -/// supply. -pub type NativeCredit = - CreditFungible<::AccountId, ::Currency>; - -/// Credit (aka negative imbalance) of [Config::Assets]. -/// -/// Implies a negative imbalance in the system that can be placed into an account or alter the total -/// supply. -pub type AssetCredit = - CreditFungibles<::AccountId, ::Assets>; - -/// Credit that can be either [`NativeCredit`] or [`AssetCredit`]. -/// -/// Implies a negative imbalance in the system that can be placed into an account or alter the total -/// supply. -#[derive(RuntimeDebug, Eq, PartialEq)] -pub enum Credit { - /// Native credit. - Native(NativeCredit), - /// Asset credit. - Asset(AssetCredit), -} - -impl From> for Credit { - fn from(value: NativeCredit) -> Self { - Credit::Native(value) - } -} - -impl From> for Credit { - fn from(value: AssetCredit) -> Self { - Credit::Asset(value) - } -} - -impl TryInto> for Credit { - type Error = (); - fn try_into(self) -> Result, ()> { - match self { - Credit::Native(c) => Ok(c), + fn pool_id(asset1: &AssetKind, asset2: &AssetKind) -> Result<(AssetKind, AssetKind), ()> { + match true { + _ if asset1 > asset2 => Ok((asset2.clone(), asset1.clone())), + _ if asset1 < asset2 => Ok((asset1.clone(), asset2.clone())), _ => Err(()), } } -} - -impl TryInto> for Credit { - type Error = (); - fn try_into(self) -> Result, ()> { - match self { - Credit::Asset(c) => Ok(c), - _ => Err(()), - } + fn address(id: &(AssetKind, AssetKind)) -> Result { + let encoded = sp_io::hashing::blake2_256(&Encode::encode(id)[..]); + Decode::decode(&mut TrailingZeroInput::new(encoded.as_ref())).map_err(|_| ()) } } -impl Credit { - /// Create zero native credit. - pub fn native_zero() -> Self { - NativeCredit::::zero().into() - } - - /// Amount of `self`. - pub fn peek(&self) -> T::Balance { - match self { - Credit::Native(c) => c.peek(), - Credit::Asset(c) => c.peek(), - } - } - - /// Asset class of `self`. - pub fn asset(&self) -> T::MultiAssetId { - match self { - Credit::Native(_) => T::MultiAssetIdConverter::get_native(), - Credit::Asset(c) => c.asset().into(), - } +/// Pool locator that chains the `First` and `Second` implementations of [`PoolLocator`]. +/// +/// If the `First` implementation fails, it falls back to the `Second`. +pub struct Chain(PhantomData<(First, Second)>); +impl PoolLocator + for Chain +where + First: PoolLocator, + Second: PoolLocator, +{ + fn pool_id(asset1: &AssetKind, asset2: &AssetKind) -> Result<(AssetKind, AssetKind), ()> { + First::pool_id(asset1, asset2).or(Second::pool_id(asset1, asset2)) } - - /// Consume `self` and return two independent instances; the first is guaranteed to be at most - /// `amount` and the second will be the remainder. - pub fn split(self, amount: T::Balance) -> (Self, Self) { - match self { - Credit::Native(c) => { - let (left, right) = c.split(amount); - (left.into(), right.into()) - }, - Credit::Asset(c) => { - let (left, right) = c.split(amount); - (left.into(), right.into()) - }, - } + fn address(id: &(AssetKind, AssetKind)) -> Result { + First::address(id).or(Second::address(id)) } } From f49074443d4242bbed268e2d252cfb9e41444bad Mon Sep 17 00:00:00 2001 From: muharem Date: Wed, 25 Oct 2023 16:26:08 +0200 Subject: [PATCH 54/98] asset conversion tx payment --- .../asset-conversion-tx-payment/src/lib.rs | 1 - .../asset-conversion-tx-payment/src/mock.rs | 43 ++++++++++--------- .../src/payment.rs | 17 ++++---- .../asset-conversion-tx-payment/src/tests.rs | 43 +++++++++---------- 4 files changed, 53 insertions(+), 51 deletions(-) diff --git a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/lib.rs b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/lib.rs index 04a71e2ff426..ed0ed56e6e07 100644 --- a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/lib.rs +++ b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/lib.rs @@ -69,7 +69,6 @@ mod tests; mod payment; use frame_support::traits::tokens::AssetId; -use pallet_asset_conversion::MultiAssetIdConverter; pub use payment::*; /// Type aliases used for interaction with `OnChargeTransaction`. diff --git a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs index 2a18b620edef..373ecef27efc 100644 --- a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs +++ b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs @@ -16,20 +16,25 @@ use super::*; use crate as pallet_asset_conversion_tx_payment; -use codec; use frame_support::{ dispatch::DispatchClass, instances::Instance2, ord_parameter_types, pallet_prelude::*, parameter_types, - traits::{AsEnsureOriginWithArg, ConstU32, ConstU64, ConstU8, Imbalance, OnUnbalanced}, + traits::{ + tokens::{ + fungible::{NativeFromLeft, NativeOrWithId, UnionOf}, + imbalance::ResolveAssetTo, + }, + AsEnsureOriginWithArg, ConstU32, ConstU64, ConstU8, Imbalance, OnUnbalanced, + }, weights::{Weight, WeightToFee as WeightToFeeT}, PalletId, }; use frame_system as system; use frame_system::{EnsureRoot, EnsureSignedBy}; -use pallet_asset_conversion::{NativeOrAssetId, NativeOrAssetIdConverter}; +use pallet_asset_conversion::{Ascending, Chain, WithFirstAsset}; use pallet_transaction_payment::CurrencyAdapter; use sp_core::H256; use sp_runtime::{ @@ -223,10 +228,9 @@ impl pallet_assets::Config for Runtime { parameter_types! { pub const AssetConversionPalletId: PalletId = PalletId(*b"py/ascon"); - pub storage AllowMultiAssetPools: bool = false; - // should be non-zero if AllowMultiAssetPools is true, otherwise can be zero pub storage LiquidityWithdrawalFee: Permill = Permill::from_percent(0); pub const MaxSwapPathLength: u32 = 4; + pub const Native: NativeOrWithId = NativeOrWithId::Native; } ord_parameter_types! { @@ -235,27 +239,26 @@ ord_parameter_types! { impl pallet_asset_conversion::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type Currency = Balances; - type AssetId = u32; + type Balance = Balance; + type HigherPrecisionBalance = u128; + type AssetKind = NativeOrWithId; + type Assets = UnionOf, AccountId>; + type PoolId = (Self::AssetKind, Self::AssetKind); + type PoolLocator = Chain< + WithFirstAsset>, + Ascending>, + >; type PoolAssetId = u32; - type Assets = Assets; type PoolAssets = PoolAssets; + type PoolSetupFee = ConstU64<100>; // should be more or equal to the existential deposit + type PoolSetupFeeAsset = Native; + type PoolSetupFeeTarget = ResolveAssetTo; type PalletId = AssetConversionPalletId; - type WeightInfo = (); type LPFee = ConstU32<3>; // means 0.3% - type PoolSetupFee = ConstU64<100>; // should be more or equal to the existential deposit - type PoolSetupFeeReceiver = AssetConversionOrigin; type LiquidityWithdrawalFee = LiquidityWithdrawalFee; - type AllowMultiAssetPools = AllowMultiAssetPools; type MaxSwapPathLength = MaxSwapPathLength; type MintMinLiquidity = ConstU64<100>; // 100 is good enough when the main currency has 12 decimals. - - type Balance = u64; - type HigherPrecisionBalance = u128; - - type MultiAssetId = NativeOrAssetId; - type MultiAssetIdConverter = NativeOrAssetIdConverter; - + type WeightInfo = (); pallet_asset_conversion::runtime_benchmarks_enabled! { type BenchmarkHelper = (); } @@ -264,5 +267,5 @@ impl pallet_asset_conversion::Config for Runtime { impl Config for Runtime { type RuntimeEvent = RuntimeEvent; type Fungibles = Assets; - type OnChargeAssetTransaction = AssetConversionAdapter; + type OnChargeAssetTransaction = AssetConversionAdapter; } diff --git a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/payment.rs b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/payment.rs index ccea9e55bbed..f2f2c57bb376 100644 --- a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/payment.rs +++ b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/payment.rs @@ -24,7 +24,7 @@ use frame_support::{ }; use pallet_asset_conversion::Swap; use sp_runtime::{ - traits::{DispatchInfoOf, PostDispatchInfoOf, Zero}, + traits::{DispatchInfoOf, Get, PostDispatchInfoOf, Zero}, transaction_validity::InvalidTransaction, Saturating, }; @@ -76,16 +76,17 @@ pub trait OnChargeAssetTransaction { /// Implements the asset transaction for a balance to asset converter (implementing [`Swap`]). /// /// The converter is given the complete fee in terms of the asset used for the transaction. -pub struct AssetConversionAdapter(PhantomData<(C, CON)>); +pub struct AssetConversionAdapter(PhantomData<(C, CON, N)>); /// Default implementation for a runtime instantiating this pallet, an asset to native swapper. -impl OnChargeAssetTransaction for AssetConversionAdapter +impl OnChargeAssetTransaction for AssetConversionAdapter where + N: Get, T: Config, C: Inspect<::AccountId>, - CON: Swap, MultiAssetId = T::MultiAssetId>, + CON: Swap, AssetKind = T::AssetKind>, BalanceOf: Into>, - T::MultiAssetId: From>, + T::AssetKind: From>, BalanceOf: IsType<::AccountId>>::Balance>, { type Balance = BalanceOf; @@ -116,7 +117,7 @@ where let asset_consumed = CON::swap_tokens_for_exact_tokens( who.clone(), - vec![asset_id.into(), T::MultiAssetIdConverter::get_native()], + vec![asset_id.into(), N::get()], native_asset_required, None, who.clone(), @@ -168,8 +169,8 @@ where match CON::swap_exact_tokens_for_tokens( who.clone(), // we already deposited the native to `who` vec![ - T::MultiAssetIdConverter::get_native(), // we provide the native - asset_id.into(), // we want asset_id back + N::get(), // we provide the native + asset_id.into(), // we want asset_id back ], swap_back, // amount of the native asset to convert to `asset_id` None, // no minimum amount back diff --git a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/tests.rs b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/tests.rs index d50a05164235..62faed269d37 100644 --- a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/tests.rs +++ b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/tests.rs @@ -20,14 +20,13 @@ use frame_support::{ dispatch::{DispatchInfo, PostDispatchInfo}, pallet_prelude::*, traits::{ - fungible::Inspect, + fungible::{Inspect, NativeOrWithId}, fungibles::{Inspect as FungiblesInspect, Mutate}, }, weights::Weight, }; use frame_system as system; use mock::{ExtrinsicBaseWeight, *}; -use pallet_asset_conversion::NativeOrAssetId; use pallet_balances::Call as BalancesCall; use sp_runtime::{traits::StaticLookup, BuildStorage}; @@ -127,12 +126,12 @@ fn setup_lp(asset_id: u32, balance_factor: u64) { 10_000 * balance_factor + ed_asset )); - let token_1 = NativeOrAssetId::Native; - let token_2 = NativeOrAssetId::Asset(asset_id); + let token_1 = NativeOrWithId::Native; + let token_2 = NativeOrWithId::WithId(asset_id); assert_ok!(AssetConversion::create_pool( RuntimeOrigin::signed(lp_provider), - Box::new(token_1), - Box::new(token_2) + Box::new(token_1.clone()), + Box::new(token_2.clone()) )); assert_ok!(AssetConversion::add_liquidity( @@ -228,8 +227,8 @@ fn transaction_payment_in_asset_possible() { let fee_in_native = base_weight + tx_weight + len as u64; let input_quote = AssetConversion::quote_price_tokens_for_exact_tokens( - NativeOrAssetId::Asset(asset_id), - NativeOrAssetId::Native, + NativeOrWithId::WithId(asset_id), + NativeOrWithId::Native, fee_in_native, true, ); @@ -337,8 +336,8 @@ fn transaction_payment_without_fee() { let len = 10; let fee_in_native = base_weight + weight + len as u64; let input_quote = AssetConversion::quote_price_tokens_for_exact_tokens( - NativeOrAssetId::Asset(asset_id), - NativeOrAssetId::Native, + NativeOrWithId::WithId(asset_id), + NativeOrWithId::Native, fee_in_native, true, ); @@ -355,8 +354,8 @@ fn transaction_payment_without_fee() { assert_eq!(Assets::balance(asset_id, caller), balance - fee_in_asset); let refund = AssetConversion::quote_price_exact_tokens_for_tokens( - NativeOrAssetId::Native, - NativeOrAssetId::Asset(asset_id), + NativeOrWithId::Native, + NativeOrWithId::WithId(asset_id), fee_in_native, true, ) @@ -412,8 +411,8 @@ fn asset_transaction_payment_with_tip_and_refund() { let len = 10; let fee_in_native = base_weight + weight + len as u64 + tip; let input_quote = AssetConversion::quote_price_tokens_for_exact_tokens( - NativeOrAssetId::Asset(asset_id), - NativeOrAssetId::Native, + NativeOrWithId::WithId(asset_id), + NativeOrWithId::Native, fee_in_native, true, ); @@ -428,8 +427,8 @@ fn asset_transaction_payment_with_tip_and_refund() { let final_weight = 50; let expected_fee = fee_in_native - final_weight - tip; let expected_token_refund = AssetConversion::quote_price_exact_tokens_for_tokens( - NativeOrAssetId::Native, - NativeOrAssetId::Asset(asset_id), + NativeOrWithId::Native, + NativeOrWithId::WithId(asset_id), fee_in_native - expected_fee - tip, true, ) @@ -493,8 +492,8 @@ fn payment_from_account_with_only_assets() { let fee_in_native = base_weight + weight + len as u64; let ed = Balances::minimum_balance(); let fee_in_asset = AssetConversion::quote_price_tokens_for_exact_tokens( - NativeOrAssetId::Asset(asset_id), - NativeOrAssetId::Native, + NativeOrWithId::WithId(asset_id), + NativeOrWithId::Native, fee_in_native + ed, true, ) @@ -509,8 +508,8 @@ fn payment_from_account_with_only_assets() { assert_eq!(Assets::balance(asset_id, caller), balance - fee_in_asset); let refund = AssetConversion::quote_price_exact_tokens_for_tokens( - NativeOrAssetId::Native, - NativeOrAssetId::Asset(asset_id), + NativeOrWithId::Native, + NativeOrWithId::WithId(asset_id), ed, true, ) @@ -585,8 +584,8 @@ fn converted_fee_is_never_zero_if_input_fee_is_not() { // validate even a small fee gets converted to asset. let fee_in_native = base_weight + weight + len as u64; let fee_in_asset = AssetConversion::quote_price_tokens_for_exact_tokens( - NativeOrAssetId::Asset(asset_id), - NativeOrAssetId::Native, + NativeOrWithId::WithId(asset_id), + NativeOrWithId::Native, fee_in_native, true, ) From 45d450d97902f7836cbd4b340e56d6388562fadc Mon Sep 17 00:00:00 2001 From: muharem Date: Wed, 25 Oct 2023 16:27:00 +0200 Subject: [PATCH 55/98] substrate test runtime --- substrate/bin/node/runtime/src/lib.rs | 54 ++++++++++++++++----------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index 1d4caed8b494..245dbae0f8c6 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -36,8 +36,13 @@ use frame_support::{ pallet_prelude::Get, parameter_types, traits::{ - fungible::{Balanced, Credit, HoldConsideration, ItemOf}, - tokens::{nonfungibles_v2::Inspect, pay::PayAssetFromAccount, GetSalary, PayFromAccount}, + fungible::{ + Balanced, Credit, HoldConsideration, ItemOf, NativeFromLeft, NativeOrWithId, UnionOf, + }, + tokens::{ + imbalance::ResolveAssetTo, nonfungibles_v2::Inspect, pay::PayAssetFromAccount, + GetSalary, PayFromAccount, + }, AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU16, ConstU32, Contains, Currency, EitherOfDiverse, EqualPrivilegeOnly, Imbalance, InsideBoth, InstanceFilter, KeyOwnerProofSystem, LinearStoragePrice, LockIdentifier, Nothing, OnUnbalanced, @@ -57,7 +62,7 @@ use frame_system::{ }; pub use node_primitives::{AccountId, Signature}; use node_primitives::{AccountIndex, Balance, BlockNumber, Hash, Moment, Nonce}; -use pallet_asset_conversion::{NativeOrAssetId, NativeOrAssetIdConverter}; +use pallet_asset_conversion::{Ascending, Chain, WithFirstAsset}; use pallet_broker::{CoreAssignment, CoreIndex, CoretimeInterface, PartsOf57600}; use pallet_election_provider_multi_phase::{GeometricDepositBase, SolutionAccuracyOf}; use pallet_identity::simple::IdentityInfo; @@ -565,8 +570,11 @@ impl pallet_asset_tx_payment::Config for Runtime { impl pallet_asset_conversion_tx_payment::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Fungibles = Assets; - type OnChargeAssetTransaction = - pallet_asset_conversion_tx_payment::AssetConversionAdapter; + type OnChargeAssetTransaction = pallet_asset_conversion_tx_payment::AssetConversionAdapter< + Balances, + AssetConversion, + Native, + >; } parameter_types! { @@ -1636,32 +1644,34 @@ impl pallet_assets::Config for Runtime { parameter_types! { pub const AssetConversionPalletId: PalletId = PalletId(*b"py/ascon"); - pub AllowMultiAssetPools: bool = true; pub const PoolSetupFee: Balance = 1 * DOLLARS; // should be more or equal to the existential deposit pub const MintMinLiquidity: Balance = 100; // 100 is good enough when the main currency has 10-12 decimals. - pub const LiquidityWithdrawalFee: Permill = Permill::from_percent(0); // should be non-zero if AllowMultiAssetPools is true, otherwise can be zero. + pub const LiquidityWithdrawalFee: Permill = Permill::from_percent(0); + pub const Native: NativeOrWithId = NativeOrWithId::Native; } impl pallet_asset_conversion::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type Currency = Balances; - type HigherPrecisionBalance = sp_core::U256; - type Assets = Assets; type Balance = u128; - type PoolAssets = PoolAssets; - type AssetId = >::AssetId; - type MultiAssetId = NativeOrAssetId; + type HigherPrecisionBalance = sp_core::U256; + type AssetKind = NativeOrWithId; + type Assets = UnionOf, AccountId>; + type PoolId = (Self::AssetKind, Self::AssetKind); + type PoolLocator = Chain< + WithFirstAsset>, + Ascending>, + >; type PoolAssetId = >::AssetId; + type PoolAssets = PoolAssets; + type PoolSetupFee = PoolSetupFee; + type PoolSetupFeeAsset = Native; + type PoolSetupFeeTarget = ResolveAssetTo; type PalletId = AssetConversionPalletId; type LPFee = ConstU32<3>; // means 0.3% - type PoolSetupFee = PoolSetupFee; - type PoolSetupFeeReceiver = AssetConversionOrigin; type LiquidityWithdrawalFee = LiquidityWithdrawalFee; type WeightInfo = pallet_asset_conversion::weights::SubstrateWeight; - type AllowMultiAssetPools = AllowMultiAssetPools; type MaxSwapPathLength = ConstU32<4>; type MintMinLiquidity = MintMinLiquidity; - type MultiAssetIdConverter = NativeOrAssetIdConverter; #[cfg(feature = "runtime-benchmarks")] type BenchmarkHelper = (); } @@ -2562,19 +2572,19 @@ impl_runtime_apis! { impl pallet_asset_conversion::AssetConversionApi< Block, Balance, - NativeOrAssetId + NativeOrWithId > for Runtime { - fn quote_price_exact_tokens_for_tokens(asset1: NativeOrAssetId, asset2: NativeOrAssetId, amount: Balance, include_fee: bool) -> Option { + fn quote_price_exact_tokens_for_tokens(asset1: NativeOrWithId, asset2: NativeOrWithId, amount: Balance, include_fee: bool) -> Option { AssetConversion::quote_price_exact_tokens_for_tokens(asset1, asset2, amount, include_fee) } - fn quote_price_tokens_for_exact_tokens(asset1: NativeOrAssetId, asset2: NativeOrAssetId, amount: Balance, include_fee: bool) -> Option { + fn quote_price_tokens_for_exact_tokens(asset1: NativeOrWithId, asset2: NativeOrWithId, amount: Balance, include_fee: bool) -> Option { AssetConversion::quote_price_tokens_for_exact_tokens(asset1, asset2, amount, include_fee) } - fn get_reserves(asset1: NativeOrAssetId, asset2: NativeOrAssetId) -> Option<(Balance, Balance)> { - AssetConversion::get_reserves(&asset1, &asset2).ok() + fn get_reserves(asset1: NativeOrWithId, asset2: NativeOrWithId) -> Option<(Balance, Balance)> { + AssetConversion::get_reserves(asset1, asset2).ok() } } From baac8e505258728dbbd69bc7b1b443e185a7459b Mon Sep 17 00:00:00 2001 From: muharem Date: Wed, 25 Oct 2023 16:27:33 +0200 Subject: [PATCH 56/98] asset hub common impls --- .../runtimes/assets/common/src/benchmarks.rs | 42 ++ .../runtimes/assets/common/src/lib.rs | 2 + .../common/src/local_and_foreign_assets.rs | 466 ++---------------- 3 files changed, 72 insertions(+), 438 deletions(-) create mode 100644 cumulus/parachains/runtimes/assets/common/src/benchmarks.rs diff --git a/cumulus/parachains/runtimes/assets/common/src/benchmarks.rs b/cumulus/parachains/runtimes/assets/common/src/benchmarks.rs new file mode 100644 index 000000000000..ed1379695317 --- /dev/null +++ b/cumulus/parachains/runtimes/assets/common/src/benchmarks.rs @@ -0,0 +1,42 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use cumulus_primitives_core::ParaId; +use sp_runtime::traits::Get; +use sp_std::marker::PhantomData; +use xcm::latest::prelude::*; + +/// Creates asset pairs for liquidity pools with `Target` always being the first asset. +pub struct AssetPairFactory( + PhantomData<(Target, SelfParaId, PalletId)>, +); +impl, SelfParaId: Get, PalletId: Get> + pallet_asset_conversion::BenchmarkHelper + for AssetPairFactory +{ + fn create_pair(_seed1: u32, seed2: u32) -> (MultiLocation, MultiLocation) { + ( + Target::get(), + MultiLocation::new( + 1, + X3( + Parachain(SelfParaId::get().into()), + PalletInstance(PalletId::get() as u8), + GeneralIndex(seed2.into()), + ), + ), + ) + } +} diff --git a/cumulus/parachains/runtimes/assets/common/src/lib.rs b/cumulus/parachains/runtimes/assets/common/src/lib.rs index f45c3289aab4..15327f51b2a9 100644 --- a/cumulus/parachains/runtimes/assets/common/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/common/src/lib.rs @@ -15,6 +15,8 @@ #![cfg_attr(not(feature = "std"), no_std)] +#[cfg(feature = "runtime-benchmarks")] +pub mod benchmarks; pub mod foreign_creators; pub mod fungible_conversion; pub mod local_and_foreign_assets; diff --git a/cumulus/parachains/runtimes/assets/common/src/local_and_foreign_assets.rs b/cumulus/parachains/runtimes/assets/common/src/local_and_foreign_assets.rs index f9f935dab822..7dd497797eaa 100644 --- a/cumulus/parachains/runtimes/assets/common/src/local_and_foreign_assets.rs +++ b/cumulus/parachains/runtimes/assets/common/src/local_and_foreign_assets.rs @@ -13,48 +13,42 @@ // See the License for the specific language governing permissions and // limitations under the License. -use frame_support::traits::{ - fungibles::{Balanced, Create, HandleImbalanceDrop, Inspect, Mutate, Unbalanced}, - tokens::{ - DepositConsequence, Fortitude, Precision, Preservation, Provenance, WithdrawConsequence, - }, - AccountTouch, Contains, ContainsPair, Get, PalletInfoAccess, +use frame_support::traits::Get; +use sp_runtime::{ + traits::{Convert, MaybeEquivalence}, + Either, + Either::{Left, Right}, }; -use pallet_asset_conversion::{MultiAssetIdConversionResult, MultiAssetIdConverter}; -use parachains_common::AccountId; -use sp_runtime::{traits::MaybeEquivalence, DispatchError, DispatchResult}; use sp_std::marker::PhantomData; use xcm::latest::MultiLocation; -pub struct MultiLocationConverter, MultiLocationMatcher> { - _phantom: PhantomData<(NativeAssetLocation, MultiLocationMatcher)>, +/// Converts a given [`MultiLocation`] to [`Either::Left`] when equal to `Target`, or +/// [`Either::Right`] otherwise. +/// +/// Suitable for use as a `Criterion` with [`frame_support::traits::tokens::fungible::UnionOf`]. +pub struct TargetFromLeft(PhantomData); +impl> Convert> + for TargetFromLeft +{ + fn convert(l: MultiLocation) -> Either<(), MultiLocation> { + Target::get().eq(&l).then(|| Left(())).map_or(Right(l), |n| n) + } } -impl MultiAssetIdConverter - for MultiLocationConverter +/// Converts a given [`MultiLocation`] to [`Either::Left`] based on the `Equivalence` criteria. +/// Returns [`Either::Right`] if not equivalent. +/// +/// Suitable for use as a `Criterion` with [`frame_support::traits::tokens::fungibles::UnionOf`]. +pub struct LocalFromLeft(PhantomData<(Equivalence, AssetId)>); +impl Convert> + for LocalFromLeft where - NativeAssetLocation: Get, - MultiLocationMatcher: Contains, + Equivalence: MaybeEquivalence, { - fn get_native() -> MultiLocation { - NativeAssetLocation::get() - } - - fn is_native(asset_id: &MultiLocation) -> bool { - *asset_id == Self::get_native() - } - - fn try_convert( - asset_id: &MultiLocation, - ) -> MultiAssetIdConversionResult { - if Self::is_native(asset_id) { - return MultiAssetIdConversionResult::Native - } - - if MultiLocationMatcher::contains(asset_id) { - MultiAssetIdConversionResult::Converted(*asset_id) - } else { - MultiAssetIdConversionResult::Unsupported(*asset_id) + fn convert(l: MultiLocation) -> Either { + match Equivalence::convert(&l) { + Some(id) => Left(id), + None => Right(l), } } } @@ -63,407 +57,3 @@ pub trait MatchesLocalAndForeignAssetsMultiLocation { fn is_local(location: &MultiLocation) -> bool; fn is_foreign(location: &MultiLocation) -> bool; } - -pub struct LocalAndForeignAssets { - _phantom: PhantomData<(Assets, LocalAssetIdConverter, ForeignAssets)>, -} - -impl Unbalanced - for LocalAndForeignAssets -where - Assets: Inspect - + Unbalanced - + Balanced - + PalletInfoAccess, - LocalAssetIdConverter: MaybeEquivalence, - ForeignAssets: Inspect - + Unbalanced - + Balanced, -{ - fn handle_dust(dust: frame_support::traits::fungibles::Dust) { - let credit = dust.into_credit(); - - if let Some(asset) = LocalAssetIdConverter::convert(&credit.asset()) { - Assets::handle_raw_dust(asset, credit.peek()); - } else { - ForeignAssets::handle_raw_dust(credit.asset(), credit.peek()); - } - - // As we have already handled the dust, we must stop credit's drop from happening: - sp_std::mem::forget(credit); - } - - fn write_balance( - asset: >::AssetId, - who: &AccountId, - amount: >::Balance, - ) -> Result>::Balance>, DispatchError> { - if let Some(asset) = LocalAssetIdConverter::convert(&asset) { - Assets::write_balance(asset, who, amount) - } else { - ForeignAssets::write_balance(asset, who, amount) - } - } - - /// Set the total issuance of `asset` to `amount`. - fn set_total_issuance(asset: Self::AssetId, amount: Self::Balance) { - if let Some(asset) = LocalAssetIdConverter::convert(&asset) { - Assets::set_total_issuance(asset, amount) - } else { - ForeignAssets::set_total_issuance(asset, amount) - } - } - - fn decrease_balance( - asset: Self::AssetId, - who: &AccountId, - amount: Self::Balance, - precision: Precision, - preservation: Preservation, - force: Fortitude, - ) -> Result { - if let Some(asset) = LocalAssetIdConverter::convert(&asset) { - Assets::decrease_balance(asset, who, amount, precision, preservation, force) - } else { - ForeignAssets::decrease_balance(asset, who, amount, precision, preservation, force) - } - } - - fn increase_balance( - asset: Self::AssetId, - who: &AccountId, - amount: Self::Balance, - precision: Precision, - ) -> Result { - if let Some(asset) = LocalAssetIdConverter::convert(&asset) { - Assets::increase_balance(asset, who, amount, precision) - } else { - ForeignAssets::increase_balance(asset, who, amount, precision) - } - } -} - -impl Inspect - for LocalAndForeignAssets -where - Assets: Inspect, - LocalAssetIdConverter: MaybeEquivalence, - ForeignAssets: Inspect, -{ - type AssetId = MultiLocation; - type Balance = u128; - - /// The total amount of issuance in the system. - fn total_issuance(asset: Self::AssetId) -> Self::Balance { - if let Some(asset) = LocalAssetIdConverter::convert(&asset) { - Assets::total_issuance(asset) - } else { - ForeignAssets::total_issuance(asset) - } - } - - /// The minimum balance any single account may have. - fn minimum_balance(asset: Self::AssetId) -> Self::Balance { - if let Some(asset) = LocalAssetIdConverter::convert(&asset) { - Assets::minimum_balance(asset) - } else { - ForeignAssets::minimum_balance(asset) - } - } - - fn total_balance( - asset: >::AssetId, - account: &AccountId, - ) -> >::Balance { - if let Some(asset) = LocalAssetIdConverter::convert(&asset) { - Assets::total_balance(asset, account) - } else { - ForeignAssets::total_balance(asset, account) - } - } - - /// Get the `asset` balance of `who`. - fn balance(asset: Self::AssetId, who: &AccountId) -> Self::Balance { - if let Some(asset) = LocalAssetIdConverter::convert(&asset) { - Assets::balance(asset, who) - } else { - ForeignAssets::balance(asset, who) - } - } - - /// Get the maximum amount of `asset` that `who` can withdraw/transfer successfully. - fn reducible_balance( - asset: Self::AssetId, - who: &AccountId, - presevation: Preservation, - fortitude: Fortitude, - ) -> Self::Balance { - if let Some(asset) = LocalAssetIdConverter::convert(&asset) { - Assets::reducible_balance(asset, who, presevation, fortitude) - } else { - ForeignAssets::reducible_balance(asset, who, presevation, fortitude) - } - } - - /// Returns `true` if the `asset` balance of `who` may be increased by `amount`. - /// - /// - `asset`: The asset that should be deposited. - /// - `who`: The account of which the balance should be increased by `amount`. - /// - `amount`: How much should the balance be increased? - /// - `mint`: Will `amount` be minted to deposit it into `account`? - fn can_deposit( - asset: Self::AssetId, - who: &AccountId, - amount: Self::Balance, - mint: Provenance, - ) -> DepositConsequence { - if let Some(asset) = LocalAssetIdConverter::convert(&asset) { - Assets::can_deposit(asset, who, amount, mint) - } else { - ForeignAssets::can_deposit(asset, who, amount, mint) - } - } - - /// Returns `Failed` if the `asset` balance of `who` may not be decreased by `amount`, otherwise - /// the consequence. - fn can_withdraw( - asset: Self::AssetId, - who: &AccountId, - amount: Self::Balance, - ) -> WithdrawConsequence { - if let Some(asset) = LocalAssetIdConverter::convert(&asset) { - Assets::can_withdraw(asset, who, amount) - } else { - ForeignAssets::can_withdraw(asset, who, amount) - } - } - - /// Returns `true` if an `asset` exists. - fn asset_exists(asset: Self::AssetId) -> bool { - if let Some(asset) = LocalAssetIdConverter::convert(&asset) { - Assets::asset_exists(asset) - } else { - ForeignAssets::asset_exists(asset) - } - } -} - -impl Mutate - for LocalAndForeignAssets -where - Assets: Mutate - + Inspect - + Balanced - + PalletInfoAccess, - LocalAssetIdConverter: MaybeEquivalence, - ForeignAssets: Mutate - + Inspect - + Balanced, -{ - /// Transfer funds from one account into another. - fn transfer( - asset: MultiLocation, - source: &AccountId, - dest: &AccountId, - amount: Self::Balance, - keep_alive: Preservation, - ) -> Result { - if let Some(asset_id) = LocalAssetIdConverter::convert(&asset) { - Assets::transfer(asset_id, source, dest, amount, keep_alive) - } else { - ForeignAssets::transfer(asset, source, dest, amount, keep_alive) - } - } -} - -impl Create - for LocalAndForeignAssets -where - Assets: Create + Inspect, - LocalAssetIdConverter: MaybeEquivalence, - ForeignAssets: Create + Inspect, -{ - /// Create a new fungible asset. - fn create( - asset_id: Self::AssetId, - admin: AccountId, - is_sufficient: bool, - min_balance: Self::Balance, - ) -> DispatchResult { - if let Some(asset_id) = LocalAssetIdConverter::convert(&asset_id) { - Assets::create(asset_id, admin, is_sufficient, min_balance) - } else { - ForeignAssets::create(asset_id, admin, is_sufficient, min_balance) - } - } -} - -impl AccountTouch - for LocalAndForeignAssets -where - Assets: AccountTouch, - LocalAssetIdConverter: MaybeEquivalence, - ForeignAssets: AccountTouch, -{ - type Balance = u128; - - fn deposit_required( - asset_id: MultiLocation, - ) -> >::Balance { - if let Some(asset_id) = LocalAssetIdConverter::convert(&asset_id) { - Assets::deposit_required(asset_id) - } else { - ForeignAssets::deposit_required(asset_id) - } - } - - fn touch( - asset_id: MultiLocation, - who: AccountId, - depositor: AccountId, - ) -> Result<(), DispatchError> { - if let Some(asset_id) = LocalAssetIdConverter::convert(&asset_id) { - Assets::touch(asset_id, who, depositor) - } else { - ForeignAssets::touch(asset_id, who, depositor) - } - } -} - -/// Implements [`ContainsPair`] trait for a pair of asset and account IDs. -impl ContainsPair - for LocalAndForeignAssets -where - Assets: PalletInfoAccess + ContainsPair, - LocalAssetIdConverter: MaybeEquivalence, - ForeignAssets: ContainsPair, -{ - /// Check if an account with the given asset ID and account address exists. - fn contains(asset_id: &MultiLocation, who: &AccountId) -> bool { - if let Some(asset_id) = LocalAssetIdConverter::convert(asset_id) { - Assets::contains(&asset_id, &who) - } else { - ForeignAssets::contains(&asset_id, &who) - } - } -} - -impl Balanced - for LocalAndForeignAssets -where - Assets: - Balanced + Inspect + PalletInfoAccess, - LocalAssetIdConverter: MaybeEquivalence, - ForeignAssets: - Balanced + Inspect, -{ - type OnDropDebt = DebtDropIndirection; - type OnDropCredit = CreditDropIndirection; -} - -pub struct DebtDropIndirection { - _phantom: PhantomData>, -} - -impl HandleImbalanceDrop - for DebtDropIndirection -where - Assets: Balanced + Inspect, - LocalAssetIdConverter: MaybeEquivalence, - ForeignAssets: - Balanced + Inspect, -{ - fn handle(asset: MultiLocation, amount: u128) { - if let Some(asset_id) = LocalAssetIdConverter::convert(&asset) { - Assets::OnDropDebt::handle(asset_id, amount); - } else { - ForeignAssets::OnDropDebt::handle(asset, amount); - } - } -} - -pub struct CreditDropIndirection { - _phantom: PhantomData>, -} - -impl HandleImbalanceDrop - for CreditDropIndirection -where - Assets: Balanced + Inspect, - LocalAssetIdConverter: MaybeEquivalence, - ForeignAssets: - Balanced + Inspect, -{ - fn handle(asset: MultiLocation, amount: u128) { - if let Some(asset_id) = LocalAssetIdConverter::convert(&asset) { - Assets::OnDropCredit::handle(asset_id, amount); - } else { - ForeignAssets::OnDropCredit::handle(asset, amount); - } - } -} - -#[cfg(test)] -mod tests { - use crate::{ - local_and_foreign_assets::MultiLocationConverter, AssetIdForPoolAssetsConvert, - AssetIdForTrustBackedAssetsConvert, - }; - use frame_support::traits::EverythingBut; - use pallet_asset_conversion::{MultiAssetIdConversionResult, MultiAssetIdConverter}; - use sp_runtime::traits::MaybeEquivalence; - use xcm::latest::prelude::*; - use xcm_builder::StartsWith; - - #[test] - fn test_multi_location_converter_works() { - frame_support::parameter_types! { - pub const WestendLocation: MultiLocation = MultiLocation::parent(); - pub TrustBackedAssetsPalletLocation: MultiLocation = PalletInstance(50_u8).into(); - pub PoolAssetsPalletLocation: MultiLocation = PalletInstance(55_u8).into(); - } - - type C = MultiLocationConverter< - WestendLocation, - EverythingBut>, - >; - - let native_asset = WestendLocation::get(); - let local_asset = - AssetIdForTrustBackedAssetsConvert::::convert_back( - &123, - ) - .unwrap(); - let pool_asset = - AssetIdForPoolAssetsConvert::::convert_back(&456).unwrap(); - let foreign_asset1 = MultiLocation { parents: 1, interior: X1(Parachain(2222)) }; - let foreign_asset2 = MultiLocation { - parents: 2, - interior: X2(GlobalConsensus(ByGenesis([1; 32])), Parachain(2222)), - }; - - assert!(C::is_native(&native_asset)); - assert!(!C::is_native(&local_asset)); - assert!(!C::is_native(&pool_asset)); - assert!(!C::is_native(&foreign_asset1)); - assert!(!C::is_native(&foreign_asset2)); - - assert_eq!(C::try_convert(&native_asset), MultiAssetIdConversionResult::Native); - assert_eq!( - C::try_convert(&local_asset), - MultiAssetIdConversionResult::Converted(local_asset) - ); - assert_eq!( - C::try_convert(&pool_asset), - MultiAssetIdConversionResult::Unsupported(pool_asset) - ); - assert_eq!( - C::try_convert(&foreign_asset1), - MultiAssetIdConversionResult::Converted(foreign_asset1) - ); - assert_eq!( - C::try_convert(&foreign_asset2), - MultiAssetIdConversionResult::Converted(foreign_asset2) - ); - } -} From c4eb5148dc89dcb505c0a59ba8d8b065c2186c56 Mon Sep 17 00:00:00 2001 From: muharem Date: Wed, 25 Oct 2023 16:34:26 +0200 Subject: [PATCH 57/98] asset hub runtimes --- .../assets/asset-hub-kusama/src/lib.rs | 68 ++++--- .../assets/asset-hub-kusama/src/xcm_config.rs | 33 +-- .../assets/asset-hub-rococo/src/lib.rs | 73 ++++--- .../assets/asset-hub-rococo/src/xcm_config.rs | 33 +-- .../assets/asset-hub-westend/src/lib.rs | 189 ++++-------------- .../asset-hub-westend/src/xcm_config.rs | 34 +--- .../assets/asset-hub-westend/tests/tests.rs | 5 +- 7 files changed, 126 insertions(+), 309 deletions(-) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/lib.rs index a1ee0e4df71c..583fdd359a0a 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/lib.rs @@ -29,7 +29,7 @@ pub mod xcm_config; use assets_common::{ foreign_creators::ForeignCreators, - local_and_foreign_assets::{LocalAndForeignAssets, MultiLocationConverter}, + local_and_foreign_assets::{LocalFromLeft, TargetFromLeft}, matching::FromSiblingParachain, AssetIdForTrustBackedAssetsConvert, MultiLocationForAssetId, }; @@ -57,8 +57,8 @@ use frame_support::{ genesis_builder_helper::{build_config, create_default_config}, ord_parameter_types, parameter_types, traits::{ - AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU32, ConstU64, ConstU8, EitherOfDiverse, - InstanceFilter, + fungible, fungibles, tokens::imbalance::ResolveAssetTo, AsEnsureOriginWithArg, ConstBool, + ConstU128, ConstU32, ConstU64, ConstU8, EitherOfDiverse, InstanceFilter, }, weights::{ConstantMultiplier, Weight}, BoundedVec, PalletId, @@ -93,10 +93,7 @@ use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; use xcm::latest::prelude::*; use xcm_executor::XcmExecutor; -use crate::xcm_config::{ - ForeignCreatorsSovereignAccountOf, LocalAndForeignAssetsMultiLocationMatcher, - TrustBackedAssetsPalletLocation, -}; +use crate::xcm_config::{ForeignCreatorsSovereignAccountOf, TrustBackedAssetsPalletLocation}; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; impl_opaque_keys! { @@ -292,8 +289,6 @@ impl pallet_assets::Config for Runtime { parameter_types! { pub const AssetConversionPalletId: PalletId = PalletId(*b"py/ascon"); - pub const AllowMultiAssetPools: bool = false; - // should be non-zero if AllowMultiAssetPools is true, otherwise can be zero pub const LiquidityWithdrawalFee: Permill = Permill::from_percent(0); } @@ -328,35 +323,50 @@ impl pallet_assets::Config for Runtime { type BenchmarkHelper = (); } +/// Union fungibles implementation for `Assets`` and `ForeignAssets`. +pub type LocalAndForeignAssets = fungibles::UnionOf< + Assets, + ForeignAssets, + LocalFromLeft< + AssetIdForTrustBackedAssetsConvert, + AssetIdForTrustBackedAssets, + >, + MultiLocation, + AccountId, +>; + impl pallet_asset_conversion::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Balance = Balance; type HigherPrecisionBalance = sp_core::U256; - type Currency = Balances; - type AssetId = MultiLocation; - type Assets = LocalAndForeignAssets< - Assets, - AssetIdForTrustBackedAssetsConvert, - ForeignAssets, + type AssetKind = MultiLocation; + type Assets = fungible::UnionOf< + Balances, + LocalAndForeignAssets, + TargetFromLeft, + Self::AssetKind, + Self::AccountId, >; - type PoolAssets = PoolAssets; + type PoolId = (Self::AssetKind, Self::AssetKind); + type PoolLocator = + pallet_asset_conversion::WithFirstAsset; type PoolAssetId = u32; + type PoolAssets = PoolAssets; type PoolSetupFee = ConstU128<0>; // Asset class deposit fees are sufficient to prevent spam - type PoolSetupFeeReceiver = AssetConversionOrigin; - // should be non-zero if `AllowMultiAssetPools` is true, otherwise can be zero. + type PoolSetupFeeAsset = KsmLocation; + type PoolSetupFeeTarget = ResolveAssetTo; type LiquidityWithdrawalFee = LiquidityWithdrawalFee; type LPFee = ConstU32<3>; type PalletId = AssetConversionPalletId; - type AllowMultiAssetPools = AllowMultiAssetPools; type MaxSwapPathLength = ConstU32<4>; - type MultiAssetId = MultiLocation; - type MultiAssetIdConverter = - MultiLocationConverter; type MintMinLiquidity = ConstU128<100>; type WeightInfo = weights::pallet_asset_conversion::WeightInfo; #[cfg(feature = "runtime-benchmarks")] - type BenchmarkHelper = - crate::xcm_config::BenchmarkMultiLocationConverter>; + type BenchmarkHelper = assets_common::benchmarks::AssetPairFactory< + KsmLocation, + parachain_info::Pallet, + xcm_config::AssetsPalletIndex, + >; } parameter_types! { @@ -715,12 +725,8 @@ impl pallet_collator_selection::Config for Runtime { impl pallet_asset_conversion_tx_payment::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type Fungibles = LocalAndForeignAssets< - Assets, - AssetIdForTrustBackedAssetsConvert, - ForeignAssets, - >; - type OnChargeAssetTransaction = AssetConversionAdapter; + type Fungibles = LocalAndForeignAssets; + type OnChargeAssetTransaction = AssetConversionAdapter; } parameter_types! { @@ -1042,7 +1048,7 @@ impl_runtime_apis! { AssetConversion::quote_price_tokens_for_exact_tokens(asset1, asset2, amount, include_fee) } fn get_reserves(asset1: MultiLocation, asset2: MultiLocation) -> Option<(Balance, Balance)> { - AssetConversion::get_reserves(&asset1, &asset2).ok() + AssetConversion::get_reserves(asset1, asset2).ok() } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/xcm_config.rs index 13da81fe5912..5047fbe1e170 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/xcm_config.rs @@ -49,9 +49,6 @@ use xcm_builder::{ }; use xcm_executor::{traits::WithOriginFilter, XcmExecutor}; -#[cfg(feature = "runtime-benchmarks")] -use {cumulus_primitives_core::ParaId, sp_core::Get}; - parameter_types! { pub const KsmLocation: MultiLocation = MultiLocation::parent(); pub const RelayNetwork: Option = Some(NetworkId::Kusama); @@ -59,8 +56,8 @@ parameter_types! { pub UniversalLocation: InteriorMultiLocation = X2(GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(ParachainInfo::parachain_id().into())); pub UniversalLocationNetworkId: NetworkId = UniversalLocation::get().global_consensus().unwrap(); - pub TrustBackedAssetsPalletLocation: MultiLocation = - PalletInstance(::index() as u8).into(); + pub AssetsPalletIndex: u32 = ::index() as u32; + pub TrustBackedAssetsPalletLocation: MultiLocation = PalletInstance(AssetsPalletIndex::get() as u8).into(); pub ForeignAssetsPalletLocation: MultiLocation = PalletInstance(::index() as u8).into(); pub PoolAssetsPalletLocation: MultiLocation = @@ -612,29 +609,3 @@ impl pallet_assets::BenchmarkHelper for XcmBenchmarkHelper { MultiLocation { parents: 1, interior: X1(Parachain(id)) } } } - -#[cfg(feature = "runtime-benchmarks")] -pub struct BenchmarkMultiLocationConverter { - _phantom: sp_std::marker::PhantomData, -} - -#[cfg(feature = "runtime-benchmarks")] -impl pallet_asset_conversion::BenchmarkHelper - for BenchmarkMultiLocationConverter -where - SelfParaId: Get, -{ - fn asset_id(asset_id: u32) -> MultiLocation { - MultiLocation { - parents: 1, - interior: X3( - Parachain(SelfParaId::get().into()), - PalletInstance(::index() as u8), - GeneralIndex(asset_id.into()), - ), - } - } - fn multiasset_id(asset_id: u32) -> MultiLocation { - Self::asset_id(asset_id) - } -} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs index 1d18296cb6aa..dcfc41c79620 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -35,7 +35,7 @@ pub mod xcm_config; use assets_common::{ foreign_creators::ForeignCreators, - local_and_foreign_assets::{LocalAndForeignAssets, MultiLocationConverter}, + local_and_foreign_assets::{LocalFromLeft, TargetFromLeft}, matching::FromSiblingParachain, AssetIdForTrustBackedAssetsConvert, MultiLocationForAssetId, }; @@ -60,8 +60,8 @@ use frame_support::{ dispatch::DispatchClass, ord_parameter_types, parameter_types, traits::{ - AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU32, ConstU64, ConstU8, EitherOfDiverse, - Equals, InstanceFilter, + fungible, fungibles, tokens::imbalance::ResolveAssetTo, AsEnsureOriginWithArg, ConstBool, + ConstU128, ConstU32, ConstU64, ConstU8, EitherOfDiverse, Equals, InstanceFilter, }, weights::{ConstantMultiplier, Weight}, BoundedVec, PalletId, @@ -83,8 +83,9 @@ use parachains_common::{ use sp_runtime::RuntimeDebug; use xcm::opaque::v3::MultiLocation; use xcm_config::{ - ForeignAssetsConvertedConcreteId, GovernanceLocation, PoolAssetsConvertedConcreteId, - TokenLocation, TrustBackedAssetsConvertedConcreteId, XcmConfig, + ForeignAssetsConvertedConcreteId, ForeignCreatorsSovereignAccountOf, GovernanceLocation, + PoolAssetsConvertedConcreteId, TokenLocation, TrustBackedAssetsConvertedConcreteId, + TrustBackedAssetsPalletLocation, XcmConfig, }; #[cfg(any(feature = "std", test))] @@ -96,10 +97,6 @@ use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; use xcm::latest::prelude::*; use xcm_executor::XcmExecutor; -use crate::xcm_config::{ - ForeignCreatorsSovereignAccountOf, LocalAndForeignAssetsMultiLocationMatcher, - TrustBackedAssetsPalletLocation, -}; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; /// Enum for handling differences in the runtime configuration for `AssetHubRococo` vs. @@ -298,8 +295,6 @@ impl pallet_assets::Config for Runtime { parameter_types! { pub const AssetConversionPalletId: PalletId = PalletId(*b"py/ascon"); - pub const AllowMultiAssetPools: bool = false; - // should be non-zero if AllowMultiAssetPools is true, otherwise can be zero pub const LiquidityWithdrawalFee: Permill = Permill::from_percent(0); } @@ -334,35 +329,50 @@ impl pallet_assets::Config for Runtime { type BenchmarkHelper = (); } +/// Union fungibles implementation for `Assets`` and `ForeignAssets`. +pub type LocalAndForeignAssets = fungibles::UnionOf< + Assets, + ForeignAssets, + LocalFromLeft< + AssetIdForTrustBackedAssetsConvert, + AssetIdForTrustBackedAssets, + >, + MultiLocation, + AccountId, +>; + impl pallet_asset_conversion::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Balance = Balance; type HigherPrecisionBalance = sp_core::U256; - type Currency = Balances; - type AssetId = MultiLocation; - type Assets = LocalAndForeignAssets< - Assets, - AssetIdForTrustBackedAssetsConvert, - ForeignAssets, + type AssetKind = MultiLocation; + type Assets = fungible::UnionOf< + Balances, + LocalAndForeignAssets, + TargetFromLeft, + Self::AssetKind, + Self::AccountId, >; - type PoolAssets = PoolAssets; + type PoolId = (Self::AssetKind, Self::AssetKind); + type PoolLocator = + pallet_asset_conversion::WithFirstAsset; type PoolAssetId = u32; + type PoolAssets = PoolAssets; type PoolSetupFee = ConstU128<0>; // Asset class deposit fees are sufficient to prevent spam - type PoolSetupFeeReceiver = AssetConversionOrigin; - // should be non-zero if `AllowMultiAssetPools` is true, otherwise can be zero. + type PoolSetupFeeAsset = TokenLocation; + type PoolSetupFeeTarget = ResolveAssetTo; type LiquidityWithdrawalFee = LiquidityWithdrawalFee; type LPFee = ConstU32<3>; type PalletId = AssetConversionPalletId; - type AllowMultiAssetPools = AllowMultiAssetPools; type MaxSwapPathLength = ConstU32<4>; - type MultiAssetId = MultiLocation; - type MultiAssetIdConverter = - MultiLocationConverter; type MintMinLiquidity = ConstU128<100>; type WeightInfo = weights::pallet_asset_conversion::WeightInfo; #[cfg(feature = "runtime-benchmarks")] - type BenchmarkHelper = - crate::xcm_config::BenchmarkMultiLocationConverter>; + type BenchmarkHelper = assets_common::benchmarks::AssetPairFactory< + TokenLocation, + parachain_info::Pallet, + xcm_config::AssetsPalletIndex, + >; } parameter_types! { @@ -727,12 +737,9 @@ impl pallet_collator_selection::Config for Runtime { impl pallet_asset_conversion_tx_payment::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type Fungibles = LocalAndForeignAssets< - Assets, - AssetIdForTrustBackedAssetsConvert, - ForeignAssets, - >; - type OnChargeAssetTransaction = AssetConversionAdapter; + type Fungibles = LocalAndForeignAssets; + type OnChargeAssetTransaction = + AssetConversionAdapter; } parameter_types! { @@ -1123,7 +1130,7 @@ impl_runtime_apis! { } fn get_reserves(asset1: MultiLocation, asset2: MultiLocation) -> Option<(Balance, Balance)> { - AssetConversion::get_reserves(&asset1, &asset2).ok() + AssetConversion::get_reserves(asset1, asset2).ok() } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs index b88de9efb09c..19319c89c875 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs @@ -57,9 +57,6 @@ use xcm_builder::{ }; use xcm_executor::{traits::WithOriginFilter, XcmExecutor}; -#[cfg(feature = "runtime-benchmarks")] -use cumulus_primitives_core::ParaId; - parameter_types! { pub storage Flavor: RuntimeFlavor = RuntimeFlavor::default(); pub const TokenLocation: MultiLocation = MultiLocation::parent(); @@ -67,8 +64,8 @@ parameter_types! { pub UniversalLocation: InteriorMultiLocation = X2(GlobalConsensus(RelayNetwork::get()), Parachain(ParachainInfo::parachain_id().into())); pub UniversalLocationNetworkId: NetworkId = UniversalLocation::get().global_consensus().unwrap(); - pub TrustBackedAssetsPalletLocation: MultiLocation = - PalletInstance(::index() as u8).into(); + pub AssetsPalletIndex: u32 = ::index() as u32; + pub TrustBackedAssetsPalletLocation: MultiLocation = PalletInstance(AssetsPalletIndex::get() as u8).into(); pub ForeignAssetsPalletLocation: MultiLocation = PalletInstance(::index() as u8).into(); pub PoolAssetsPalletLocation: MultiLocation = @@ -718,32 +715,6 @@ impl pallet_assets::BenchmarkHelper for XcmBenchmarkHelper { } } -#[cfg(feature = "runtime-benchmarks")] -pub struct BenchmarkMultiLocationConverter { - _phantom: sp_std::marker::PhantomData, -} - -#[cfg(feature = "runtime-benchmarks")] -impl pallet_asset_conversion::BenchmarkHelper - for BenchmarkMultiLocationConverter -where - SelfParaId: Get, -{ - fn asset_id(asset_id: u32) -> MultiLocation { - MultiLocation { - parents: 1, - interior: X3( - Parachain(SelfParaId::get().into()), - PalletInstance(::index() as u8), - GeneralIndex(asset_id.into()), - ), - } - } - fn multiasset_id(asset_id: u32) -> MultiLocation { - Self::asset_id(asset_id) - } -} - /// All configuration related to bridging pub mod bridging { use super::*; diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index 01d327c7c593..8f7518ee6bf3 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -27,11 +27,8 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); mod weights; pub mod xcm_config; -use crate::xcm_config::{ - LocalAndForeignAssetsMultiLocationMatcher, TrustBackedAssetsPalletLocation, -}; use assets_common::{ - local_and_foreign_assets::{LocalAndForeignAssets, MultiLocationConverter}, + local_and_foreign_assets::{LocalFromLeft, TargetFromLeft}, AssetIdForTrustBackedAssetsConvert, }; use codec::{Decode, Encode, MaxEncodedLen}; @@ -42,8 +39,9 @@ use frame_support::{ genesis_builder_helper::{build_config, create_default_config}, ord_parameter_types, parameter_types, traits::{ - tokens::nonfungibles_v2::Inspect, AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU32, - ConstU64, ConstU8, InstanceFilter, + fungible, fungibles, + tokens::{imbalance::ResolveAssetTo, nonfungibles_v2::Inspect}, + AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU32, ConstU64, ConstU8, InstanceFilter, }, weights::{ConstantMultiplier, Weight}, BoundedVec, PalletId, @@ -77,8 +75,8 @@ use sp_version::RuntimeVersion; use xcm::opaque::v3::MultiLocation; use xcm_config::{ ForeignAssetsConvertedConcreteId, PoolAssetsConvertedConcreteId, - TrustBackedAssetsConvertedConcreteId, WestendLocation, XcmConfig, - XcmOriginToTransactDispatchOrigin, + TrustBackedAssetsConvertedConcreteId, TrustBackedAssetsPalletLocation, WestendLocation, + XcmConfig, XcmOriginToTransactDispatchOrigin, }; #[cfg(any(feature = "std", test))] @@ -269,8 +267,6 @@ impl pallet_assets::Config for Runtime { parameter_types! { pub const AssetConversionPalletId: PalletId = PalletId(*b"py/ascon"); - pub const AllowMultiAssetPools: bool = false; - // should be non-zero if AllowMultiAssetPools is true, otherwise can be zero pub const LiquidityWithdrawalFee: Permill = Permill::from_percent(0); } @@ -304,34 +300,50 @@ impl pallet_assets::Config for Runtime { type BenchmarkHelper = (); } +/// Union fungibles implementation for `Assets`` and `ForeignAssets`. +pub type LocalAndForeignAssets = fungibles::UnionOf< + Assets, + ForeignAssets, + LocalFromLeft< + AssetIdForTrustBackedAssetsConvert, + AssetIdForTrustBackedAssets, + >, + MultiLocation, + AccountId, +>; + impl pallet_asset_conversion::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Balance = Balance; type HigherPrecisionBalance = sp_core::U256; - type Currency = Balances; - type AssetId = MultiLocation; - type Assets = LocalAndForeignAssets< - Assets, - AssetIdForTrustBackedAssetsConvert, - ForeignAssets, + type AssetKind = MultiLocation; + type Assets = fungible::UnionOf< + Balances, + LocalAndForeignAssets, + TargetFromLeft, + Self::AssetKind, + Self::AccountId, >; - type PoolAssets = PoolAssets; + type PoolId = (Self::AssetKind, Self::AssetKind); + type PoolLocator = + pallet_asset_conversion::WithFirstAsset; type PoolAssetId = u32; + type PoolAssets = PoolAssets; type PoolSetupFee = ConstU128<0>; // Asset class deposit fees are sufficient to prevent spam - type PoolSetupFeeReceiver = AssetConversionOrigin; - type LiquidityWithdrawalFee = LiquidityWithdrawalFee; // should be non-zero if AllowMultiAssetPools is true, otherwise can be zero. + type PoolSetupFeeAsset = WestendLocation; + type PoolSetupFeeTarget = ResolveAssetTo; + type LiquidityWithdrawalFee = LiquidityWithdrawalFee; type LPFee = ConstU32<3>; type PalletId = AssetConversionPalletId; - type AllowMultiAssetPools = AllowMultiAssetPools; type MaxSwapPathLength = ConstU32<4>; - type MultiAssetId = MultiLocation; - type MultiAssetIdConverter = - MultiLocationConverter; type MintMinLiquidity = ConstU128<100>; type WeightInfo = weights::pallet_asset_conversion::WeightInfo; #[cfg(feature = "runtime-benchmarks")] - type BenchmarkHelper = - crate::xcm_config::BenchmarkMultiLocationConverter>; + type BenchmarkHelper = assets_common::benchmarks::AssetPairFactory< + WestendLocation, + parachain_info::Pallet, + xcm_config::AssetsPalletIndex, + >; } parameter_types! { @@ -690,12 +702,9 @@ impl pallet_collator_selection::Config for Runtime { impl pallet_asset_conversion_tx_payment::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type Fungibles = LocalAndForeignAssets< - Assets, - AssetIdForTrustBackedAssetsConvert, - ForeignAssets, - >; - type OnChargeAssetTransaction = AssetConversionAdapter; + type Fungibles = LocalAndForeignAssets; + type OnChargeAssetTransaction = + AssetConversionAdapter; } parameter_types! { @@ -871,8 +880,6 @@ pub type Migrations = ( // unreleased pallet_collator_selection::migration::v1::MigrateToV1, // unreleased - migrations::NativeAssetParents0ToParents1Migration, - // unreleased pallet_multisig::migrations::v1::MigrateToV1, // unreleased InitStorageVersions, @@ -1106,7 +1113,7 @@ impl_runtime_apis! { } fn get_reserves(asset1: MultiLocation, asset2: MultiLocation) -> Option<(Balance, Balance)> { - AssetConversion::get_reserves(&asset1, &asset2).ok() + AssetConversion::get_reserves(asset1, asset2).ok() } } @@ -1435,117 +1442,3 @@ cumulus_pallet_parachain_system::register_validate_block! { Runtime = Runtime, BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::, } - -pub mod migrations { - use super::*; - use frame_support::{ - pallet_prelude::Get, - traits::{ - fungibles::{Inspect, Mutate}, - tokens::Preservation, - OnRuntimeUpgrade, OriginTrait, - }, - }; - use parachains_common::impls::AccountIdOf; - use sp_runtime::{traits::StaticLookup, Saturating}; - - /// Temporary migration because of bug with native asset, it can be removed once applied on - /// `AssetHubWestend`. Migrates pools with `MultiLocation { parents: 0, interior: Here }` to - /// `MultiLocation { parents: 1, interior: Here }` - pub struct NativeAssetParents0ToParents1Migration(sp_std::marker::PhantomData); - impl< - T: pallet_asset_conversion::Config, - > OnRuntimeUpgrade for NativeAssetParents0ToParents1Migration - where - ::PoolAssetId: Into, - AccountIdOf: Into<[u8; 32]>, - ::AccountId: - Into<<::RuntimeOrigin as OriginTrait>::AccountId>, - <::Lookup as StaticLookup>::Source: - From<::AccountId>, - sp_runtime::AccountId32: From<::AccountId>, - { - fn on_runtime_upgrade() -> Weight { - let invalid_native_asset = MultiLocation { parents: 0, interior: Here }; - let valid_native_asset = WestendLocation::get(); - - let mut reads: u64 = 1; - let mut writes: u64 = 0; - - // migrate pools with invalid native asset - let pools = pallet_asset_conversion::Pools::::iter().collect::>(); - reads.saturating_accrue(1); - for (old_pool_id, pool_info) in pools { - let old_pool_account = - pallet_asset_conversion::Pallet::::get_pool_account(&old_pool_id); - reads.saturating_accrue(1); - let pool_asset_id = pool_info.lp_token.clone(); - if old_pool_id.0 != invalid_native_asset { - // skip, if ok - continue - } - - // fix new account - let new_pool_id = pallet_asset_conversion::Pallet::::get_pool_id( - valid_native_asset, - old_pool_id.1, - ); - let new_pool_account = - pallet_asset_conversion::Pallet::::get_pool_account(&new_pool_id); - frame_system::Pallet::::inc_providers(&new_pool_account); - reads.saturating_accrue(2); - writes.saturating_accrue(1); - - // move currency - let _ = Balances::transfer_all( - RuntimeOrigin::signed(sp_runtime::AccountId32::from(old_pool_account.clone())), - sp_runtime::AccountId32::from(new_pool_account.clone()).into(), - false, - ); - reads.saturating_accrue(2); - writes.saturating_accrue(2); - - // move LP token - let _ = T::PoolAssets::transfer( - pool_asset_id.clone(), - &old_pool_account, - &new_pool_account, - T::PoolAssets::balance(pool_asset_id.clone(), &old_pool_account), - Preservation::Expendable, - ); - reads.saturating_accrue(1); - writes.saturating_accrue(2); - - // change the ownership of LP token - let _ = pallet_assets::Pallet::::transfer_ownership( - RuntimeOrigin::signed(sp_runtime::AccountId32::from(old_pool_account.clone())), - pool_asset_id.into(), - sp_runtime::AccountId32::from(new_pool_account.clone()).into(), - ); - reads.saturating_accrue(1); - writes.saturating_accrue(2); - - // move LocalOrForeignAssets - let _ = T::Assets::transfer( - old_pool_id.1, - &old_pool_account, - &new_pool_account, - T::Assets::balance(old_pool_id.1, &old_pool_account), - Preservation::Expendable, - ); - reads.saturating_accrue(1); - writes.saturating_accrue(2); - - // dec providers for old account - let _ = frame_system::Pallet::::dec_providers(&old_pool_account); - writes.saturating_accrue(1); - - // change pool key - pallet_asset_conversion::Pools::::insert(new_pool_id, pool_info); - pallet_asset_conversion::Pools::::remove(old_pool_id); - } - - T::DbWeight::get().reads_writes(reads, writes) - } - } -} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs index 0b108e3c022b..7ef53179c54b 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs @@ -56,9 +56,6 @@ use xcm_builder::{ }; use xcm_executor::{traits::WithOriginFilter, XcmExecutor}; -#[cfg(feature = "runtime-benchmarks")] -use {cumulus_primitives_core::ParaId, sp_core::Get}; - parameter_types! { pub const WestendLocation: MultiLocation = MultiLocation::parent(); pub const RelayNetwork: Option = Some(NetworkId::Westend); @@ -66,8 +63,8 @@ parameter_types! { pub UniversalLocation: InteriorMultiLocation = X2(GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(ParachainInfo::parachain_id().into())); pub UniversalLocationNetworkId: NetworkId = UniversalLocation::get().global_consensus().unwrap(); - pub TrustBackedAssetsPalletLocation: MultiLocation = - PalletInstance(::index() as u8).into(); + pub AssetsPalletIndex: u32 = ::index() as u32; + pub TrustBackedAssetsPalletLocation: MultiLocation = PalletInstance(AssetsPalletIndex::get() as u8).into(); pub ForeignAssetsPalletLocation: MultiLocation = PalletInstance(::index() as u8).into(); pub PoolAssetsPalletLocation: MultiLocation = @@ -649,30 +646,3 @@ impl pallet_assets::BenchmarkHelper for XcmBenchmarkHelper { MultiLocation { parents: 1, interior: X1(Parachain(id)) } } } - -#[cfg(feature = "runtime-benchmarks")] -pub struct BenchmarkMultiLocationConverter { - _phantom: sp_std::marker::PhantomData, -} - -#[cfg(feature = "runtime-benchmarks")] -impl pallet_asset_conversion::BenchmarkHelper - for BenchmarkMultiLocationConverter -where - SelfParaId: Get, -{ - fn asset_id(asset_id: u32) -> MultiLocation { - MultiLocation { - parents: 1, - interior: X3( - Parachain(SelfParaId::get().into()), - PalletInstance(::index() as u8), - GeneralIndex(asset_id.into()), - ), - } - } - - fn multiasset_id(asset_id: u32) -> MultiLocation { - Self::asset_id(asset_id) - } -} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs index 599ff90e254a..8a1f92b0ca12 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs @@ -27,9 +27,8 @@ use asset_hub_westend_runtime::{ }; pub use asset_hub_westend_runtime::{ xcm_config::{CheckingAccount, TrustBackedAssetsPalletLocation, XcmConfig}, - AllowMultiAssetPools, AssetDeposit, Assets, Balances, ExistentialDeposit, ForeignAssets, - ForeignAssetsInstance, ParachainSystem, Runtime, SessionKeys, System, - TrustBackedAssetsInstance, + AssetDeposit, Assets, Balances, ExistentialDeposit, ForeignAssets, ForeignAssetsInstance, + ParachainSystem, Runtime, SessionKeys, System, TrustBackedAssetsInstance, }; use asset_test_utils::{CollatorSessionKeys, ExtBuilder, XcmReceivedFrom}; use codec::{Decode, DecodeLimit, Encode}; From d28ffeaf8975cde403711b4b5a4e26de3373724d Mon Sep 17 00:00:00 2001 From: muharem Date: Wed, 25 Oct 2023 16:34:46 +0200 Subject: [PATCH 58/98] asset hub emulated tests --- .../emulated/assets/asset-hub-rococo/src/tests/swap.rs | 2 +- .../emulated/assets/asset-hub-westend/src/tests/swap.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-rococo/src/tests/swap.rs b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-rococo/src/tests/swap.rs index b16667eaeaec..21162d60bf26 100644 --- a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-rococo/src/tests/swap.rs +++ b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-rococo/src/tests/swap.rs @@ -355,7 +355,7 @@ fn cannot_create_pool_from_pool_assets() { Box::new(asset_native), Box::new(asset_one), ), - Err(DispatchError::Module(ModuleError{index: _, error: _, message})) => assert_eq!(message, Some("UnsupportedAsset")) + Err(DispatchError::Module(ModuleError{index: _, error: _, message})) => assert_eq!(message, Some("Unknown")) ); }); } diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/swap.rs b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/swap.rs index feb30fa0347e..1c59b2696bf5 100644 --- a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/swap.rs +++ b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/swap.rs @@ -341,7 +341,7 @@ fn cannot_create_pool_from_pool_assets() { Box::new(asset_native), Box::new(asset_one), ), - Err(DispatchError::Module(ModuleError{index: _, error: _, message})) => assert_eq!(message, Some("UnsupportedAsset")) + Err(DispatchError::Module(ModuleError{index: _, error: _, message})) => assert_eq!(message, Some("Unknown")) ); }); } From d255301880a0a9f830aac5dde02c83d364acc6d9 Mon Sep 17 00:00:00 2001 From: muharem Date: Wed, 25 Oct 2023 18:54:13 +0200 Subject: [PATCH 59/98] clippy fixes --- substrate/frame/asset-conversion/src/lib.rs | 20 ++++++++++---------- substrate/frame/asset-conversion/src/swap.rs | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/substrate/frame/asset-conversion/src/lib.rs b/substrate/frame/asset-conversion/src/lib.rs index 6cc73452b250..041877139c20 100644 --- a/substrate/frame/asset-conversion/src/lib.rs +++ b/substrate/frame/asset-conversion/src/lib.rs @@ -770,7 +770,7 @@ pub mod pallet { ) -> Result, (CreditOf, DispatchError)> { let amount_in = credit_in.peek(); let inspect_path = |credit_asset| { - ensure!(path.get(0).map_or(false, |a| *a == credit_asset), Error::::InvalidPath); + ensure!(path.first().map_or(false, |a| *a == credit_asset), Error::::InvalidPath); ensure!(!amount_in.is_zero(), Error::::ZeroAmount); ensure!(amount_out_min.map_or(true, |a| !a.is_zero()), Error::::ZeroAmount); @@ -815,7 +815,7 @@ pub mod pallet { ) -> Result<(CreditOf, CreditOf), (CreditOf, DispatchError)> { let amount_in_max = credit_in.peek(); let inspect_path = |credit_asset| { - ensure!(path.get(0).map_or(false, |a| a == &credit_asset), Error::::InvalidPath); + ensure!(path.first().map_or(false, |a| a == &credit_asset), Error::::InvalidPath); ensure!(amount_in_max > Zero::zero(), Error::::ZeroAmount); ensure!(amount_out > Zero::zero(), Error::::ZeroAmount); @@ -883,11 +883,11 @@ pub mod pallet { let resolve_path = || -> Result, DispatchError> { for pos in 0..=path.len() { if let Some([(asset1, _), (asset2, amount_out)]) = path.get(pos..=pos + 1) { - let pool_from = T::PoolLocator::pool_address(&asset1, &asset2) + let pool_from = T::PoolLocator::pool_address(asset1, asset2) .map_err(|_| Error::::InvalidAssetPair)?; if let Some((asset3, _)) = path.get(pos + 2) { - let pool_to = T::PoolLocator::pool_address(&asset2, &asset3) + let pool_to = T::PoolLocator::pool_address(asset2, asset3) .map_err(|_| Error::::InvalidAssetPair)?; T::Assets::transfer( @@ -904,7 +904,7 @@ pub mod pallet { } } } - return Err(Error::::InvalidPath.into()) + Err(Error::::InvalidPath.into()) }; let credit_out = match resolve_path() { @@ -913,7 +913,7 @@ pub mod pallet { }; let pool_to = if let Some([(asset1, _), (asset2, _)]) = path.get(0..2) { - match T::PoolLocator::pool_address(&asset1, &asset2) { + match T::PoolLocator::pool_address(asset1, asset2) { Ok(address) => address, Err(_) => return Err((credit_in, Error::::InvalidAssetPair.into())), } @@ -1121,7 +1121,7 @@ pub mod pallet { let reserve_out = T::HigherPrecisionBalance::from(*reserve_out); if reserve_in.is_zero() || reserve_out.is_zero() { - return Err(Error::::ZeroLiquidity.into()) + return Err(Error::::ZeroLiquidity) } let amount_in_with_fee = amount_in @@ -1156,11 +1156,11 @@ pub mod pallet { let reserve_out = T::HigherPrecisionBalance::from(*reserve_out); if reserve_in.is_zero() || reserve_out.is_zero() { - Err(Error::::ZeroLiquidity.into())? + Err(Error::::ZeroLiquidity)? } if amount_out >= reserve_out { - Err(Error::::AmountOutTooHigh.into())? + Err(Error::::AmountOutTooHigh)? } let numerator = reserve_in @@ -1193,7 +1193,7 @@ pub mod pallet { let mut pools = BTreeSet::::new(); for assets_pair in path.windows(2) { if let [asset1, asset2] = assets_pair { - let pool_id = T::PoolLocator::pool_id(&asset1, &asset2) + let pool_id = T::PoolLocator::pool_id(asset1, asset2) .map_err(|_| Error::::InvalidAssetPair)?; let new_element = pools.insert(pool_id); diff --git a/substrate/frame/asset-conversion/src/swap.rs b/substrate/frame/asset-conversion/src/swap.rs index f4f88e3e0695..a6154e294147 100644 --- a/substrate/frame/asset-conversion/src/swap.rs +++ b/substrate/frame/asset-conversion/src/swap.rs @@ -138,7 +138,7 @@ impl Swap for Pallet { keep_alive, ) })?; - Ok(amount_out.into()) + Ok(amount_out) } fn swap_tokens_for_exact_tokens( @@ -159,7 +159,7 @@ impl Swap for Pallet { keep_alive, ) })?; - Ok(amount_in.into()) + Ok(amount_in) } } From 4ec7496fa2632385b08fae860fcf28a523a7b5de Mon Sep 17 00:00:00 2001 From: muharem Date: Wed, 25 Oct 2023 19:02:17 +0200 Subject: [PATCH 60/98] clippy fixes --- substrate/frame/asset-conversion/src/tests.rs | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/substrate/frame/asset-conversion/src/tests.rs b/substrate/frame/asset-conversion/src/tests.rs index 5700622d426b..32423435cbb7 100644 --- a/substrate/frame/asset-conversion/src/tests.rs +++ b/substrate/frame/asset-conversion/src/tests.rs @@ -85,7 +85,7 @@ fn balance(owner: u128, token_id: NativeOrWithId) -> u128 { } fn pool_balance(owner: u128, token_id: u32) -> u128 { - <::PoolAssets>::balance(token_id, &owner) + <::PoolAssets>::balance(token_id, owner) } fn get_native_ed() -> u128 { @@ -542,9 +542,9 @@ fn can_remove_liquidity() { assert_ok!(Balances::force_set_balance( RuntimeOrigin::root(), user, - 10000000000 + ed_token_1.clone() + 10000000000 + ed_token_1 )); - assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user, 100000 + ed_token_2.clone())); + assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, user, 100000 + ed_token_2)); assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), @@ -588,9 +588,9 @@ fn can_remove_liquidity() { assert_eq!( balance(user, token_1.clone()), - 10000000000 - 1000000000 + 899991000 + ed_token_1.clone() + 10000000000 - 1000000000 + 899991000 + ed_token_1 ); - assert_eq!(balance(user, token_2.clone()), 89999 + ed_token_2.clone()); + assert_eq!(balance(user, token_2.clone()), 89999 + ed_token_2); assert_eq!(pool_balance(user, lp_token), 0); }); } @@ -1675,7 +1675,7 @@ fn swap_when_existential_deposit_would_cause_reaping_pool_account() { // causes an account removal for native token 1 locate in the middle of a swap path let amount_in = AssetConversion::balance_path_from_amount_out( 110, - vec![token_3.clone(), token_1.clone()].try_into().unwrap(), + vec![token_3.clone(), token_1.clone()], ) .unwrap() .first() @@ -1697,7 +1697,7 @@ fn swap_when_existential_deposit_would_cause_reaping_pool_account() { // causes an account removal for asset token 2 locate in the middle of a swap path let amount_in = AssetConversion::balance_path_from_amount_out( 110, - vec![token_1.clone(), token_2.clone()].try_into().unwrap(), + vec![token_1.clone(), token_2.clone()], ) .unwrap() .first() @@ -2133,15 +2133,15 @@ fn swap_transactional() { ::PoolLocator::address(&(token_1.clone(), token_3.clone())).unwrap(); assert_eq!(Balances::balance(&pool_1), liquidity1); - assert_eq!(Assets::balance(2, &pool_1), liquidity2); + assert_eq!(Assets::balance(2, pool_1), liquidity2); assert_eq!(Balances::balance(&pool_2), liquidity1); - assert_eq!(Assets::balance(3, &pool_2), liquidity2); + assert_eq!(Assets::balance(3, pool_2), liquidity2); // the amount that would cause a transfer from the last pool in the path to fail let expected_out = liquidity2 - asset_ed + 1; let amount_in = AssetConversion::balance_path_from_amount_out( expected_out, - vec![token_2.clone(), token_1.clone(), token_3.clone()].try_into().unwrap(), + vec![token_2.clone(), token_1.clone(), token_3.clone()], ) .unwrap() .first() @@ -2205,9 +2205,9 @@ fn swap_transactional() { ); assert_eq!(Balances::balance(&pool_1), liquidity1); - assert_eq!(Assets::balance(2, &pool_1), liquidity2); + assert_eq!(Assets::balance(2, pool_1), liquidity2); assert_eq!(Balances::balance(&pool_2), liquidity1); - assert_eq!(Assets::balance(3, &pool_2), liquidity2); + assert_eq!(Assets::balance(3, pool_2), liquidity2); }) } @@ -2314,7 +2314,7 @@ fn swap_credit_insufficient_amount_bounds() { .unwrap_err(); assert_eq!( error, - (expected_credit_in.into(), Error::::ProvidedMinimumNotSufficientForSwap.into()) + (expected_credit_in, Error::::ProvidedMinimumNotSufficientForSwap.into()) ); // provided `credit_in` is not sufficient to swap for desired `amount_out` From 2ce50712e389fd3fb68735e072b3ce14aec7c345 Mon Sep 17 00:00:00 2001 From: Muharem Date: Thu, 26 Oct 2023 12:40:25 +0200 Subject: [PATCH 61/98] Apply suggestions from code review Co-authored-by: Liam Aharon --- .../frame/support/src/traits/tokens/fungible/union_of.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/substrate/frame/support/src/traits/tokens/fungible/union_of.rs b/substrate/frame/support/src/traits/tokens/fungible/union_of.rs index 0ba8cc28d83c..8b46f0cb522d 100644 --- a/substrate/frame/support/src/traits/tokens/fungible/union_of.rs +++ b/substrate/frame/support/src/traits/tokens/fungible/union_of.rs @@ -34,7 +34,7 @@ use sp_runtime::{ }; use sp_std::cmp::Ordering; -/// The `NativeOrWithId` enum classifies an asset as either `Native`` to the current chain or as an +/// The `NativeOrWithId` enum classifies an asset as either `Native` to the current chain or as an /// asset with a specific ID. #[derive(Decode, Encode, Default, MaxEncodedLen, TypeInfo, Clone, Debug)] pub enum NativeOrWithId @@ -95,7 +95,7 @@ impl Convert, Either<(), AssetId>> for Nat /// - `Left` is `fungible::*` implementation that is incorporated into the resulting union. /// - `Right` is `fungibles::*` implementation that is incorporated into the resulting union. /// - `Criterion` determines whether the `AssetKind` belongs to the `Left` or `Right` set. -/// - `AssetKind` is a superset type encompassing asset kinds from `Left`` and `Right` sets. +/// - `AssetKind` is a superset type encompassing asset kinds from `Left` and `Right` sets. /// - `AccountId` is an account identifier type. pub struct UnionOf( sp_std::marker::PhantomData<(Left, Right, Criterion, AssetKind, AccountId)>, From c60fa97ff7b2f7aab35f93ed7e42937b203a68b8 Mon Sep 17 00:00:00 2001 From: muharem Date: Thu, 26 Oct 2023 13:12:36 +0200 Subject: [PATCH 62/98] fixes --- .../frame/support/src/traits/tokens/fungible/union_of.rs | 7 ++++--- .../frame/support/src/traits/tokens/fungibles/union_of.rs | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/substrate/frame/support/src/traits/tokens/fungible/union_of.rs b/substrate/frame/support/src/traits/tokens/fungible/union_of.rs index 8b46f0cb522d..43a6bae92848 100644 --- a/substrate/frame/support/src/traits/tokens/fungible/union_of.rs +++ b/substrate/frame/support/src/traits/tokens/fungible/union_of.rs @@ -31,12 +31,13 @@ use sp_runtime::{ traits::Convert, DispatchError, DispatchResult, Either, Either::{Left, Right}, + RuntimeDebug, }; use sp_std::cmp::Ordering; /// The `NativeOrWithId` enum classifies an asset as either `Native` to the current chain or as an /// asset with a specific ID. -#[derive(Decode, Encode, Default, MaxEncodedLen, TypeInfo, Clone, Debug)] +#[derive(Decode, Encode, Default, MaxEncodedLen, TypeInfo, Clone, RuntimeDebug)] pub enum NativeOrWithId where AssetId: Ord, @@ -407,10 +408,10 @@ impl< precision: Precision, ) -> Result { match Criterion::convert(asset) { - Left(()) => >::decrease_balance_on_hold( + Left(()) => >::increase_balance_on_hold( reason, who, amount, precision, ), - Right(a) => >::decrease_balance_on_hold( + Right(a) => >::increase_balance_on_hold( a, reason, who, amount, precision, ), } diff --git a/substrate/frame/support/src/traits/tokens/fungibles/union_of.rs b/substrate/frame/support/src/traits/tokens/fungibles/union_of.rs index eb6927553d49..6b093f1f6cc5 100644 --- a/substrate/frame/support/src/traits/tokens/fungibles/union_of.rs +++ b/substrate/frame/support/src/traits/tokens/fungibles/union_of.rs @@ -355,10 +355,10 @@ impl< precision: Precision, ) -> Result { match Criterion::convert(asset) { - Left(a) => >::decrease_balance_on_hold( + Left(a) => >::increase_balance_on_hold( a, reason, who, amount, precision, ), - Right(a) => >::decrease_balance_on_hold( + Right(a) => >::increase_balance_on_hold( a, reason, who, amount, precision, ), } From 3b187eb747958203982854e7215185e0ee915eff Mon Sep 17 00:00:00 2001 From: muharem Date: Thu, 26 Oct 2023 13:17:40 +0200 Subject: [PATCH 63/98] derive Eq --- substrate/frame/support/src/traits/tokens/fungible/union_of.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/substrate/frame/support/src/traits/tokens/fungible/union_of.rs b/substrate/frame/support/src/traits/tokens/fungible/union_of.rs index 43a6bae92848..3fad47afddd3 100644 --- a/substrate/frame/support/src/traits/tokens/fungible/union_of.rs +++ b/substrate/frame/support/src/traits/tokens/fungible/union_of.rs @@ -37,7 +37,7 @@ use sp_std::cmp::Ordering; /// The `NativeOrWithId` enum classifies an asset as either `Native` to the current chain or as an /// asset with a specific ID. -#[derive(Decode, Encode, Default, MaxEncodedLen, TypeInfo, Clone, RuntimeDebug)] +#[derive(Decode, Encode, Default, MaxEncodedLen, TypeInfo, Clone, RuntimeDebug, Eq)] pub enum NativeOrWithId where AssetId: Ord, @@ -75,7 +75,6 @@ impl PartialEq for NativeOrWithId { self.cmp(other) == Ordering::Equal } } -impl Eq for NativeOrWithId {} /// Criterion for [`UnionOf`] where a set for [`NativeOrWithId::Native`] asset located from the left /// and for [`NativeOrWithId::WithId`] from the right. From 03bec01f1687cd1fc2d72a876c584500622c2160 Mon Sep 17 00:00:00 2001 From: muharem Date: Thu, 26 Oct 2023 13:27:52 +0200 Subject: [PATCH 64/98] update bounds --- substrate/frame/asset-conversion/src/types.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/substrate/frame/asset-conversion/src/types.rs b/substrate/frame/asset-conversion/src/types.rs index 049a028be9ca..fd6d41a55b61 100644 --- a/substrate/frame/asset-conversion/src/types.rs +++ b/substrate/frame/asset-conversion/src/types.rs @@ -74,7 +74,7 @@ pub struct WithFirstAsset( impl PoolLocator for WithFirstAsset where - AssetKind: PartialEq + Clone + Encode, + AssetKind: Eq + Clone + Encode, AccountId: Decode, FirstAsset: Get, { @@ -98,7 +98,7 @@ pub struct Ascending(PhantomData<(AccountId, AssetKind)>); impl PoolLocator for Ascending where - AssetKind: PartialOrd + Clone + Encode, + AssetKind: Ord + Clone + Encode, AccountId: Decode, { fn pool_id(asset1: &AssetKind, asset2: &AssetKind) -> Result<(AssetKind, AssetKind), ()> { From 51c6dfc26ef3bdb3e0bacc249691e90d25d88072 Mon Sep 17 00:00:00 2001 From: muharem Date: Mon, 30 Oct 2023 15:23:33 +0100 Subject: [PATCH 65/98] benchmarks --- .../assets/asset-hub-kusama/src/lib.rs | 2 +- .../src/weights/pallet_asset_conversion.rs | 224 ++++++++--- .../assets/asset-hub-rococo/src/lib.rs | 2 +- .../src/weights/pallet_asset_conversion.rs | 224 ++++++++--- .../assets/asset-hub-westend/src/lib.rs | 2 +- .../src/weights/pallet_asset_conversion.rs | 228 ++++++++--- .../runtimes/assets/common/src/benchmarks.rs | 24 +- .../asset-conversion/src/benchmarking.rs | 376 +++++++++--------- substrate/frame/asset-conversion/src/lib.rs | 18 +- substrate/frame/asset-conversion/src/mock.rs | 2 +- .../frame/asset-conversion/src/weights.rs | 232 ++++++----- 11 files changed, 846 insertions(+), 488 deletions(-) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/lib.rs index 583fdd359a0a..a8fb78e3059e 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/lib.rs @@ -358,7 +358,7 @@ impl pallet_asset_conversion::Config for Runtime { type LiquidityWithdrawalFee = LiquidityWithdrawalFee; type LPFee = ConstU32<3>; type PalletId = AssetConversionPalletId; - type MaxSwapPathLength = ConstU32<4>; + type MaxSwapPathLength = ConstU32<3>; type MintMinLiquidity = ConstU128<100>; type WeightInfo = weights::pallet_asset_conversion::WeightInfo; #[cfg(feature = "runtime-benchmarks")] diff --git a/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/weights/pallet_asset_conversion.rs b/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/weights/pallet_asset_conversion.rs index 702f3743a720..54a458de51ae 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/weights/pallet_asset_conversion.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/weights/pallet_asset_conversion.rs @@ -17,42 +17,48 @@ //! Autogenerated weights for `pallet_asset_conversion` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-07-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-10-30, STEPS: `20`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-ynta1nyy-project-238-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: ``, WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-kusama-dev")`, DB CACHE: 1024 +//! HOSTNAME: `cob`, CPU: `` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-kusama-dev")`, DB CACHE: `1024` // Executed Command: -// target/production/polkadot-parachain +// ./target/debug/polkadot-parachain // benchmark // pallet -// --steps=50 -// --repeat=20 +// --chain=asset-hub-kusama-dev +// --steps=20 +// --repeat=2 +// --pallet=pallet-asset-conversion // --extrinsic=* // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/builds/parity/mirrors/cumulus/.git/.artifacts/bench.json -// --pallet=pallet_asset_conversion -// --chain=asset-hub-kusama-dev -// --header=./file_header.txt -// --output=./parachains/runtimes/assets/asset-hub-kusama/src/weights/ +// --output=./cumulus/parachains/runtimes/assets/asset-hub-kusama/src/weights/pallet_asset_conversion.rs +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] #![allow(unused_imports)] #![allow(missing_docs)] -use frame_support::{traits::Get, weights::Weight}; +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions for `pallet_asset_conversion`. -pub struct WeightInfo(PhantomData); -impl pallet_asset_conversion::WeightInfo for WeightInfo { +/// Weight functions needed for `pallet_asset_conversion`. +pub trait WeightInfo { + fn create_pool() -> Weight; + fn add_liquidity() -> Weight; + fn remove_liquidity() -> Weight; + fn swap_exact_tokens_for_tokens(n: u32, ) -> Weight; + fn swap_tokens_for_exact_tokens(n: u32, ) -> Weight; +} + +/// Weights for `pallet_asset_conversion` using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { /// Storage: `AssetConversion::Pools` (r:1 w:1) /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`) - /// Storage: UNKNOWN KEY `0x76a2c49709deec21d9c05f96c1f47351` (r:1 w:0) - /// Proof: UNKNOWN KEY `0x76a2c49709deec21d9c05f96c1f47351` (r:1 w:0) - /// Storage: `System::Account` (r:2 w:1) + /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `ForeignAssets::Account` (r:1 w:1) /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) @@ -66,22 +72,21 @@ impl pallet_asset_conversion::WeightInfo for WeightInfo /// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) fn create_pool() -> Weight { // Proof Size summary in bytes: - // Measured: `480` - // Estimated: `6196` - // Minimum execution time: 88_484_000 picoseconds. - Weight::from_parts(92_964_000, 0) - .saturating_add(Weight::from_parts(0, 6196)) - .saturating_add(T::DbWeight::get().reads(9)) - .saturating_add(T::DbWeight::get().writes(7)) + // Measured: `408` + // Estimated: `4689` + // Minimum execution time: 909_000_000 picoseconds. + Weight::from_parts(944_000_000, 4689) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(7_u64)) } /// Storage: `AssetConversion::Pools` (r:1 w:0) /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `ForeignAssets::Asset` (r:1 w:1) /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) /// Storage: `ForeignAssets::Account` (r:2 w:2) /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `PoolAssets::Asset` (r:1 w:1) /// Proof: `PoolAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) /// Storage: `PoolAssets::Account` (r:2 w:2) @@ -90,35 +95,138 @@ impl pallet_asset_conversion::WeightInfo for WeightInfo // Proof Size summary in bytes: // Measured: `1117` // Estimated: `7404` - // Minimum execution time: 153_015_000 picoseconds. - Weight::from_parts(157_018_000, 0) - .saturating_add(Weight::from_parts(0, 7404)) - .saturating_add(T::DbWeight::get().reads(8)) - .saturating_add(T::DbWeight::get().writes(7)) + // Minimum execution time: 1_629_000_000 picoseconds. + Weight::from_parts(1_766_000_000, 7404) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(7_u64)) + } + /// Storage: `AssetConversion::Pools` (r:1 w:0) + /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:2 w:2) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `PoolAssets::Asset` (r:1 w:1) + /// Proof: `PoolAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `PoolAssets::Account` (r:1 w:1) + /// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + fn remove_liquidity() -> Weight { + // Proof Size summary in bytes: + // Measured: `1106` + // Estimated: `7404` + // Minimum execution time: 1_431_000_000 picoseconds. + Weight::from_parts(1_517_000_000, 7404) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:2 w:2) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:4 w:4) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `n` is `[2, 3]`. + fn swap_exact_tokens_for_tokens(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0 + n * (557 ±0)` + // Estimated: `7404 + n * (393 ±180)` + // Minimum execution time: 922_000_000 picoseconds. + Weight::from_parts(954_000_000, 7404) + // Standard Error: 15_697_849 + .saturating_add(Weight::from_parts(39_602_040, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + .saturating_add(Weight::from_parts(0, 393).saturating_mul(n.into())) + } + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Asset` (r:2 w:2) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:4 w:4) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) + /// The range of component `n` is `[2, 3]`. + fn swap_tokens_for_exact_tokens(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0 + n * (557 ±0)` + // Estimated: `7404 + n * (393 ±73)` + // Minimum execution time: 929_000_000 picoseconds. + Weight::from_parts(947_000_000, 7404) + // Standard Error: 15_496_079 + .saturating_add(Weight::from_parts(41_295_918, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + .saturating_add(Weight::from_parts(0, 393).saturating_mul(n.into())) + } +} + +// For backwards compatibility and tests. +impl WeightInfo for () { + /// Storage: `AssetConversion::Pools` (r:1 w:1) + /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:1 w:1) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) + /// Storage: `AssetConversion::NextPoolAssetId` (r:1 w:1) + /// Proof: `AssetConversion::NextPoolAssetId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `PoolAssets::Asset` (r:1 w:1) + /// Proof: `PoolAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `PoolAssets::Account` (r:1 w:1) + /// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + fn create_pool() -> Weight { + // Proof Size summary in bytes: + // Measured: `408` + // Estimated: `4689` + // Minimum execution time: 909_000_000 picoseconds. + Weight::from_parts(944_000_000, 4689) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(7_u64)) } /// Storage: `AssetConversion::Pools` (r:1 w:0) /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:2 w:2) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `PoolAssets::Asset` (r:1 w:1) + /// Proof: `PoolAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `PoolAssets::Account` (r:2 w:2) + /// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + fn add_liquidity() -> Weight { + // Proof Size summary in bytes: + // Measured: `1117` + // Estimated: `7404` + // Minimum execution time: 1_629_000_000 picoseconds. + Weight::from_parts(1_766_000_000, 7404) + .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(RocksDbWeight::get().writes(7_u64)) + } + /// Storage: `AssetConversion::Pools` (r:1 w:0) + /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`) /// Storage: `ForeignAssets::Asset` (r:1 w:1) /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) /// Storage: `ForeignAssets::Account` (r:2 w:2) /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `PoolAssets::Asset` (r:1 w:1) /// Proof: `PoolAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: UNKNOWN KEY `0x2433d831722b1f4aeb1666953f1c0e77` (r:1 w:0) - /// Proof: UNKNOWN KEY `0x2433d831722b1f4aeb1666953f1c0e77` (r:1 w:0) /// Storage: `PoolAssets::Account` (r:1 w:1) /// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) fn remove_liquidity() -> Weight { // Proof Size summary in bytes: // Measured: `1106` // Estimated: `7404` - // Minimum execution time: 141_726_000 picoseconds. - Weight::from_parts(147_865_000, 0) - .saturating_add(Weight::from_parts(0, 7404)) - .saturating_add(T::DbWeight::get().reads(8)) - .saturating_add(T::DbWeight::get().writes(6)) + // Minimum execution time: 1_431_000_000 picoseconds. + Weight::from_parts(1_517_000_000, 7404) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) } /// Storage: `ForeignAssets::Asset` (r:2 w:2) /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) @@ -126,15 +234,18 @@ impl pallet_asset_conversion::WeightInfo for WeightInfo /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:2 w:2) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn swap_exact_tokens_for_tokens() -> Weight { + /// The range of component `n` is `[2, 3]`. + fn swap_exact_tokens_for_tokens(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1148` - // Estimated: `13818` - // Minimum execution time: 168_619_000 picoseconds. - Weight::from_parts(174_283_000, 0) - .saturating_add(Weight::from_parts(0, 13818)) - .saturating_add(T::DbWeight::get().reads(8)) - .saturating_add(T::DbWeight::get().writes(8)) + // Measured: `0 + n * (557 ±0)` + // Estimated: `7404 + n * (393 ±180)` + // Minimum execution time: 922_000_000 picoseconds. + Weight::from_parts(954_000_000, 7404) + // Standard Error: 15_697_849 + .saturating_add(Weight::from_parts(39_602_040, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + .saturating_add(Weight::from_parts(0, 393).saturating_mul(n.into())) } /// Storage: `System::Account` (r:2 w:2) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) @@ -142,14 +253,17 @@ impl pallet_asset_conversion::WeightInfo for WeightInfo /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) /// Storage: `ForeignAssets::Account` (r:4 w:4) /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) - fn swap_tokens_for_exact_tokens() -> Weight { + /// The range of component `n` is `[2, 3]`. + fn swap_tokens_for_exact_tokens(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1148` - // Estimated: `13818` - // Minimum execution time: 171_565_000 picoseconds. - Weight::from_parts(173_702_000, 0) - .saturating_add(Weight::from_parts(0, 13818)) - .saturating_add(T::DbWeight::get().reads(8)) - .saturating_add(T::DbWeight::get().writes(8)) + // Measured: `0 + n * (557 ±0)` + // Estimated: `7404 + n * (393 ±73)` + // Minimum execution time: 929_000_000 picoseconds. + Weight::from_parts(947_000_000, 7404) + // Standard Error: 15_496_079 + .saturating_add(Weight::from_parts(41_295_918, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + .saturating_add(Weight::from_parts(0, 393).saturating_mul(n.into())) } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs index dcfc41c79620..004cc9b23283 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -364,7 +364,7 @@ impl pallet_asset_conversion::Config for Runtime { type LiquidityWithdrawalFee = LiquidityWithdrawalFee; type LPFee = ConstU32<3>; type PalletId = AssetConversionPalletId; - type MaxSwapPathLength = ConstU32<4>; + type MaxSwapPathLength = ConstU32<3>; type MintMinLiquidity = ConstU128<100>; type WeightInfo = weights::pallet_asset_conversion::WeightInfo; #[cfg(feature = "runtime-benchmarks")] diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_asset_conversion.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_asset_conversion.rs index 4514fbfa8763..a416c6947ce2 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_asset_conversion.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_asset_conversion.rs @@ -17,42 +17,48 @@ //! Autogenerated weights for `pallet_asset_conversion` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-07-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-10-30, STEPS: `20`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-ynta1nyy-project-238-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: ``, WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-rococo-dev")`, DB CACHE: 1024 +//! HOSTNAME: `cob`, CPU: `` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-rococo-dev")`, DB CACHE: `1024` // Executed Command: -// target/production/polkadot-parachain +// ./target/debug/polkadot-parachain // benchmark // pallet -// --steps=50 -// --repeat=20 +// --chain=asset-hub-rococo-dev +// --steps=20 +// --repeat=2 +// --pallet=pallet-asset-conversion // --extrinsic=* // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/builds/parity/mirrors/cumulus/.git/.artifacts/bench.json -// --pallet=pallet_asset_conversion -// --chain=asset-hub-rococo-dev -// --header=./file_header.txt -// --output=./parachains/runtimes/assets/asset-hub-rococo/src/weights/ +// --output=./cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_asset_conversion.rs +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] #![allow(unused_imports)] #![allow(missing_docs)] -use frame_support::{traits::Get, weights::Weight}; +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions for `pallet_asset_conversion`. -pub struct WeightInfo(PhantomData); -impl pallet_asset_conversion::WeightInfo for WeightInfo { +/// Weight functions needed for `pallet_asset_conversion`. +pub trait WeightInfo { + fn create_pool() -> Weight; + fn add_liquidity() -> Weight; + fn remove_liquidity() -> Weight; + fn swap_exact_tokens_for_tokens(n: u32, ) -> Weight; + fn swap_tokens_for_exact_tokens(n: u32, ) -> Weight; +} + +/// Weights for `pallet_asset_conversion` using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { /// Storage: `AssetConversion::Pools` (r:1 w:1) /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`) - /// Storage: UNKNOWN KEY `0x76a2c49709deec21d9c05f96c1f47351` (r:1 w:0) - /// Proof: UNKNOWN KEY `0x76a2c49709deec21d9c05f96c1f47351` (r:1 w:0) - /// Storage: `System::Account` (r:2 w:1) + /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `ForeignAssets::Account` (r:1 w:1) /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) @@ -66,22 +72,21 @@ impl pallet_asset_conversion::WeightInfo for WeightInfo /// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) fn create_pool() -> Weight { // Proof Size summary in bytes: - // Measured: `480` - // Estimated: `6196` - // Minimum execution time: 88_484_000 picoseconds. - Weight::from_parts(92_964_000, 0) - .saturating_add(Weight::from_parts(0, 6196)) - .saturating_add(T::DbWeight::get().reads(9)) - .saturating_add(T::DbWeight::get().writes(7)) + // Measured: `408` + // Estimated: `4689` + // Minimum execution time: 917_000_000 picoseconds. + Weight::from_parts(989_000_000, 4689) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(7_u64)) } /// Storage: `AssetConversion::Pools` (r:1 w:0) /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `ForeignAssets::Asset` (r:1 w:1) /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) /// Storage: `ForeignAssets::Account` (r:2 w:2) /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `PoolAssets::Asset` (r:1 w:1) /// Proof: `PoolAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) /// Storage: `PoolAssets::Account` (r:2 w:2) @@ -90,35 +95,138 @@ impl pallet_asset_conversion::WeightInfo for WeightInfo // Proof Size summary in bytes: // Measured: `1117` // Estimated: `7404` - // Minimum execution time: 153_015_000 picoseconds. - Weight::from_parts(157_018_000, 0) - .saturating_add(Weight::from_parts(0, 7404)) - .saturating_add(T::DbWeight::get().reads(8)) - .saturating_add(T::DbWeight::get().writes(7)) + // Minimum execution time: 1_622_000_000 picoseconds. + Weight::from_parts(1_639_000_000, 7404) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(7_u64)) + } + /// Storage: `AssetConversion::Pools` (r:1 w:0) + /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:2 w:2) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `PoolAssets::Asset` (r:1 w:1) + /// Proof: `PoolAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `PoolAssets::Account` (r:1 w:1) + /// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + fn remove_liquidity() -> Weight { + // Proof Size summary in bytes: + // Measured: `1106` + // Estimated: `7404` + // Minimum execution time: 1_496_000_000 picoseconds. + Weight::from_parts(1_497_000_000, 7404) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:2 w:2) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:4 w:4) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `n` is `[2, 3]`. + fn swap_exact_tokens_for_tokens(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0 + n * (557 ±0)` + // Estimated: `7404 + n * (393 ±180)` + // Minimum execution time: 925_000_000 picoseconds. + Weight::from_parts(947_000_000, 7404) + // Standard Error: 15_819_119 + .saturating_add(Weight::from_parts(38_448_979, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + .saturating_add(Weight::from_parts(0, 393).saturating_mul(n.into())) + } + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Asset` (r:2 w:2) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:4 w:4) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) + /// The range of component `n` is `[2, 3]`. + fn swap_tokens_for_exact_tokens(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0 + n * (557 ±0)` + // Estimated: `7404 + n * (393 ±73)` + // Minimum execution time: 913_000_000 picoseconds. + Weight::from_parts(945_000_000, 7404) + // Standard Error: 17_010_580 + .saturating_add(Weight::from_parts(43_326_530, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + .saturating_add(Weight::from_parts(0, 393).saturating_mul(n.into())) + } +} + +// For backwards compatibility and tests. +impl WeightInfo for () { + /// Storage: `AssetConversion::Pools` (r:1 w:1) + /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:1 w:1) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) + /// Storage: `AssetConversion::NextPoolAssetId` (r:1 w:1) + /// Proof: `AssetConversion::NextPoolAssetId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `PoolAssets::Asset` (r:1 w:1) + /// Proof: `PoolAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `PoolAssets::Account` (r:1 w:1) + /// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + fn create_pool() -> Weight { + // Proof Size summary in bytes: + // Measured: `408` + // Estimated: `4689` + // Minimum execution time: 917_000_000 picoseconds. + Weight::from_parts(989_000_000, 4689) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(7_u64)) } /// Storage: `AssetConversion::Pools` (r:1 w:0) /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:2 w:2) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `PoolAssets::Asset` (r:1 w:1) + /// Proof: `PoolAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `PoolAssets::Account` (r:2 w:2) + /// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + fn add_liquidity() -> Weight { + // Proof Size summary in bytes: + // Measured: `1117` + // Estimated: `7404` + // Minimum execution time: 1_622_000_000 picoseconds. + Weight::from_parts(1_639_000_000, 7404) + .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(RocksDbWeight::get().writes(7_u64)) + } + /// Storage: `AssetConversion::Pools` (r:1 w:0) + /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`) /// Storage: `ForeignAssets::Asset` (r:1 w:1) /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) /// Storage: `ForeignAssets::Account` (r:2 w:2) /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `PoolAssets::Asset` (r:1 w:1) /// Proof: `PoolAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: UNKNOWN KEY `0x2433d831722b1f4aeb1666953f1c0e77` (r:1 w:0) - /// Proof: UNKNOWN KEY `0x2433d831722b1f4aeb1666953f1c0e77` (r:1 w:0) /// Storage: `PoolAssets::Account` (r:1 w:1) /// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) fn remove_liquidity() -> Weight { // Proof Size summary in bytes: // Measured: `1106` // Estimated: `7404` - // Minimum execution time: 141_726_000 picoseconds. - Weight::from_parts(147_865_000, 0) - .saturating_add(Weight::from_parts(0, 7404)) - .saturating_add(T::DbWeight::get().reads(8)) - .saturating_add(T::DbWeight::get().writes(6)) + // Minimum execution time: 1_496_000_000 picoseconds. + Weight::from_parts(1_497_000_000, 7404) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) } /// Storage: `ForeignAssets::Asset` (r:2 w:2) /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) @@ -126,15 +234,18 @@ impl pallet_asset_conversion::WeightInfo for WeightInfo /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:2 w:2) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn swap_exact_tokens_for_tokens() -> Weight { + /// The range of component `n` is `[2, 3]`. + fn swap_exact_tokens_for_tokens(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1148` - // Estimated: `13818` - // Minimum execution time: 168_619_000 picoseconds. - Weight::from_parts(174_283_000, 0) - .saturating_add(Weight::from_parts(0, 13818)) - .saturating_add(T::DbWeight::get().reads(8)) - .saturating_add(T::DbWeight::get().writes(8)) + // Measured: `0 + n * (557 ±0)` + // Estimated: `7404 + n * (393 ±180)` + // Minimum execution time: 925_000_000 picoseconds. + Weight::from_parts(947_000_000, 7404) + // Standard Error: 15_819_119 + .saturating_add(Weight::from_parts(38_448_979, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + .saturating_add(Weight::from_parts(0, 393).saturating_mul(n.into())) } /// Storage: `System::Account` (r:2 w:2) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) @@ -142,14 +253,17 @@ impl pallet_asset_conversion::WeightInfo for WeightInfo /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) /// Storage: `ForeignAssets::Account` (r:4 w:4) /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) - fn swap_tokens_for_exact_tokens() -> Weight { + /// The range of component `n` is `[2, 3]`. + fn swap_tokens_for_exact_tokens(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1148` - // Estimated: `13818` - // Minimum execution time: 171_565_000 picoseconds. - Weight::from_parts(173_702_000, 0) - .saturating_add(Weight::from_parts(0, 13818)) - .saturating_add(T::DbWeight::get().reads(8)) - .saturating_add(T::DbWeight::get().writes(8)) + // Measured: `0 + n * (557 ±0)` + // Estimated: `7404 + n * (393 ±73)` + // Minimum execution time: 913_000_000 picoseconds. + Weight::from_parts(945_000_000, 7404) + // Standard Error: 17_010_580 + .saturating_add(Weight::from_parts(43_326_530, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + .saturating_add(Weight::from_parts(0, 393).saturating_mul(n.into())) } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index 8f7518ee6bf3..fe9bb3dd99e0 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -335,7 +335,7 @@ impl pallet_asset_conversion::Config for Runtime { type LiquidityWithdrawalFee = LiquidityWithdrawalFee; type LPFee = ConstU32<3>; type PalletId = AssetConversionPalletId; - type MaxSwapPathLength = ConstU32<4>; + type MaxSwapPathLength = ConstU32<3>; type MintMinLiquidity = ConstU128<100>; type WeightInfo = weights::pallet_asset_conversion::WeightInfo; #[cfg(feature = "runtime-benchmarks")] diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_asset_conversion.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_asset_conversion.rs index 2f39df654030..87c22abfcf57 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_asset_conversion.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_asset_conversion.rs @@ -17,44 +17,48 @@ //! Autogenerated weights for `pallet_asset_conversion` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-07-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-10-30, STEPS: `20`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-ynta1nyy-project-238-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: ``, WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-westend-dev")`, DB CACHE: 1024 +//! HOSTNAME: `cob`, CPU: `` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-westend-dev")`, DB CACHE: `1024` // Executed Command: -// ./target/production/polkadot-parachain +// ./target/debug/polkadot-parachain // benchmark // pallet // --chain=asset-hub-westend-dev -// --wasm-execution=compiled -// --pallet=pallet_asset_conversion -// --no-storage-info -// --no-median-slopes -// --no-min-squares +// --steps=20 +// --repeat=2 +// --pallet=pallet-asset-conversion // --extrinsic=* -// --steps=50 -// --repeat=20 -// --json -// --header=./file_header.txt -// --output=./parachains/runtimes/assets/asset-hub-westend/src/weights/ +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_asset_conversion.rs +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] #![allow(unused_imports)] #![allow(missing_docs)] -use frame_support::{traits::Get, weights::Weight}; +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions for `pallet_asset_conversion`. -pub struct WeightInfo(PhantomData); -impl pallet_asset_conversion::WeightInfo for WeightInfo { +/// Weight functions needed for `pallet_asset_conversion`. +pub trait WeightInfo { + fn create_pool() -> Weight; + fn add_liquidity() -> Weight; + fn remove_liquidity() -> Weight; + fn swap_exact_tokens_for_tokens(n: u32, ) -> Weight; + fn swap_tokens_for_exact_tokens(n: u32, ) -> Weight; +} + +/// Weights for `pallet_asset_conversion` using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { /// Storage: `AssetConversion::Pools` (r:1 w:1) /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`) - /// Storage: UNKNOWN KEY `0x76a2c49709deec21d9c05f96c1f47351` (r:1 w:0) - /// Proof: UNKNOWN KEY `0x76a2c49709deec21d9c05f96c1f47351` (r:1 w:0) - /// Storage: `System::Account` (r:2 w:1) + /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `ForeignAssets::Account` (r:1 w:1) /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) @@ -68,22 +72,21 @@ impl pallet_asset_conversion::WeightInfo for WeightInfo /// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) fn create_pool() -> Weight { // Proof Size summary in bytes: - // Measured: `480` - // Estimated: `6196` - // Minimum execution time: 90_011_000 picoseconds. - Weight::from_parts(92_372_000, 0) - .saturating_add(Weight::from_parts(0, 6196)) - .saturating_add(T::DbWeight::get().reads(9)) - .saturating_add(T::DbWeight::get().writes(7)) + // Measured: `408` + // Estimated: `4689` + // Minimum execution time: 904_000_000 picoseconds. + Weight::from_parts(938_000_000, 4689) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(7_u64)) } /// Storage: `AssetConversion::Pools` (r:1 w:0) /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `ForeignAssets::Asset` (r:1 w:1) /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) /// Storage: `ForeignAssets::Account` (r:2 w:2) /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `PoolAssets::Asset` (r:1 w:1) /// Proof: `PoolAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) /// Storage: `PoolAssets::Account` (r:2 w:2) @@ -92,35 +95,138 @@ impl pallet_asset_conversion::WeightInfo for WeightInfo // Proof Size summary in bytes: // Measured: `1117` // Estimated: `7404` - // Minimum execution time: 153_484_000 picoseconds. - Weight::from_parts(155_465_000, 0) - .saturating_add(Weight::from_parts(0, 7404)) - .saturating_add(T::DbWeight::get().reads(8)) - .saturating_add(T::DbWeight::get().writes(7)) + // Minimum execution time: 1_613_000_000 picoseconds. + Weight::from_parts(1_923_000_000, 7404) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(7_u64)) + } + /// Storage: `AssetConversion::Pools` (r:1 w:0) + /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:2 w:2) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `PoolAssets::Asset` (r:1 w:1) + /// Proof: `PoolAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `PoolAssets::Account` (r:1 w:1) + /// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + fn remove_liquidity() -> Weight { + // Proof Size summary in bytes: + // Measured: `1106` + // Estimated: `7404` + // Minimum execution time: 1_479_000_000 picoseconds. + Weight::from_parts(1_559_000_000, 7404) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + /// Storage: `ForeignAssets::Asset` (r:2 w:2) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:4 w:4) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `n` is `[2, 3]`. + fn swap_exact_tokens_for_tokens(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0 + n * (557 ±0)` + // Estimated: `7404 + n * (393 ±180)` + // Minimum execution time: 920_000_000 picoseconds. + Weight::from_parts(950_000_000, 7404) + // Standard Error: 18_279_661 + .saturating_add(Weight::from_parts(47_071_428, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + .saturating_add(Weight::from_parts(0, 393).saturating_mul(n.into())) + } + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Asset` (r:2 w:2) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:4 w:4) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) + /// The range of component `n` is `[2, 3]`. + fn swap_tokens_for_exact_tokens(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0 + n * (557 ±0)` + // Estimated: `7404 + n * (393 ±180)` + // Minimum execution time: 932_000_000 picoseconds. + Weight::from_parts(944_000_000, 7404) + // Standard Error: 14_264_382 + .saturating_add(Weight::from_parts(41_938_775, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + .saturating_add(Weight::from_parts(0, 393).saturating_mul(n.into())) + } +} + +// For backwards compatibility and tests. +impl WeightInfo for () { + /// Storage: `AssetConversion::Pools` (r:1 w:1) + /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:1 w:1) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) + /// Storage: `AssetConversion::NextPoolAssetId` (r:1 w:1) + /// Proof: `AssetConversion::NextPoolAssetId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `PoolAssets::Asset` (r:1 w:1) + /// Proof: `PoolAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `PoolAssets::Account` (r:1 w:1) + /// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + fn create_pool() -> Weight { + // Proof Size summary in bytes: + // Measured: `408` + // Estimated: `4689` + // Minimum execution time: 904_000_000 picoseconds. + Weight::from_parts(938_000_000, 4689) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(7_u64)) } /// Storage: `AssetConversion::Pools` (r:1 w:0) /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Asset` (r:1 w:1) + /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) + /// Storage: `ForeignAssets::Account` (r:2 w:2) + /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `PoolAssets::Asset` (r:1 w:1) + /// Proof: `PoolAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `PoolAssets::Account` (r:2 w:2) + /// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + fn add_liquidity() -> Weight { + // Proof Size summary in bytes: + // Measured: `1117` + // Estimated: `7404` + // Minimum execution time: 1_613_000_000 picoseconds. + Weight::from_parts(1_923_000_000, 7404) + .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(RocksDbWeight::get().writes(7_u64)) + } + /// Storage: `AssetConversion::Pools` (r:1 w:0) + /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`) /// Storage: `ForeignAssets::Asset` (r:1 w:1) /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) /// Storage: `ForeignAssets::Account` (r:2 w:2) /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `PoolAssets::Asset` (r:1 w:1) /// Proof: `PoolAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: UNKNOWN KEY `0x2433d831722b1f4aeb1666953f1c0e77` (r:1 w:0) - /// Proof: UNKNOWN KEY `0x2433d831722b1f4aeb1666953f1c0e77` (r:1 w:0) /// Storage: `PoolAssets::Account` (r:1 w:1) /// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) fn remove_liquidity() -> Weight { // Proof Size summary in bytes: // Measured: `1106` // Estimated: `7404` - // Minimum execution time: 141_326_000 picoseconds. - Weight::from_parts(143_882_000, 0) - .saturating_add(Weight::from_parts(0, 7404)) - .saturating_add(T::DbWeight::get().reads(8)) - .saturating_add(T::DbWeight::get().writes(6)) + // Minimum execution time: 1_479_000_000 picoseconds. + Weight::from_parts(1_559_000_000, 7404) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) } /// Storage: `ForeignAssets::Asset` (r:2 w:2) /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) @@ -128,15 +234,18 @@ impl pallet_asset_conversion::WeightInfo for WeightInfo /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:2 w:2) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn swap_exact_tokens_for_tokens() -> Weight { + /// The range of component `n` is `[2, 3]`. + fn swap_exact_tokens_for_tokens(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1148` - // Estimated: `13818` - // Minimum execution time: 168_556_000 picoseconds. - Weight::from_parts(170_313_000, 0) - .saturating_add(Weight::from_parts(0, 13818)) - .saturating_add(T::DbWeight::get().reads(8)) - .saturating_add(T::DbWeight::get().writes(8)) + // Measured: `0 + n * (557 ±0)` + // Estimated: `7404 + n * (393 ±180)` + // Minimum execution time: 920_000_000 picoseconds. + Weight::from_parts(950_000_000, 7404) + // Standard Error: 18_279_661 + .saturating_add(Weight::from_parts(47_071_428, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + .saturating_add(Weight::from_parts(0, 393).saturating_mul(n.into())) } /// Storage: `System::Account` (r:2 w:2) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) @@ -144,14 +253,17 @@ impl pallet_asset_conversion::WeightInfo for WeightInfo /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) /// Storage: `ForeignAssets::Account` (r:4 w:4) /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) - fn swap_tokens_for_exact_tokens() -> Weight { + /// The range of component `n` is `[2, 3]`. + fn swap_tokens_for_exact_tokens(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1148` - // Estimated: `13818` - // Minimum execution time: 167_704_000 picoseconds. - Weight::from_parts(170_034_000, 0) - .saturating_add(Weight::from_parts(0, 13818)) - .saturating_add(T::DbWeight::get().reads(8)) - .saturating_add(T::DbWeight::get().writes(8)) + // Measured: `0 + n * (557 ±0)` + // Estimated: `7404 + n * (393 ±180)` + // Minimum execution time: 932_000_000 picoseconds. + Weight::from_parts(944_000_000, 7404) + // Standard Error: 14_264_382 + .saturating_add(Weight::from_parts(41_938_775, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + .saturating_add(Weight::from_parts(0, 393).saturating_mul(n.into())) } } diff --git a/cumulus/parachains/runtimes/assets/common/src/benchmarks.rs b/cumulus/parachains/runtimes/assets/common/src/benchmarks.rs index ed1379695317..344cb5ca3368 100644 --- a/cumulus/parachains/runtimes/assets/common/src/benchmarks.rs +++ b/cumulus/parachains/runtimes/assets/common/src/benchmarks.rs @@ -26,17 +26,19 @@ impl, SelfParaId: Get, PalletId: Get> pallet_asset_conversion::BenchmarkHelper for AssetPairFactory { - fn create_pair(_seed1: u32, seed2: u32) -> (MultiLocation, MultiLocation) { - ( - Target::get(), - MultiLocation::new( - 1, - X3( - Parachain(SelfParaId::get().into()), - PalletInstance(PalletId::get() as u8), - GeneralIndex(seed2.into()), - ), + fn create_pair(seed1: u32, seed2: u32) -> (MultiLocation, MultiLocation) { + let with_id = MultiLocation::new( + 1, + X3( + Parachain(SelfParaId::get().into()), + PalletInstance(PalletId::get() as u8), + GeneralIndex(seed2.into()), ), - ) + ); + if seed1 % 2 == 0 { + (with_id, Target::get()) + } else { + (Target::get(), with_id) + } } } diff --git a/substrate/frame/asset-conversion/src/benchmarking.rs b/substrate/frame/asset-conversion/src/benchmarking.rs index 2892995830bc..f0e02c802ad8 100644 --- a/substrate/frame/asset-conversion/src/benchmarking.rs +++ b/substrate/frame/asset-conversion/src/benchmarking.rs @@ -29,12 +29,14 @@ use frame_support::{ }; use frame_system::RawOrigin as SystemOrigin; use sp_core::Get; -use sp_runtime::traits::StaticLookup; use sp_std::{marker::PhantomData, prelude::*}; /// Benchmark Helper pub trait BenchmarkHelper { /// Returns a valid assets pair for the pool creation. + /// + /// When a specific asset, such as the native asset, is required in every pool, it should be + /// returned for each odd-numbered seed. fn create_pair(seed1: u32, seed2: u32) -> (AssetKind, AssetKind); } @@ -53,65 +55,105 @@ pub struct NativeOrWithIdFactory(PhantomData); impl + Ord> BenchmarkHelper> for NativeOrWithIdFactory { - fn create_pair(_seed1: u32, seed2: u32) -> (NativeOrWithId, NativeOrWithId) { - (NativeOrWithId::Native, NativeOrWithId::WithId(seed2.into())) + fn create_pair(seed1: u32, seed2: u32) -> (NativeOrWithId, NativeOrWithId) { + if seed1 % 2 == 0 { + (NativeOrWithId::WithId(seed2.into()), NativeOrWithId::Native) + } else { + (NativeOrWithId::Native, NativeOrWithId::WithId(seed2.into())) + } } } -const INITIAL_ASSET_BALANCE: u128 = 1_000_000_000_000; -type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; - -fn get_lp_token_id() -> T::PoolAssetId +/// Provides a pair of amounts expected to serve as sufficient initial liquidity for a pool. +fn valid_liquidity_amount(ed1: T::Balance, ed2: T::Balance) -> (T::Balance, T::Balance) where - T::PoolAssetId: Into, + T::Assets: Inspect, { - let next_id: u32 = AssetConversion::::get_next_pool_asset_id().into(); - (next_id - 1).into() + let l = + ed1.max(ed2) + T::MintMinLiquidity::get() + T::MintMinLiquidity::get() + T::Balance::one(); + (l, l) } -fn create_asset(asset: &T::AssetKind) -> (T::AccountId, AccountIdLookupOf) +/// Create the `asset` and mint the `amount` for the `caller`. +fn create_asset(caller: &T::AccountId, asset: &T::AssetKind, amount: T::Balance) where - T::Balance: From, T::Assets: Create + Mutate, { - let caller: T::AccountId = whitelisted_caller(); - let caller_lookup = T::Lookup::unlookup(caller.clone()); if !T::Assets::asset_exists(asset.clone()) { - assert_ok!(T::Assets::create(asset.clone(), caller.clone(), true, 1.into())); + assert_ok!(T::Assets::create(asset.clone(), caller.clone(), true, T::Balance::one())); } - assert_ok!(T::Assets::mint_into(asset.clone(), &caller, INITIAL_ASSET_BALANCE.into())); - - (caller, caller_lookup) + assert_ok!(T::Assets::mint_into( + asset.clone(), + &caller, + amount + T::Assets::minimum_balance(asset.clone()) + )); } -fn create_asset_and_pool( - asset1: &T::AssetKind, - asset2: &T::AssetKind, -) -> (T::PoolAssetId, T::AccountId, AccountIdLookupOf) +/// Create the designated fee asset for pool creation. +fn create_fee_asset(caller: &T::AccountId) where - T::Balance: From, T::Assets: Create + Mutate, - T::PoolAssetId: Into, { let fee_asset = T::PoolSetupFeeAsset::get(); - let (_, _) = create_asset::(&fee_asset); - let (_, _) = create_asset::(asset1); - let (caller, caller_lookup) = create_asset::(asset2); - + if !T::Assets::asset_exists(fee_asset.clone()) { + assert_ok!(T::Assets::create(fee_asset.clone(), caller.clone(), true, T::Balance::one())); + } assert_ok!(T::Assets::mint_into( fee_asset.clone(), &caller, - T::PoolSetupFee::get() + T::Assets::minimum_balance(fee_asset) + T::Assets::minimum_balance(fee_asset) + )); +} + +/// Mint the fee asset for the `caller` sufficient to cover the fee for creating a new pool. +fn mint_setup_fee_asset( + caller: &T::AccountId, + asset1: &T::AssetKind, + asset2: &T::AssetKind, + lp_token: &T::PoolAssetId, +) where + T::Assets: Create + Mutate, +{ + assert_ok!(T::Assets::mint_into( + T::PoolSetupFeeAsset::get(), + &caller, + T::PoolSetupFee::get() + + T::Assets::deposit_required(asset1.clone()) + + T::Assets::deposit_required(asset2.clone()) + + T::PoolAssets::deposit_required(lp_token.clone()) )); +} + +/// Creates a pool for a given asset pair. +/// +/// This action mints the necessary amounts of the given assets for the `caller` to provide initial +/// liquidity. It returns the LP token ID along with a pair of amounts sufficient for the pool's +/// initial liquidity. +fn create_asset_and_pool( + caller: &T::AccountId, + asset1: &T::AssetKind, + asset2: &T::AssetKind, +) -> (T::PoolAssetId, T::Balance, T::Balance) +where + T::Assets: Create + Mutate, +{ + let (liquidity1, liquidity2) = valid_liquidity_amount::( + T::Assets::minimum_balance(asset1.clone()), + T::Assets::minimum_balance(asset2.clone()), + ); + create_asset::(caller, asset1, liquidity1); + create_asset::(caller, asset2, liquidity2); + let lp_token = AssetConversion::::get_next_pool_asset_id(); + + mint_setup_fee_asset::(caller, asset1, asset2, &lp_token); assert_ok!(AssetConversion::::create_pool( SystemOrigin::Signed(caller.clone()).into(), Box::new(asset1.clone()), Box::new(asset2.clone()) )); - let lp_token = get_lp_token_id::(); - (lp_token, caller, caller_lookup) + (lp_token, liquidity1, liquidity2) } fn assert_last_event(generic_event: ::RuntimeEvent) { @@ -122,82 +164,79 @@ fn assert_last_event(generic_event: ::RuntimeEvent) { assert_eq!(event, &system_event); } -#[benchmarks(where T::Balance: From + Into, T::Assets: Create + Mutate, T::PoolAssetId: Into,)] +#[benchmarks(where T::Assets: Create + Mutate, T::PoolAssetId: Into,)] mod benchmarks { use super::*; #[benchmark] fn create_pool() { + let caller: T::AccountId = whitelisted_caller(); let (asset1, asset2) = T::BenchmarkHelper::create_pair(0, 1); - let (_, _) = create_asset::(&asset1); - let (caller, _) = create_asset::(&asset2); + create_asset::(&caller, &asset1, T::Assets::minimum_balance(asset1.clone())); + create_asset::(&caller, &asset2, T::Assets::minimum_balance(asset2.clone())); - assert_ok!(T::Assets::mint_into( - T::PoolSetupFeeAsset::get(), - &caller, - T::PoolSetupFee::get() + T::Assets::minimum_balance(T::PoolSetupFeeAsset::get()) - )); + let lp_token = AssetConversion::::get_next_pool_asset_id(); + create_fee_asset::(&caller); + mint_setup_fee_asset::(&caller, &asset1, &asset2, &lp_token); #[extrinsic_call] _(SystemOrigin::Signed(caller.clone()), Box::new(asset1.clone()), Box::new(asset2.clone())); - let lp_token = get_lp_token_id::(); let pool_id = T::PoolLocator::pool_id(&asset1, &asset2).unwrap(); let pool_account = T::PoolLocator::address(&pool_id).unwrap(); assert_last_event::( - Event::PoolCreated { creator: caller.clone(), pool_account, pool_id, lp_token }.into(), + Event::PoolCreated { creator: caller, pool_account, pool_id, lp_token }.into(), ); } #[benchmark] fn add_liquidity() { + let caller: T::AccountId = whitelisted_caller(); let (asset1, asset2) = T::BenchmarkHelper::create_pair(0, 1); - let (lp_token, caller, _) = create_asset_and_pool::(&asset1, &asset2); - let ed: u128 = T::Assets::minimum_balance(asset1.clone()).into(); - let add_amount = 1000 + ed; + + create_fee_asset::(&caller); + let (lp_token, liquidity1, liquidity2) = + create_asset_and_pool::(&caller, &asset1, &asset2); #[extrinsic_call] _( SystemOrigin::Signed(caller.clone()), Box::new(asset1.clone()), Box::new(asset2.clone()), - add_amount.into(), - 1000.into(), - 0.into(), - 0.into(), + liquidity1, + liquidity2, + T::Balance::one(), + T::Balance::zero(), caller.clone(), ); let pool_account = T::PoolLocator::pool_address(&asset1, &asset2).unwrap(); let lp_minted = - AssetConversion::::calc_lp_amount_for_zero_supply(&add_amount.into(), &1000.into()) - .unwrap() - .into(); - assert_eq!(T::PoolAssets::balance(lp_token, &caller), lp_minted.into()); - assert_eq!(T::Assets::balance(asset1, &pool_account), add_amount.into()); - assert_eq!(T::Assets::balance(asset2, &pool_account), 1000.into()); + AssetConversion::::calc_lp_amount_for_zero_supply(&liquidity1, &liquidity2).unwrap(); + assert_eq!(T::PoolAssets::balance(lp_token, &caller), lp_minted); + assert_eq!(T::Assets::balance(asset1, &pool_account), liquidity1); + assert_eq!(T::Assets::balance(asset2, &pool_account), liquidity2); } #[benchmark] fn remove_liquidity() { + let caller: T::AccountId = whitelisted_caller(); let (asset1, asset2) = T::BenchmarkHelper::create_pair(0, 1); - let (lp_token, caller, _) = create_asset_and_pool::(&asset1, &asset2); - let ed: u128 = T::Assets::minimum_balance(asset1.clone()).into(); - let add_amount = 100 * ed; - let lp_minted = - AssetConversion::::calc_lp_amount_for_zero_supply(&add_amount.into(), &1000.into()) - .unwrap() - .into(); - let remove_lp_amount = lp_minted.checked_div(10).unwrap(); + + create_fee_asset::(&caller); + let (lp_token, liquidity1, liquidity2) = + create_asset_and_pool::(&caller, &asset1, &asset2); + + let remove_lp_amount = T::Balance::one(); assert_ok!(AssetConversion::::add_liquidity( SystemOrigin::Signed(caller.clone()).into(), Box::new(asset1.clone()), Box::new(asset2.clone()), - add_amount.into(), - 1000.into(), - 0.into(), - 0.into(), + liquidity1, + liquidity2, + T::Balance::one(), + T::Balance::zero(), caller.clone(), )); let total_supply = @@ -208,142 +247,115 @@ mod benchmarks { SystemOrigin::Signed(caller.clone()), Box::new(asset1), Box::new(asset2), - remove_lp_amount.into(), - 0.into(), - 0.into(), + remove_lp_amount, + T::Balance::zero(), + T::Balance::zero(), caller.clone(), ); - let new_total_supply = - >::total_issuance(lp_token.clone()); - assert_eq!(new_total_supply, total_supply - remove_lp_amount.into()); + let new_total_supply = >::total_issuance(lp_token); + assert_eq!(new_total_supply, total_supply - remove_lp_amount); } #[benchmark] - fn swap_exact_tokens_for_tokens() { - let (asset1, asset2) = T::BenchmarkHelper::create_pair(0, 1); - let (_, asset3) = T::BenchmarkHelper::create_pair(1, 2); - let (_, asset4) = T::BenchmarkHelper::create_pair(2, 3); - - let (_, caller, _) = create_asset_and_pool::(&asset1, &asset2); - let ed: u128 = T::Assets::minimum_balance(asset1.clone()).into(); - - assert_ok!(AssetConversion::::add_liquidity( - SystemOrigin::Signed(caller.clone()).into(), - Box::new(asset1.clone()), - Box::new(asset2.clone()), - (100 * ed).into(), - 200.into(), - 1.into(), - 0.into(), - caller.clone(), - )); - - let (_, _, _) = create_asset_and_pool::(&asset2, &asset3); - assert_ok!(AssetConversion::::add_liquidity( - SystemOrigin::Signed(caller.clone()).into(), - Box::new(asset2.clone()), - Box::new(asset3.clone()), - 200.into(), - 2000.into(), - 1.into(), - 0.into(), - caller.clone(), - )); - - let (_, _, _) = create_asset_and_pool::(&asset3, &asset4); - assert_ok!(AssetConversion::::add_liquidity( - SystemOrigin::Signed(caller.clone()).into(), - Box::new(asset3.clone()), - Box::new(asset4.clone()), - 2000.into(), - 2000.into(), - 1.into(), - 1.into(), - caller.clone(), + fn swap_exact_tokens_for_tokens(n: Linear<2, { T::MaxSwapPathLength::get() }>) { + let mut swap_amount = T::Balance::one(); + let mut path = vec![]; + + let caller: T::AccountId = whitelisted_caller(); + create_fee_asset::(&caller); + for n in 1..n { + let (asset1, asset2) = T::BenchmarkHelper::create_pair(n - 1, n); + swap_amount = swap_amount + T::Balance::one(); + if path.len() == 0 { + path = vec![Box::new(asset1.clone()), Box::new(asset2.clone())]; + } else { + path.push(Box::new(asset2.clone())); + } + + let (_, liquidity1, liquidity2) = create_asset_and_pool::(&caller, &asset1, &asset2); + + assert_ok!(AssetConversion::::add_liquidity( + SystemOrigin::Signed(caller.clone()).into(), + Box::new(asset1.clone()), + Box::new(asset2.clone()), + liquidity1, + liquidity2, + T::Balance::one(), + T::Balance::zero(), + caller.clone(), + )); + } + + let asset_in = *path.first().unwrap().clone(); + assert_ok!(T::Assets::mint_into( + asset_in.clone(), + &caller, + swap_amount + T::Balance::one() )); - let path = vec![ - Box::new(asset1.clone()), - Box::new(asset2.clone()), - Box::new(asset3.clone()), - Box::new(asset4.clone()), - ]; - - let swap_amount = ed.into(); - let asset1_balance = T::Assets::balance(asset1.clone(), &caller); + let init_caller_balance = T::Assets::balance(asset_in.clone(), &caller); #[extrinsic_call] - _(SystemOrigin::Signed(caller.clone()), path, swap_amount, 1.into(), caller.clone(), false); + _( + SystemOrigin::Signed(caller.clone()), + path, + swap_amount, + T::Balance::one(), + caller.clone(), + true, + ); - let new_asset1_balance = T::Assets::balance(asset1, &caller); - assert_eq!(new_asset1_balance, asset1_balance - ed.into()); + let actual_balance = T::Assets::balance(asset_in, &caller); + assert_eq!(actual_balance, init_caller_balance - swap_amount); } #[benchmark] - fn swap_tokens_for_exact_tokens() { - let (asset1, asset2) = T::BenchmarkHelper::create_pair(0, 1); - let (_, asset3) = T::BenchmarkHelper::create_pair(1, 2); - let (_, asset4) = T::BenchmarkHelper::create_pair(2, 3); - - let (_, caller, _) = create_asset_and_pool::(&asset1, &asset2); - let ed: u128 = T::Assets::minimum_balance(asset1.clone()).into(); - - assert_ok!(AssetConversion::::add_liquidity( - SystemOrigin::Signed(caller.clone()).into(), - Box::new(asset1.clone()), - Box::new(asset2.clone()), - (1000 * ed).into(), - 500.into(), - 1.into(), - 0.into(), - caller.clone(), - )); - - let (_, _, _) = create_asset_and_pool::(&asset2, &asset3); - assert_ok!(AssetConversion::::add_liquidity( - SystemOrigin::Signed(caller.clone()).into(), - Box::new(asset2.clone()), - Box::new(asset3.clone()), - 2000.into(), - 2000.into(), - 1.into(), - 0.into(), - caller.clone(), - )); - - let (_, _, _) = create_asset_and_pool::(&asset3, &asset4); - assert_ok!(AssetConversion::::add_liquidity( - SystemOrigin::Signed(caller.clone()).into(), - Box::new(asset3.clone()), - Box::new(asset4.clone()), - 2000.into(), - 2000.into(), - 1.into(), - 0.into(), - caller.clone(), - )); - - let path = vec![ - Box::new(asset1.clone()), - Box::new(asset2.clone()), - Box::new(asset3.clone()), - Box::new(asset4.clone()), - ]; - - let asset4_balance = T::Assets::balance(asset4.clone(), &caller); + fn swap_tokens_for_exact_tokens(n: Linear<2, { T::MaxSwapPathLength::get() }>) { + let mut max_swap_amount = T::Balance::one(); + let mut path = vec![]; + + let caller: T::AccountId = whitelisted_caller(); + create_fee_asset::(&caller); + for n in 1..n { + let (asset1, asset2) = T::BenchmarkHelper::create_pair(n - 1, n); + max_swap_amount = max_swap_amount + T::Balance::one() + T::Balance::one(); + if path.len() == 0 { + path = vec![Box::new(asset1.clone()), Box::new(asset2.clone())]; + } else { + path.push(Box::new(asset2.clone())); + } + + let (_, liquidity1, liquidity2) = create_asset_and_pool::(&caller, &asset1, &asset2); + + assert_ok!(AssetConversion::::add_liquidity( + SystemOrigin::Signed(caller.clone()).into(), + Box::new(asset1.clone()), + Box::new(asset2.clone()), + liquidity1, + liquidity2, + T::Balance::one(), + T::Balance::zero(), + caller.clone(), + )); + } + + let asset_in = *path.first().unwrap().clone(); + let asset_out = *path.last().unwrap().clone(); + assert_ok!(T::Assets::mint_into(asset_in, &caller, max_swap_amount)); + let init_caller_balance = T::Assets::balance(asset_out.clone(), &caller); #[extrinsic_call] _( SystemOrigin::Signed(caller.clone()), - path.clone(), - 100.into(), - (1000 * ed).into(), + path, + T::Balance::one(), + max_swap_amount, caller.clone(), - false, + true, ); - let new_asset4_balance = T::Assets::balance(asset4, &caller); - assert_eq!(new_asset4_balance, asset4_balance + 100.into()); + let actual_balance = T::Assets::balance(asset_out, &caller); + assert_eq!(actual_balance, init_caller_balance + T::Balance::one()); } impl_benchmark_test_suite!(AssetConversion, crate::mock::new_test_ext(), crate::mock::Test); diff --git a/substrate/frame/asset-conversion/src/lib.rs b/substrate/frame/asset-conversion/src/lib.rs index 041877139c20..7f4a8e21c4b3 100644 --- a/substrate/frame/asset-conversion/src/lib.rs +++ b/substrate/frame/asset-conversion/src/lib.rs @@ -129,7 +129,7 @@ pub mod pallet { /// Registry of assets utilized for providing liquidity to pools. type Assets: Inspect + Mutate - + AccountTouch + + AccountTouch + ContainsPair + Balanced; @@ -150,7 +150,7 @@ pub mod pallet { type PoolAssets: Inspect + Create + Mutate - + AccountTouch; + + AccountTouch; /// A % the liquidity providers will take of every swap. Represents 10ths of a percent. #[pallet::constant] @@ -605,7 +605,7 @@ pub mod pallet { /// [`AssetConversionApi::quote_price_exact_tokens_for_tokens`] runtime call can be called /// for a quote. #[pallet::call_index(3)] - #[pallet::weight(T::WeightInfo::swap_exact_tokens_for_tokens())] + #[pallet::weight(T::WeightInfo::swap_exact_tokens_for_tokens(path.len() as u32))] pub fn swap_exact_tokens_for_tokens( origin: OriginFor, path: Vec>, @@ -633,7 +633,7 @@ pub mod pallet { /// [`AssetConversionApi::quote_price_tokens_for_exact_tokens`] runtime call can be called /// for a quote. #[pallet::call_index(4)] - #[pallet::weight(T::WeightInfo::swap_tokens_for_exact_tokens())] + #[pallet::weight(T::WeightInfo::swap_tokens_for_exact_tokens(path.len() as u32))] pub fn swap_tokens_for_exact_tokens( origin: OriginFor, path: Vec>, @@ -770,7 +770,10 @@ pub mod pallet { ) -> Result, (CreditOf, DispatchError)> { let amount_in = credit_in.peek(); let inspect_path = |credit_asset| { - ensure!(path.first().map_or(false, |a| *a == credit_asset), Error::::InvalidPath); + ensure!( + path.first().map_or(false, |a| *a == credit_asset), + Error::::InvalidPath + ); ensure!(!amount_in.is_zero(), Error::::ZeroAmount); ensure!(amount_out_min.map_or(true, |a| !a.is_zero()), Error::::ZeroAmount); @@ -815,7 +818,10 @@ pub mod pallet { ) -> Result<(CreditOf, CreditOf), (CreditOf, DispatchError)> { let amount_in_max = credit_in.peek(); let inspect_path = |credit_asset| { - ensure!(path.first().map_or(false, |a| a == &credit_asset), Error::::InvalidPath); + ensure!( + path.first().map_or(false, |a| a == &credit_asset), + Error::::InvalidPath + ); ensure!(amount_in_max > Zero::zero(), Error::::ZeroAmount); ensure!(amount_out > Zero::zero(), Error::::ZeroAmount); diff --git a/substrate/frame/asset-conversion/src/mock.rs b/substrate/frame/asset-conversion/src/mock.rs index 05de1b3d90f7..377cbc314e19 100644 --- a/substrate/frame/asset-conversion/src/mock.rs +++ b/substrate/frame/asset-conversion/src/mock.rs @@ -180,7 +180,7 @@ impl Config for Test { type MaxSwapPathLength = ConstU32<4>; type MintMinLiquidity = ConstU128<100>; // 100 is good enough when the main currency has 12 decimals. #[cfg(feature = "runtime-benchmarks")] - type BenchmarkHelper = NativeOrWithIdFactory; + type BenchmarkHelper = (); } pub(crate) fn new_test_ext() -> sp_io::TestExternalities { diff --git a/substrate/frame/asset-conversion/src/weights.rs b/substrate/frame/asset-conversion/src/weights.rs index 550878ba0be9..a0e687f7a416 100644 --- a/substrate/frame/asset-conversion/src/weights.rs +++ b/substrate/frame/asset-conversion/src/weights.rs @@ -15,29 +15,27 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_asset_conversion +//! Autogenerated weights for `pallet_asset_conversion` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-07-18, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-10-30, STEPS: `5`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-gghbxkbs-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: ``, WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` +//! HOSTNAME: `cob`, CPU: `` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` // Executed Command: -// target/production/substrate +// ./target/debug/substrate-node // benchmark // pallet -// --steps=50 -// --repeat=20 +// --chain=dev +// --steps=5 +// --repeat=2 +// --pallet=pallet-asset-conversion // --extrinsic=* // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/builds/parity/mirrors/substrate/.git/.artifacts/bench.json -// --pallet=pallet_asset_conversion -// --chain=dev -// --header=./HEADER-APACHE2 -// --output=./frame/asset-conversion/src/weights.rs -// --template=./.maintain/frame-weight-template.hbs +// --output=./substrate/frame/asset-conversion/src/weights.rs +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,25 +45,25 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_asset_conversion. +/// Weight functions needed for `pallet_asset_conversion`. pub trait WeightInfo { fn create_pool() -> Weight; fn add_liquidity() -> Weight; fn remove_liquidity() -> Weight; - fn swap_exact_tokens_for_tokens() -> Weight; - fn swap_tokens_for_exact_tokens() -> Weight; + fn swap_exact_tokens_for_tokens(n: u32, ) -> Weight; + fn swap_tokens_for_exact_tokens(n: u32, ) -> Weight; } -/// Weights for pallet_asset_conversion using the Substrate node and recommended hardware. +/// Weights for `pallet_asset_conversion` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { /// Storage: `AssetConversion::Pools` (r:1 w:1) /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:2 w:2) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:1 w:1) + /// Storage: `Assets::Account` (r:2 w:2) /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `Assets::Asset` (r:1 w:1) + /// Storage: `Assets::Asset` (r:2 w:2) /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) /// Storage: `AssetConversion::NextPoolAssetId` (r:1 w:1) /// Proof: `AssetConversion::NextPoolAssetId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -75,20 +73,18 @@ impl WeightInfo for SubstrateWeight { /// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) fn create_pool() -> Weight { // Proof Size summary in bytes: - // Measured: `729` - // Estimated: `6196` - // Minimum execution time: 131_688_000 picoseconds. - Weight::from_parts(134_092_000, 6196) - .saturating_add(T::DbWeight::get().reads(8_u64)) - .saturating_add(T::DbWeight::get().writes(8_u64)) + // Measured: `1081` + // Estimated: `6360` + // Minimum execution time: 1_576_000_000 picoseconds. + Weight::from_parts(1_668_000_000, 6360) + .saturating_add(T::DbWeight::get().reads(10_u64)) + .saturating_add(T::DbWeight::get().writes(10_u64)) } /// Storage: `AssetConversion::Pools` (r:1 w:0) /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// Storage: `Assets::Asset` (r:1 w:1) + /// Storage: `Assets::Asset` (r:2 w:2) /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:2 w:2) + /// Storage: `Assets::Account` (r:4 w:4) /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) /// Storage: `PoolAssets::Asset` (r:1 w:1) /// Proof: `PoolAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) @@ -96,20 +92,18 @@ impl WeightInfo for SubstrateWeight { /// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) fn add_liquidity() -> Weight { // Proof Size summary in bytes: - // Measured: `1382` - // Estimated: `6208` - // Minimum execution time: 157_310_000 picoseconds. - Weight::from_parts(161_547_000, 6208) - .saturating_add(T::DbWeight::get().reads(8_u64)) - .saturating_add(T::DbWeight::get().writes(7_u64)) + // Measured: `1761` + // Estimated: `11426` + // Minimum execution time: 1_636_000_000 picoseconds. + Weight::from_parts(1_894_000_000, 11426) + .saturating_add(T::DbWeight::get().reads(10_u64)) + .saturating_add(T::DbWeight::get().writes(9_u64)) } /// Storage: `AssetConversion::Pools` (r:1 w:0) /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// Storage: `Assets::Asset` (r:1 w:1) + /// Storage: `Assets::Asset` (r:2 w:2) /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:2 w:2) + /// Storage: `Assets::Account` (r:4 w:4) /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) /// Storage: `PoolAssets::Asset` (r:1 w:1) /// Proof: `PoolAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) @@ -117,42 +111,46 @@ impl WeightInfo for SubstrateWeight { /// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) fn remove_liquidity() -> Weight { // Proof Size summary in bytes: - // Measured: `1371` - // Estimated: `6208` - // Minimum execution time: 142_769_000 picoseconds. - Weight::from_parts(145_139_000, 6208) - .saturating_add(T::DbWeight::get().reads(7_u64)) - .saturating_add(T::DbWeight::get().writes(6_u64)) + // Measured: `1750` + // Estimated: `11426` + // Minimum execution time: 1_507_000_000 picoseconds. + Weight::from_parts(1_524_000_000, 11426) + .saturating_add(T::DbWeight::get().reads(9_u64)) + .saturating_add(T::DbWeight::get().writes(8_u64)) } - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// Storage: `Assets::Asset` (r:3 w:3) + /// Storage: `Assets::Asset` (r:4 w:4) /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:6 w:6) + /// Storage: `Assets::Account` (r:8 w:8) /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - fn swap_exact_tokens_for_tokens() -> Weight { + /// The range of component `n` is `[2, 4]`. + fn swap_exact_tokens_for_tokens(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1738` - // Estimated: `16644` - // Minimum execution time: 213_186_000 picoseconds. - Weight::from_parts(217_471_000, 16644) - .saturating_add(T::DbWeight::get().reads(10_u64)) - .saturating_add(T::DbWeight::get().writes(10_u64)) + // Measured: `0 + n * (522 ±0)` + // Estimated: `990 + n * (5218 ±0)` + // Minimum execution time: 937_000_000 picoseconds. + Weight::from_parts(941_000_000, 990) + // Standard Error: 40_863_477 + .saturating_add(Weight::from_parts(205_862_068, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(n.into()))) + .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_parts(0, 5218).saturating_mul(n.into())) } - /// Storage: `Assets::Asset` (r:3 w:3) + /// Storage: `Assets::Asset` (r:4 w:4) /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:6 w:6) + /// Storage: `Assets::Account` (r:8 w:8) /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn swap_tokens_for_exact_tokens() -> Weight { + /// The range of component `n` is `[2, 4]`. + fn swap_tokens_for_exact_tokens(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1738` - // Estimated: `16644` - // Minimum execution time: 213_793_000 picoseconds. - Weight::from_parts(218_584_000, 16644) - .saturating_add(T::DbWeight::get().reads(10_u64)) - .saturating_add(T::DbWeight::get().writes(10_u64)) + // Measured: `0 + n * (522 ±0)` + // Estimated: `990 + n * (5218 ±0)` + // Minimum execution time: 935_000_000 picoseconds. + Weight::from_parts(947_000_000, 990) + // Standard Error: 46_904_620 + .saturating_add(Weight::from_parts(218_275_862, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(n.into()))) + .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_parts(0, 5218).saturating_mul(n.into())) } } @@ -162,9 +160,9 @@ impl WeightInfo for () { /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:2 w:2) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:1 w:1) + /// Storage: `Assets::Account` (r:2 w:2) /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `Assets::Asset` (r:1 w:1) + /// Storage: `Assets::Asset` (r:2 w:2) /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) /// Storage: `AssetConversion::NextPoolAssetId` (r:1 w:1) /// Proof: `AssetConversion::NextPoolAssetId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -174,20 +172,18 @@ impl WeightInfo for () { /// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) fn create_pool() -> Weight { // Proof Size summary in bytes: - // Measured: `729` - // Estimated: `6196` - // Minimum execution time: 131_688_000 picoseconds. - Weight::from_parts(134_092_000, 6196) - .saturating_add(RocksDbWeight::get().reads(8_u64)) - .saturating_add(RocksDbWeight::get().writes(8_u64)) + // Measured: `1081` + // Estimated: `6360` + // Minimum execution time: 1_576_000_000 picoseconds. + Weight::from_parts(1_668_000_000, 6360) + .saturating_add(RocksDbWeight::get().reads(10_u64)) + .saturating_add(RocksDbWeight::get().writes(10_u64)) } /// Storage: `AssetConversion::Pools` (r:1 w:0) /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// Storage: `Assets::Asset` (r:1 w:1) + /// Storage: `Assets::Asset` (r:2 w:2) /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:2 w:2) + /// Storage: `Assets::Account` (r:4 w:4) /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) /// Storage: `PoolAssets::Asset` (r:1 w:1) /// Proof: `PoolAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) @@ -195,20 +191,18 @@ impl WeightInfo for () { /// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) fn add_liquidity() -> Weight { // Proof Size summary in bytes: - // Measured: `1382` - // Estimated: `6208` - // Minimum execution time: 157_310_000 picoseconds. - Weight::from_parts(161_547_000, 6208) - .saturating_add(RocksDbWeight::get().reads(8_u64)) - .saturating_add(RocksDbWeight::get().writes(7_u64)) + // Measured: `1761` + // Estimated: `11426` + // Minimum execution time: 1_636_000_000 picoseconds. + Weight::from_parts(1_894_000_000, 11426) + .saturating_add(RocksDbWeight::get().reads(10_u64)) + .saturating_add(RocksDbWeight::get().writes(9_u64)) } /// Storage: `AssetConversion::Pools` (r:1 w:0) /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// Storage: `Assets::Asset` (r:1 w:1) + /// Storage: `Assets::Asset` (r:2 w:2) /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:2 w:2) + /// Storage: `Assets::Account` (r:4 w:4) /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) /// Storage: `PoolAssets::Asset` (r:1 w:1) /// Proof: `PoolAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) @@ -216,41 +210,45 @@ impl WeightInfo for () { /// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) fn remove_liquidity() -> Weight { // Proof Size summary in bytes: - // Measured: `1371` - // Estimated: `6208` - // Minimum execution time: 142_769_000 picoseconds. - Weight::from_parts(145_139_000, 6208) - .saturating_add(RocksDbWeight::get().reads(7_u64)) - .saturating_add(RocksDbWeight::get().writes(6_u64)) + // Measured: `1750` + // Estimated: `11426` + // Minimum execution time: 1_507_000_000 picoseconds. + Weight::from_parts(1_524_000_000, 11426) + .saturating_add(RocksDbWeight::get().reads(9_u64)) + .saturating_add(RocksDbWeight::get().writes(8_u64)) } - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// Storage: `Assets::Asset` (r:3 w:3) + /// Storage: `Assets::Asset` (r:4 w:4) /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:6 w:6) + /// Storage: `Assets::Account` (r:8 w:8) /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - fn swap_exact_tokens_for_tokens() -> Weight { + /// The range of component `n` is `[2, 4]`. + fn swap_exact_tokens_for_tokens(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1738` - // Estimated: `16644` - // Minimum execution time: 213_186_000 picoseconds. - Weight::from_parts(217_471_000, 16644) - .saturating_add(RocksDbWeight::get().reads(10_u64)) - .saturating_add(RocksDbWeight::get().writes(10_u64)) + // Measured: `0 + n * (522 ±0)` + // Estimated: `990 + n * (5218 ±0)` + // Minimum execution time: 937_000_000 picoseconds. + Weight::from_parts(941_000_000, 990) + // Standard Error: 40_863_477 + .saturating_add(Weight::from_parts(205_862_068, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(n.into()))) + .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_parts(0, 5218).saturating_mul(n.into())) } - /// Storage: `Assets::Asset` (r:3 w:3) + /// Storage: `Assets::Asset` (r:4 w:4) /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:6 w:6) + /// Storage: `Assets::Account` (r:8 w:8) /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn swap_tokens_for_exact_tokens() -> Weight { + /// The range of component `n` is `[2, 4]`. + fn swap_tokens_for_exact_tokens(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1738` - // Estimated: `16644` - // Minimum execution time: 213_793_000 picoseconds. - Weight::from_parts(218_584_000, 16644) - .saturating_add(RocksDbWeight::get().reads(10_u64)) - .saturating_add(RocksDbWeight::get().writes(10_u64)) + // Measured: `0 + n * (522 ±0)` + // Estimated: `990 + n * (5218 ±0)` + // Minimum execution time: 935_000_000 picoseconds. + Weight::from_parts(947_000_000, 990) + // Standard Error: 46_904_620 + .saturating_add(Weight::from_parts(218_275_862, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(n.into()))) + .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_parts(0, 5218).saturating_mul(n.into())) } } From 1a9c5f3cb9d424851b9032b42061bd4a37e53708 Mon Sep 17 00:00:00 2001 From: muharem Date: Mon, 30 Oct 2023 15:31:55 +0100 Subject: [PATCH 66/98] weights --- .../src/weights/pallet_asset_conversion.rs | 182 ++++------------- .../src/weights/pallet_asset_conversion.rs | 182 ++++------------- .../src/weights/pallet_asset_conversion.rs | 184 ++++-------------- 3 files changed, 106 insertions(+), 442 deletions(-) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/weights/pallet_asset_conversion.rs b/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/weights/pallet_asset_conversion.rs index 54a458de51ae..ac58bc22e8ab 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/weights/pallet_asset_conversion.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/weights/pallet_asset_conversion.rs @@ -20,7 +20,7 @@ //! DATE: 2023-10-30, STEPS: `20`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `cob`, CPU: `` -//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-kusama-dev")`, DB CACHE: `1024` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-kusama-dev")`, DB CACHE: 1024 // Executed Command: // ./target/debug/polkadot-parachain @@ -34,28 +34,18 @@ // --wasm-execution=compiled // --heap-pages=4096 // --output=./cumulus/parachains/runtimes/assets/asset-hub-kusama/src/weights/pallet_asset_conversion.rs -// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] #![allow(unused_imports)] #![allow(missing_docs)] -use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use frame_support::{traits::Get, weights::Weight}; use core::marker::PhantomData; -/// Weight functions needed for `pallet_asset_conversion`. -pub trait WeightInfo { - fn create_pool() -> Weight; - fn add_liquidity() -> Weight; - fn remove_liquidity() -> Weight; - fn swap_exact_tokens_for_tokens(n: u32, ) -> Weight; - fn swap_tokens_for_exact_tokens(n: u32, ) -> Weight; -} - -/// Weights for `pallet_asset_conversion` using the Substrate node and recommended hardware. -pub struct SubstrateWeight(PhantomData); -impl WeightInfo for SubstrateWeight { +/// Weight functions for `pallet_asset_conversion`. +pub struct WeightInfo(PhantomData); +impl pallet_asset_conversion::WeightInfo for WeightInfo { /// Storage: `AssetConversion::Pools` (r:1 w:1) /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:1) @@ -74,10 +64,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `408` // Estimated: `4689` - // Minimum execution time: 909_000_000 picoseconds. - Weight::from_parts(944_000_000, 4689) - .saturating_add(T::DbWeight::get().reads(7_u64)) - .saturating_add(T::DbWeight::get().writes(7_u64)) + // Minimum execution time: 905_000_000 picoseconds. + Weight::from_parts(928_000_000, 0) + .saturating_add(Weight::from_parts(0, 4689)) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(7)) } /// Storage: `AssetConversion::Pools` (r:1 w:0) /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`) @@ -95,10 +86,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1117` // Estimated: `7404` - // Minimum execution time: 1_629_000_000 picoseconds. - Weight::from_parts(1_766_000_000, 7404) - .saturating_add(T::DbWeight::get().reads(8_u64)) - .saturating_add(T::DbWeight::get().writes(7_u64)) + // Minimum execution time: 1_628_000_000 picoseconds. + Weight::from_parts(1_799_000_000, 0) + .saturating_add(Weight::from_parts(0, 7404)) + .saturating_add(T::DbWeight::get().reads(8)) + .saturating_add(T::DbWeight::get().writes(7)) } /// Storage: `AssetConversion::Pools` (r:1 w:0) /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`) @@ -116,10 +108,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1106` // Estimated: `7404` - // Minimum execution time: 1_431_000_000 picoseconds. - Weight::from_parts(1_517_000_000, 7404) - .saturating_add(T::DbWeight::get().reads(7_u64)) - .saturating_add(T::DbWeight::get().writes(6_u64)) + // Minimum execution time: 1_567_000_000 picoseconds. + Weight::from_parts(1_616_000_000, 0) + .saturating_add(Weight::from_parts(0, 7404)) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(6)) } /// Storage: `ForeignAssets::Asset` (r:2 w:2) /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) @@ -131,13 +124,14 @@ impl WeightInfo for SubstrateWeight { fn swap_exact_tokens_for_tokens(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + n * (557 ±0)` - // Estimated: `7404 + n * (393 ±180)` - // Minimum execution time: 922_000_000 picoseconds. - Weight::from_parts(954_000_000, 7404) - // Standard Error: 15_697_849 - .saturating_add(Weight::from_parts(39_602_040, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(4_u64)) + // Estimated: `7404 + n * (393 ±92)` + // Minimum execution time: 940_000_000 picoseconds. + Weight::from_parts(959_000_000, 0) + .saturating_add(Weight::from_parts(0, 7404)) + // Standard Error: 17_639_773 + .saturating_add(Weight::from_parts(45_724_489, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(4)) .saturating_add(Weight::from_parts(0, 393).saturating_mul(n.into())) } /// Storage: `System::Account` (r:2 w:2) @@ -148,122 +142,16 @@ impl WeightInfo for SubstrateWeight { /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) /// The range of component `n` is `[2, 3]`. fn swap_tokens_for_exact_tokens(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `0 + n * (557 ±0)` - // Estimated: `7404 + n * (393 ±73)` - // Minimum execution time: 929_000_000 picoseconds. - Weight::from_parts(947_000_000, 7404) - // Standard Error: 15_496_079 - .saturating_add(Weight::from_parts(41_295_918, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(4_u64)) - .saturating_add(Weight::from_parts(0, 393).saturating_mul(n.into())) - } -} - -// For backwards compatibility and tests. -impl WeightInfo for () { - /// Storage: `AssetConversion::Pools` (r:1 w:1) - /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// Storage: `ForeignAssets::Account` (r:1 w:1) - /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) - /// Storage: `ForeignAssets::Asset` (r:1 w:1) - /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) - /// Storage: `AssetConversion::NextPoolAssetId` (r:1 w:1) - /// Proof: `AssetConversion::NextPoolAssetId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: `PoolAssets::Asset` (r:1 w:1) - /// Proof: `PoolAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `PoolAssets::Account` (r:1 w:1) - /// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - fn create_pool() -> Weight { - // Proof Size summary in bytes: - // Measured: `408` - // Estimated: `4689` - // Minimum execution time: 909_000_000 picoseconds. - Weight::from_parts(944_000_000, 4689) - .saturating_add(RocksDbWeight::get().reads(7_u64)) - .saturating_add(RocksDbWeight::get().writes(7_u64)) - } - /// Storage: `AssetConversion::Pools` (r:1 w:0) - /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`) - /// Storage: `ForeignAssets::Asset` (r:1 w:1) - /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) - /// Storage: `ForeignAssets::Account` (r:2 w:2) - /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// Storage: `PoolAssets::Asset` (r:1 w:1) - /// Proof: `PoolAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `PoolAssets::Account` (r:2 w:2) - /// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - fn add_liquidity() -> Weight { - // Proof Size summary in bytes: - // Measured: `1117` - // Estimated: `7404` - // Minimum execution time: 1_629_000_000 picoseconds. - Weight::from_parts(1_766_000_000, 7404) - .saturating_add(RocksDbWeight::get().reads(8_u64)) - .saturating_add(RocksDbWeight::get().writes(7_u64)) - } - /// Storage: `AssetConversion::Pools` (r:1 w:0) - /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`) - /// Storage: `ForeignAssets::Asset` (r:1 w:1) - /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) - /// Storage: `ForeignAssets::Account` (r:2 w:2) - /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// Storage: `PoolAssets::Asset` (r:1 w:1) - /// Proof: `PoolAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `PoolAssets::Account` (r:1 w:1) - /// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - fn remove_liquidity() -> Weight { - // Proof Size summary in bytes: - // Measured: `1106` - // Estimated: `7404` - // Minimum execution time: 1_431_000_000 picoseconds. - Weight::from_parts(1_517_000_000, 7404) - .saturating_add(RocksDbWeight::get().reads(7_u64)) - .saturating_add(RocksDbWeight::get().writes(6_u64)) - } - /// Storage: `ForeignAssets::Asset` (r:2 w:2) - /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) - /// Storage: `ForeignAssets::Account` (r:4 w:4) - /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:2 w:2) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// The range of component `n` is `[2, 3]`. - fn swap_exact_tokens_for_tokens(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + n * (557 ±0)` // Estimated: `7404 + n * (393 ±180)` - // Minimum execution time: 922_000_000 picoseconds. - Weight::from_parts(954_000_000, 7404) - // Standard Error: 15_697_849 - .saturating_add(Weight::from_parts(39_602_040, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(4_u64)) - .saturating_add(Weight::from_parts(0, 393).saturating_mul(n.into())) - } - /// Storage: `System::Account` (r:2 w:2) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// Storage: `ForeignAssets::Asset` (r:2 w:2) - /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) - /// Storage: `ForeignAssets::Account` (r:4 w:4) - /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) - /// The range of component `n` is `[2, 3]`. - fn swap_tokens_for_exact_tokens(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `0 + n * (557 ±0)` - // Estimated: `7404 + n * (393 ±73)` - // Minimum execution time: 929_000_000 picoseconds. - Weight::from_parts(947_000_000, 7404) - // Standard Error: 15_496_079 - .saturating_add(Weight::from_parts(41_295_918, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(4_u64)) + // Minimum execution time: 928_000_000 picoseconds. + Weight::from_parts(956_000_000, 0) + .saturating_add(Weight::from_parts(0, 7404)) + // Standard Error: 16_273_167 + .saturating_add(Weight::from_parts(42_867_346, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(4)) .saturating_add(Weight::from_parts(0, 393).saturating_mul(n.into())) } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_asset_conversion.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_asset_conversion.rs index a416c6947ce2..0486932d1d6e 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_asset_conversion.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_asset_conversion.rs @@ -20,7 +20,7 @@ //! DATE: 2023-10-30, STEPS: `20`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `cob`, CPU: `` -//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-rococo-dev")`, DB CACHE: `1024` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-rococo-dev")`, DB CACHE: 1024 // Executed Command: // ./target/debug/polkadot-parachain @@ -34,28 +34,18 @@ // --wasm-execution=compiled // --heap-pages=4096 // --output=./cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_asset_conversion.rs -// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] #![allow(unused_imports)] #![allow(missing_docs)] -use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use frame_support::{traits::Get, weights::Weight}; use core::marker::PhantomData; -/// Weight functions needed for `pallet_asset_conversion`. -pub trait WeightInfo { - fn create_pool() -> Weight; - fn add_liquidity() -> Weight; - fn remove_liquidity() -> Weight; - fn swap_exact_tokens_for_tokens(n: u32, ) -> Weight; - fn swap_tokens_for_exact_tokens(n: u32, ) -> Weight; -} - -/// Weights for `pallet_asset_conversion` using the Substrate node and recommended hardware. -pub struct SubstrateWeight(PhantomData); -impl WeightInfo for SubstrateWeight { +/// Weight functions for `pallet_asset_conversion`. +pub struct WeightInfo(PhantomData); +impl pallet_asset_conversion::WeightInfo for WeightInfo { /// Storage: `AssetConversion::Pools` (r:1 w:1) /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:1) @@ -74,10 +64,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `408` // Estimated: `4689` - // Minimum execution time: 917_000_000 picoseconds. - Weight::from_parts(989_000_000, 4689) - .saturating_add(T::DbWeight::get().reads(7_u64)) - .saturating_add(T::DbWeight::get().writes(7_u64)) + // Minimum execution time: 906_000_000 picoseconds. + Weight::from_parts(945_000_000, 0) + .saturating_add(Weight::from_parts(0, 4689)) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(7)) } /// Storage: `AssetConversion::Pools` (r:1 w:0) /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`) @@ -95,10 +86,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1117` // Estimated: `7404` - // Minimum execution time: 1_622_000_000 picoseconds. - Weight::from_parts(1_639_000_000, 7404) - .saturating_add(T::DbWeight::get().reads(8_u64)) - .saturating_add(T::DbWeight::get().writes(7_u64)) + // Minimum execution time: 1_609_000_000 picoseconds. + Weight::from_parts(1_631_000_000, 0) + .saturating_add(Weight::from_parts(0, 7404)) + .saturating_add(T::DbWeight::get().reads(8)) + .saturating_add(T::DbWeight::get().writes(7)) } /// Storage: `AssetConversion::Pools` (r:1 w:0) /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`) @@ -116,10 +108,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1106` // Estimated: `7404` - // Minimum execution time: 1_496_000_000 picoseconds. - Weight::from_parts(1_497_000_000, 7404) - .saturating_add(T::DbWeight::get().reads(7_u64)) - .saturating_add(T::DbWeight::get().writes(6_u64)) + // Minimum execution time: 1_480_000_000 picoseconds. + Weight::from_parts(1_506_000_000, 0) + .saturating_add(Weight::from_parts(0, 7404)) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(6)) } /// Storage: `ForeignAssets::Asset` (r:2 w:2) /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) @@ -129,122 +122,16 @@ impl WeightInfo for SubstrateWeight { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `n` is `[2, 3]`. fn swap_exact_tokens_for_tokens(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `0 + n * (557 ±0)` - // Estimated: `7404 + n * (393 ±180)` - // Minimum execution time: 925_000_000 picoseconds. - Weight::from_parts(947_000_000, 7404) - // Standard Error: 15_819_119 - .saturating_add(Weight::from_parts(38_448_979, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(4_u64)) - .saturating_add(Weight::from_parts(0, 393).saturating_mul(n.into())) - } - /// Storage: `System::Account` (r:2 w:2) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// Storage: `ForeignAssets::Asset` (r:2 w:2) - /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) - /// Storage: `ForeignAssets::Account` (r:4 w:4) - /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) - /// The range of component `n` is `[2, 3]`. - fn swap_tokens_for_exact_tokens(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + n * (557 ±0)` // Estimated: `7404 + n * (393 ±73)` - // Minimum execution time: 913_000_000 picoseconds. - Weight::from_parts(945_000_000, 7404) - // Standard Error: 17_010_580 - .saturating_add(Weight::from_parts(43_326_530, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(4_u64)) - .saturating_add(Weight::from_parts(0, 393).saturating_mul(n.into())) - } -} - -// For backwards compatibility and tests. -impl WeightInfo for () { - /// Storage: `AssetConversion::Pools` (r:1 w:1) - /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// Storage: `ForeignAssets::Account` (r:1 w:1) - /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) - /// Storage: `ForeignAssets::Asset` (r:1 w:1) - /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) - /// Storage: `AssetConversion::NextPoolAssetId` (r:1 w:1) - /// Proof: `AssetConversion::NextPoolAssetId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: `PoolAssets::Asset` (r:1 w:1) - /// Proof: `PoolAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `PoolAssets::Account` (r:1 w:1) - /// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - fn create_pool() -> Weight { - // Proof Size summary in bytes: - // Measured: `408` - // Estimated: `4689` - // Minimum execution time: 917_000_000 picoseconds. - Weight::from_parts(989_000_000, 4689) - .saturating_add(RocksDbWeight::get().reads(7_u64)) - .saturating_add(RocksDbWeight::get().writes(7_u64)) - } - /// Storage: `AssetConversion::Pools` (r:1 w:0) - /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`) - /// Storage: `ForeignAssets::Asset` (r:1 w:1) - /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) - /// Storage: `ForeignAssets::Account` (r:2 w:2) - /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// Storage: `PoolAssets::Asset` (r:1 w:1) - /// Proof: `PoolAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `PoolAssets::Account` (r:2 w:2) - /// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - fn add_liquidity() -> Weight { - // Proof Size summary in bytes: - // Measured: `1117` - // Estimated: `7404` - // Minimum execution time: 1_622_000_000 picoseconds. - Weight::from_parts(1_639_000_000, 7404) - .saturating_add(RocksDbWeight::get().reads(8_u64)) - .saturating_add(RocksDbWeight::get().writes(7_u64)) - } - /// Storage: `AssetConversion::Pools` (r:1 w:0) - /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`) - /// Storage: `ForeignAssets::Asset` (r:1 w:1) - /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) - /// Storage: `ForeignAssets::Account` (r:2 w:2) - /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// Storage: `PoolAssets::Asset` (r:1 w:1) - /// Proof: `PoolAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `PoolAssets::Account` (r:1 w:1) - /// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - fn remove_liquidity() -> Weight { - // Proof Size summary in bytes: - // Measured: `1106` - // Estimated: `7404` - // Minimum execution time: 1_496_000_000 picoseconds. - Weight::from_parts(1_497_000_000, 7404) - .saturating_add(RocksDbWeight::get().reads(7_u64)) - .saturating_add(RocksDbWeight::get().writes(6_u64)) - } - /// Storage: `ForeignAssets::Asset` (r:2 w:2) - /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) - /// Storage: `ForeignAssets::Account` (r:4 w:4) - /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:2 w:2) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// The range of component `n` is `[2, 3]`. - fn swap_exact_tokens_for_tokens(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `0 + n * (557 ±0)` - // Estimated: `7404 + n * (393 ±180)` - // Minimum execution time: 925_000_000 picoseconds. - Weight::from_parts(947_000_000, 7404) - // Standard Error: 15_819_119 - .saturating_add(Weight::from_parts(38_448_979, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(4_u64)) + // Minimum execution time: 933_000_000 picoseconds. + Weight::from_parts(950_000_000, 0) + .saturating_add(Weight::from_parts(0, 7404)) + // Standard Error: 18_792_550 + .saturating_add(Weight::from_parts(46_683_673, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(4)) .saturating_add(Weight::from_parts(0, 393).saturating_mul(n.into())) } /// Storage: `System::Account` (r:2 w:2) @@ -257,13 +144,14 @@ impl WeightInfo for () { fn swap_tokens_for_exact_tokens(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + n * (557 ±0)` - // Estimated: `7404 + n * (393 ±73)` - // Minimum execution time: 913_000_000 picoseconds. - Weight::from_parts(945_000_000, 7404) - // Standard Error: 17_010_580 - .saturating_add(Weight::from_parts(43_326_530, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(4_u64)) + // Estimated: `7404 + n * (393 ±180)` + // Minimum execution time: 936_000_000 picoseconds. + Weight::from_parts(954_000_000, 0) + .saturating_add(Weight::from_parts(0, 7404)) + // Standard Error: 15_942_881 + .saturating_add(Weight::from_parts(39_755_102, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(4)) .saturating_add(Weight::from_parts(0, 393).saturating_mul(n.into())) } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_asset_conversion.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_asset_conversion.rs index 87c22abfcf57..344d990cad23 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_asset_conversion.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_asset_conversion.rs @@ -20,7 +20,7 @@ //! DATE: 2023-10-30, STEPS: `20`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `cob`, CPU: `` -//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-westend-dev")`, DB CACHE: `1024` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-westend-dev")`, DB CACHE: 1024 // Executed Command: // ./target/debug/polkadot-parachain @@ -34,135 +34,18 @@ // --wasm-execution=compiled // --heap-pages=4096 // --output=./cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_asset_conversion.rs -// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] #![allow(unused_imports)] #![allow(missing_docs)] -use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use frame_support::{traits::Get, weights::Weight}; use core::marker::PhantomData; -/// Weight functions needed for `pallet_asset_conversion`. -pub trait WeightInfo { - fn create_pool() -> Weight; - fn add_liquidity() -> Weight; - fn remove_liquidity() -> Weight; - fn swap_exact_tokens_for_tokens(n: u32, ) -> Weight; - fn swap_tokens_for_exact_tokens(n: u32, ) -> Weight; -} - -/// Weights for `pallet_asset_conversion` using the Substrate node and recommended hardware. -pub struct SubstrateWeight(PhantomData); -impl WeightInfo for SubstrateWeight { - /// Storage: `AssetConversion::Pools` (r:1 w:1) - /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// Storage: `ForeignAssets::Account` (r:1 w:1) - /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) - /// Storage: `ForeignAssets::Asset` (r:1 w:1) - /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) - /// Storage: `AssetConversion::NextPoolAssetId` (r:1 w:1) - /// Proof: `AssetConversion::NextPoolAssetId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: `PoolAssets::Asset` (r:1 w:1) - /// Proof: `PoolAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `PoolAssets::Account` (r:1 w:1) - /// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - fn create_pool() -> Weight { - // Proof Size summary in bytes: - // Measured: `408` - // Estimated: `4689` - // Minimum execution time: 904_000_000 picoseconds. - Weight::from_parts(938_000_000, 4689) - .saturating_add(T::DbWeight::get().reads(7_u64)) - .saturating_add(T::DbWeight::get().writes(7_u64)) - } - /// Storage: `AssetConversion::Pools` (r:1 w:0) - /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`) - /// Storage: `ForeignAssets::Asset` (r:1 w:1) - /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) - /// Storage: `ForeignAssets::Account` (r:2 w:2) - /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// Storage: `PoolAssets::Asset` (r:1 w:1) - /// Proof: `PoolAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `PoolAssets::Account` (r:2 w:2) - /// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - fn add_liquidity() -> Weight { - // Proof Size summary in bytes: - // Measured: `1117` - // Estimated: `7404` - // Minimum execution time: 1_613_000_000 picoseconds. - Weight::from_parts(1_923_000_000, 7404) - .saturating_add(T::DbWeight::get().reads(8_u64)) - .saturating_add(T::DbWeight::get().writes(7_u64)) - } - /// Storage: `AssetConversion::Pools` (r:1 w:0) - /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`) - /// Storage: `ForeignAssets::Asset` (r:1 w:1) - /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) - /// Storage: `ForeignAssets::Account` (r:2 w:2) - /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// Storage: `PoolAssets::Asset` (r:1 w:1) - /// Proof: `PoolAssets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `PoolAssets::Account` (r:1 w:1) - /// Proof: `PoolAssets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - fn remove_liquidity() -> Weight { - // Proof Size summary in bytes: - // Measured: `1106` - // Estimated: `7404` - // Minimum execution time: 1_479_000_000 picoseconds. - Weight::from_parts(1_559_000_000, 7404) - .saturating_add(T::DbWeight::get().reads(7_u64)) - .saturating_add(T::DbWeight::get().writes(6_u64)) - } - /// Storage: `ForeignAssets::Asset` (r:2 w:2) - /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) - /// Storage: `ForeignAssets::Account` (r:4 w:4) - /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:2 w:2) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// The range of component `n` is `[2, 3]`. - fn swap_exact_tokens_for_tokens(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `0 + n * (557 ±0)` - // Estimated: `7404 + n * (393 ±180)` - // Minimum execution time: 920_000_000 picoseconds. - Weight::from_parts(950_000_000, 7404) - // Standard Error: 18_279_661 - .saturating_add(Weight::from_parts(47_071_428, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(4_u64)) - .saturating_add(Weight::from_parts(0, 393).saturating_mul(n.into())) - } - /// Storage: `System::Account` (r:2 w:2) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// Storage: `ForeignAssets::Asset` (r:2 w:2) - /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) - /// Storage: `ForeignAssets::Account` (r:4 w:4) - /// Proof: `ForeignAssets::Account` (`max_values`: None, `max_size`: Some(732), added: 3207, mode: `MaxEncodedLen`) - /// The range of component `n` is `[2, 3]`. - fn swap_tokens_for_exact_tokens(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `0 + n * (557 ±0)` - // Estimated: `7404 + n * (393 ±180)` - // Minimum execution time: 932_000_000 picoseconds. - Weight::from_parts(944_000_000, 7404) - // Standard Error: 14_264_382 - .saturating_add(Weight::from_parts(41_938_775, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(4_u64)) - .saturating_add(Weight::from_parts(0, 393).saturating_mul(n.into())) - } -} - -// For backwards compatibility and tests. -impl WeightInfo for () { +/// Weight functions for `pallet_asset_conversion`. +pub struct WeightInfo(PhantomData); +impl pallet_asset_conversion::WeightInfo for WeightInfo { /// Storage: `AssetConversion::Pools` (r:1 w:1) /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:1) @@ -181,10 +64,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `408` // Estimated: `4689` - // Minimum execution time: 904_000_000 picoseconds. - Weight::from_parts(938_000_000, 4689) - .saturating_add(RocksDbWeight::get().reads(7_u64)) - .saturating_add(RocksDbWeight::get().writes(7_u64)) + // Minimum execution time: 922_000_000 picoseconds. + Weight::from_parts(1_102_000_000, 0) + .saturating_add(Weight::from_parts(0, 4689)) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(7)) } /// Storage: `AssetConversion::Pools` (r:1 w:0) /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`) @@ -202,10 +86,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1117` // Estimated: `7404` - // Minimum execution time: 1_613_000_000 picoseconds. - Weight::from_parts(1_923_000_000, 7404) - .saturating_add(RocksDbWeight::get().reads(8_u64)) - .saturating_add(RocksDbWeight::get().writes(7_u64)) + // Minimum execution time: 1_597_000_000 picoseconds. + Weight::from_parts(1_655_000_000, 0) + .saturating_add(Weight::from_parts(0, 7404)) + .saturating_add(T::DbWeight::get().reads(8)) + .saturating_add(T::DbWeight::get().writes(7)) } /// Storage: `AssetConversion::Pools` (r:1 w:0) /// Proof: `AssetConversion::Pools` (`max_values`: None, `max_size`: Some(1224), added: 3699, mode: `MaxEncodedLen`) @@ -223,10 +108,11 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1106` // Estimated: `7404` - // Minimum execution time: 1_479_000_000 picoseconds. - Weight::from_parts(1_559_000_000, 7404) - .saturating_add(RocksDbWeight::get().reads(7_u64)) - .saturating_add(RocksDbWeight::get().writes(6_u64)) + // Minimum execution time: 1_500_000_000 picoseconds. + Weight::from_parts(1_633_000_000, 0) + .saturating_add(Weight::from_parts(0, 7404)) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(6)) } /// Storage: `ForeignAssets::Asset` (r:2 w:2) /// Proof: `ForeignAssets::Asset` (`max_values`: None, `max_size`: Some(808), added: 3283, mode: `MaxEncodedLen`) @@ -238,13 +124,14 @@ impl WeightInfo for () { fn swap_exact_tokens_for_tokens(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + n * (557 ±0)` - // Estimated: `7404 + n * (393 ±180)` - // Minimum execution time: 920_000_000 picoseconds. - Weight::from_parts(950_000_000, 7404) - // Standard Error: 18_279_661 - .saturating_add(Weight::from_parts(47_071_428, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(4_u64)) + // Estimated: `7404 + n * (393 ±92)` + // Minimum execution time: 930_000_000 picoseconds. + Weight::from_parts(960_000_000, 0) + .saturating_add(Weight::from_parts(0, 7404)) + // Standard Error: 17_993_720 + .saturating_add(Weight::from_parts(41_959_183, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(4)) .saturating_add(Weight::from_parts(0, 393).saturating_mul(n.into())) } /// Storage: `System::Account` (r:2 w:2) @@ -257,13 +144,14 @@ impl WeightInfo for () { fn swap_tokens_for_exact_tokens(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + n * (557 ±0)` - // Estimated: `7404 + n * (393 ±180)` - // Minimum execution time: 932_000_000 picoseconds. - Weight::from_parts(944_000_000, 7404) - // Standard Error: 14_264_382 - .saturating_add(Weight::from_parts(41_938_775, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(4_u64)) + // Estimated: `7404 + n * (393 ±92)` + // Minimum execution time: 940_000_000 picoseconds. + Weight::from_parts(956_000_000, 0) + .saturating_add(Weight::from_parts(0, 7404)) + // Standard Error: 15_746_647 + .saturating_add(Weight::from_parts(39_193_877, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(4)) .saturating_add(Weight::from_parts(0, 393).saturating_mul(n.into())) } } From e0f1d0aa2c9c825fd5665d8eaf57247b48dad08a Mon Sep 17 00:00:00 2001 From: muharem Date: Mon, 30 Oct 2023 15:44:04 +0100 Subject: [PATCH 67/98] remove inc_providers on pool creation --- substrate/frame/asset-conversion/src/lib.rs | 1 - substrate/frame/asset-conversion/src/tests.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/substrate/frame/asset-conversion/src/lib.rs b/substrate/frame/asset-conversion/src/lib.rs index 7f4a8e21c4b3..6a00e59ac452 100644 --- a/substrate/frame/asset-conversion/src/lib.rs +++ b/substrate/frame/asset-conversion/src/lib.rs @@ -371,7 +371,6 @@ pub mod pallet { let pool_account = T::PoolLocator::address(&pool_id).map_err(|_| Error::::InvalidAssetPair)?; - frame_system::Pallet::::inc_providers(&pool_account); // pay the setup fee let fee = diff --git a/substrate/frame/asset-conversion/src/tests.rs b/substrate/frame/asset-conversion/src/tests.rs index 32423435cbb7..95f8c7412f69 100644 --- a/substrate/frame/asset-conversion/src/tests.rs +++ b/substrate/frame/asset-conversion/src/tests.rs @@ -507,7 +507,6 @@ fn add_tiny_liquidity_directly_to_pool_address() { // check the same but for token_3.clone() (non-native token) let pallet_account = ::PoolLocator::address(&(token_1.clone(), token_3.clone())).unwrap(); - assert_ok!(Assets::mint(RuntimeOrigin::signed(user), 2, pallet_account, 1)); assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), Box::new(token_1.clone()), From 78332d74eb1ac1a7cbc9dd0d54e1a8ed51bb710c Mon Sep 17 00:00:00 2001 From: muharem Date: Mon, 30 Oct 2023 17:04:35 +0100 Subject: [PATCH 68/98] remove unused var --- substrate/frame/asset-conversion/src/tests.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/substrate/frame/asset-conversion/src/tests.rs b/substrate/frame/asset-conversion/src/tests.rs index 95f8c7412f69..e69d14fcb3c4 100644 --- a/substrate/frame/asset-conversion/src/tests.rs +++ b/substrate/frame/asset-conversion/src/tests.rs @@ -505,8 +505,6 @@ fn add_tiny_liquidity_directly_to_pool_address() { )); // check the same but for token_3.clone() (non-native token) - let pallet_account = - ::PoolLocator::address(&(token_1.clone(), token_3.clone())).unwrap(); assert_ok!(AssetConversion::add_liquidity( RuntimeOrigin::signed(user), Box::new(token_1.clone()), From 3f59637721267c92bf54c59a84e677b10b107142 Mon Sep 17 00:00:00 2001 From: muharem Date: Tue, 31 Oct 2023 13:48:39 +0100 Subject: [PATCH 69/98] account touch --- substrate/frame/asset-conversion/src/lib.rs | 6 ++--- substrate/frame/assets/src/lib.rs | 12 ++++++++-- substrate/frame/balances/src/impl_fungible.rs | 24 +++++++++++++++---- substrate/frame/support/src/traits/misc.rs | 16 +++++++++---- .../src/traits/tokens/fungible/union_of.rs | 15 ++++++++---- .../src/traits/tokens/fungibles/union_of.rs | 9 ++++++- 6 files changed, 64 insertions(+), 18 deletions(-) diff --git a/substrate/frame/asset-conversion/src/lib.rs b/substrate/frame/asset-conversion/src/lib.rs index 6a00e59ac452..7f90ecce6f8d 100644 --- a/substrate/frame/asset-conversion/src/lib.rs +++ b/substrate/frame/asset-conversion/src/lib.rs @@ -378,11 +378,11 @@ pub mod pallet { T::PoolSetupFeeTarget::on_unbalanced(fee); if !T::Assets::contains(&asset1, &pool_account) { - T::Assets::touch(*asset1, pool_account.clone(), sender.clone())? + T::Assets::touch(*asset1, &pool_account, &sender)? }; if !T::Assets::contains(&asset2, &pool_account) { - T::Assets::touch(*asset2, pool_account.clone(), sender.clone())? + T::Assets::touch(*asset2, &pool_account, &sender)? }; let lp_token = NextPoolAssetId::::get() @@ -392,7 +392,7 @@ pub mod pallet { NextPoolAssetId::::set(Some(next_lp_token_id)); T::PoolAssets::create(lp_token.clone(), pool_account.clone(), false, 1u32.into())?; - T::PoolAssets::touch(lp_token.clone(), pool_account.clone(), sender.clone())?; + T::PoolAssets::touch(lp_token.clone(), &pool_account, &sender)?; let pool_info = PoolInfo { lp_token: lp_token.clone() }; Pools::::insert(pool_id.clone(), pool_info); diff --git a/substrate/frame/assets/src/lib.rs b/substrate/frame/assets/src/lib.rs index 79e4fe300187..62d2b2790882 100644 --- a/substrate/frame/assets/src/lib.rs +++ b/substrate/frame/assets/src/lib.rs @@ -1648,8 +1648,16 @@ pub mod pallet { T::AssetAccountDeposit::get() } - fn touch(asset: T::AssetId, who: T::AccountId, depositor: T::AccountId) -> DispatchResult { - Self::do_touch(asset, who, depositor, false) + fn should_touch(_: T::AssetId, _: &T::AccountId) -> bool { + true + } + + fn touch( + asset: T::AssetId, + who: &T::AccountId, + depositor: &T::AccountId, + ) -> DispatchResult { + Self::do_touch(asset, who.clone(), depositor.clone(), false) } } diff --git a/substrate/frame/balances/src/impl_fungible.rs b/substrate/frame/balances/src/impl_fungible.rs index fc8c2d71f256..6737727e0a29 100644 --- a/substrate/frame/balances/src/impl_fungible.rs +++ b/substrate/frame/balances/src/impl_fungible.rs @@ -17,10 +17,13 @@ //! Implementation of `fungible` traits for Balances pallet. use super::*; -use frame_support::traits::tokens::{ - Fortitude, - Preservation::{self, Preserve, Protect}, - Provenance::{self, Minted}, +use frame_support::traits::{ + tokens::{ + Fortitude, + Preservation::{self, Preserve, Protect}, + Provenance::{self, Minted}, + }, + AccountTouch, }; impl, I: 'static> fungible::Inspect for Pallet { @@ -356,3 +359,16 @@ impl, I: 'static> fungible::Balanced for Pallet } impl, I: 'static> fungible::BalancedHold for Pallet {} + +impl, I: 'static> AccountTouch<(), T::AccountId> for Pallet { + type Balance = T::Balance; + fn deposit_required(_: ()) -> Self::Balance { + Self::Balance::zero() + } + fn should_touch(_: (), _: &T::AccountId) -> bool { + false + } + fn touch(_: (), _: &T::AccountId, _: &T::AccountId) -> DispatchResult { + Ok(()) + } +} diff --git a/substrate/frame/support/src/traits/misc.rs b/substrate/frame/support/src/traits/misc.rs index 45a3bba9b3a6..a60dbd716c05 100644 --- a/substrate/frame/support/src/traits/misc.rs +++ b/substrate/frame/support/src/traits/misc.rs @@ -1170,17 +1170,25 @@ impl PreimageRecipient for () { fn unnote_preimage(_: &Hash) {} } -/// Trait for creating an asset account with a deposit taken from a designated depositor specified -/// by the client. +/// Trait for touching/creating an asset account with a deposit taken from a designated depositor +/// specified by the client. +/// +/// Ensures that transfers to the touched account will succeed without being denied by the system's +/// account existence requirements. For example, this is particularly useful for the account +/// creation of non-sufficient assets when its system account may not have the free consumer +/// reference required for it. pub trait AccountTouch { /// The type for currency units of the deposit. type Balance; - /// The deposit amount of a native currency required for creating an account of the `asset`. + /// The deposit amount of a native currency required for touching an account of the `asset`. fn deposit_required(asset: AssetId) -> Self::Balance; + /// Checks if an account for a given asset should be touched. + fn should_touch(asset: AssetId, who: &AccountId) -> bool; + /// Create an account for `who` of the `asset` with a deposit taken from the `depositor`. - fn touch(asset: AssetId, who: AccountId, depositor: AccountId) -> DispatchResult; + fn touch(asset: AssetId, who: &AccountId, depositor: &AccountId) -> DispatchResult; } #[cfg(test)] diff --git a/substrate/frame/support/src/traits/tokens/fungible/union_of.rs b/substrate/frame/support/src/traits/tokens/fungible/union_of.rs index 3fad47afddd3..fb9d722e6575 100644 --- a/substrate/frame/support/src/traits/tokens/fungible/union_of.rs +++ b/substrate/frame/support/src/traits/tokens/fungible/union_of.rs @@ -954,7 +954,8 @@ impl< } impl< - Left: fungible::Inspect, + Left: fungible::Inspect + + AccountTouch<(), AccountId, Balance = >::Balance>, Right: fungibles::Inspect + AccountTouch< Right::AssetId, @@ -975,10 +976,16 @@ impl< } } - fn touch(asset: AssetKind, who: AccountId, depositor: AccountId) -> DispatchResult { + fn should_touch(asset: AssetKind, who: &AccountId) -> bool { match Criterion::convert(asset) { - // no-op for `Left` since `AccountTouch` trait is not defined within `fungible::*`. - Left(()) => Ok(()), + Left(()) => >::should_touch((), who), + Right(a) => >::should_touch(a, who), + } + } + + fn touch(asset: AssetKind, who: &AccountId, depositor: &AccountId) -> DispatchResult { + match Criterion::convert(asset) { + Left(()) => >::touch((), who, depositor), Right(a) => >::touch(a, who, depositor), } diff --git a/substrate/frame/support/src/traits/tokens/fungibles/union_of.rs b/substrate/frame/support/src/traits/tokens/fungibles/union_of.rs index 6b093f1f6cc5..c7bd3a0fe2d9 100644 --- a/substrate/frame/support/src/traits/tokens/fungibles/union_of.rs +++ b/substrate/frame/support/src/traits/tokens/fungibles/union_of.rs @@ -959,7 +959,14 @@ impl< } } - fn touch(asset: AssetKind, who: AccountId, depositor: AccountId) -> DispatchResult { + fn should_touch(asset: AssetKind, who: &AccountId) -> bool { + match Criterion::convert(asset) { + Left(a) => >::should_touch(a, who), + Right(a) => >::should_touch(a, who), + } + } + + fn touch(asset: AssetKind, who: &AccountId, depositor: &AccountId) -> DispatchResult { match Criterion::convert(asset) { Left(a) => >::touch(a, who, depositor), Right(a) => From 9ea208deea331f9540004c05cfec299325819ca4 Mon Sep 17 00:00:00 2001 From: muharem Date: Tue, 31 Oct 2023 13:50:16 +0100 Subject: [PATCH 70/98] remove conrain pair from union_of --- .../src/traits/tokens/fungible/union_of.rs | 21 +------------------ .../src/traits/tokens/fungibles/union_of.rs | 19 +---------------- 2 files changed, 2 insertions(+), 38 deletions(-) diff --git a/substrate/frame/support/src/traits/tokens/fungible/union_of.rs b/substrate/frame/support/src/traits/tokens/fungible/union_of.rs index fb9d722e6575..cad677b0f27f 100644 --- a/substrate/frame/support/src/traits/tokens/fungible/union_of.rs +++ b/substrate/frame/support/src/traits/tokens/fungible/union_of.rs @@ -24,7 +24,7 @@ use frame_support::traits::{ fungible, fungibles, AssetId, DepositConsequence, Fortitude, Imbalance as ImbalanceT, Precision, Preservation, Provenance, Restriction, WithdrawConsequence, }, - AccountTouch, ContainsPair, + AccountTouch, }; use scale_info::TypeInfo; use sp_runtime::{ @@ -991,22 +991,3 @@ impl< } } } - -impl< - Left: fungible::Inspect, - Right: fungibles::Inspect - + ContainsPair, - Criterion: Convert>, - AssetKind: AssetId, - AccountId, - > ContainsPair for UnionOf -{ - fn contains(asset: &AssetKind, who: &AccountId) -> bool { - match Criterion::convert(asset.clone()) { - Left(()) => - >::total_balance(who) >= - >::minimum_balance(), - Right(a) => >::contains(&a, who), - } - } -} diff --git a/substrate/frame/support/src/traits/tokens/fungibles/union_of.rs b/substrate/frame/support/src/traits/tokens/fungibles/union_of.rs index c7bd3a0fe2d9..d74c1b360413 100644 --- a/substrate/frame/support/src/traits/tokens/fungibles/union_of.rs +++ b/substrate/frame/support/src/traits/tokens/fungibles/union_of.rs @@ -22,7 +22,7 @@ use frame_support::traits::{ fungibles, AssetId, DepositConsequence, Fortitude, Precision, Preservation, Provenance, Restriction, WithdrawConsequence, }, - AccountTouch, ContainsPair, + AccountTouch, }; use sp_runtime::{ traits::Convert, @@ -974,20 +974,3 @@ impl< } } } - -impl< - Left: fungibles::Inspect + ContainsPair, - Right: fungibles::Inspect - + ContainsPair, - Criterion: Convert>, - AssetKind: AssetId, - AccountId, - > ContainsPair for UnionOf -{ - fn contains(asset: &AssetKind, who: &AccountId) -> bool { - match Criterion::convert(asset.clone()) { - Left(a) => >::contains(&a, who), - Right(a) => >::contains(&a, who), - } - } -} From d87a80ed7bd87ae1d9a8b03258ac020c76a22571 Mon Sep 17 00:00:00 2001 From: muharem Date: Tue, 31 Oct 2023 13:57:12 +0100 Subject: [PATCH 71/98] should touch --- substrate/frame/asset-conversion/src/lib.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/substrate/frame/asset-conversion/src/lib.rs b/substrate/frame/asset-conversion/src/lib.rs index 7f90ecce6f8d..f0695678fbdd 100644 --- a/substrate/frame/asset-conversion/src/lib.rs +++ b/substrate/frame/asset-conversion/src/lib.rs @@ -81,7 +81,7 @@ use frame_support::{ Precision::Exact, Preservation::{Expendable, Preserve}, }, - AccountTouch, ContainsPair, Incrementable, OnUnbalanced, + AccountTouch, Incrementable, OnUnbalanced, }, PalletId, }; @@ -130,7 +130,6 @@ pub mod pallet { type Assets: Inspect + Mutate + AccountTouch - + ContainsPair + Balanced; /// Liquidity pool identifier. @@ -377,11 +376,11 @@ pub mod pallet { Self::withdraw(T::PoolSetupFeeAsset::get(), &sender, T::PoolSetupFee::get(), true)?; T::PoolSetupFeeTarget::on_unbalanced(fee); - if !T::Assets::contains(&asset1, &pool_account) { + if T::Assets::should_touch(*asset1.clone(), &pool_account) { T::Assets::touch(*asset1, &pool_account, &sender)? }; - if !T::Assets::contains(&asset2, &pool_account) { + if T::Assets::should_touch(*asset2.clone(), &pool_account) { T::Assets::touch(*asset2, &pool_account, &sender)? }; From 55917ddfdb79851b38f699ed5b6757f02a3f5e2f Mon Sep 17 00:00:00 2001 From: muharem Date: Tue, 31 Oct 2023 14:08:58 +0100 Subject: [PATCH 72/98] touch doc --- substrate/frame/support/src/traits/misc.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/substrate/frame/support/src/traits/misc.rs b/substrate/frame/support/src/traits/misc.rs index a60dbd716c05..dffa55dab4b8 100644 --- a/substrate/frame/support/src/traits/misc.rs +++ b/substrate/frame/support/src/traits/misc.rs @@ -1173,10 +1173,11 @@ impl PreimageRecipient for () { /// Trait for touching/creating an asset account with a deposit taken from a designated depositor /// specified by the client. /// -/// Ensures that transfers to the touched account will succeed without being denied by the system's -/// account existence requirements. For example, this is particularly useful for the account -/// creation of non-sufficient assets when its system account may not have the free consumer -/// reference required for it. +/// Ensures that transfers to the touched account will succeed without being denied by the account +/// existence requirements. For example, this is particularly useful for the account creation of +/// non-sufficient assets when its system account may not have the free consumer reference required +/// for it. If there is no risk of failing to meet those requirements, the touch operation can be a +/// no-op, as is common for native assets. pub trait AccountTouch { /// The type for currency units of the deposit. type Balance; From 62850a7855f4576a2e8560a94abce4b664ac4ee1 Mon Sep 17 00:00:00 2001 From: muharem Date: Tue, 31 Oct 2023 16:26:27 +0100 Subject: [PATCH 73/98] trader with target asset --- cumulus/parachains/common/src/impls.rs | 13 -- .../assets/asset-hub-westend/src/lib.rs | 19 +- .../asset-hub-westend/src/xcm_config.rs | 33 ++-- cumulus/primitives/utility/src/lib.rs | 175 ++++++++---------- 4 files changed, 105 insertions(+), 135 deletions(-) diff --git a/cumulus/parachains/common/src/impls.rs b/cumulus/parachains/common/src/impls.rs index 6d2e9eb9b28b..1d9c29040047 100644 --- a/cumulus/parachains/common/src/impls.rs +++ b/cumulus/parachains/common/src/impls.rs @@ -17,12 +17,10 @@ //! Taken from polkadot/runtime/common (at a21cd64) and adapted for parachains. use frame_support::traits::{ - fungible::{Balanced, Credit}, fungibles::{self, Balanced as FungiblesBalanced, Credit as FungiblesCredit}, Contains, ContainsPair, Currency, Get, Imbalance, OnUnbalanced, }; use pallet_asset_tx_payment::HandleCredit; -use sp_core::TypedGet; use sp_runtime::traits::Zero; use sp_std::marker::PhantomData; use xcm::latest::{AssetId, Fungibility::Fungible, MultiAsset, MultiLocation}; @@ -85,17 +83,6 @@ where } } -/// Implementation of [`OnUnbalanced`] which resolves the received credit to the specified account. -/// -/// If an account does not exist and the credit amount is less than the existential deposit, the -/// credit cannot be resolved and is dropped. -pub struct ResolveCreditTo(PhantomData<(A, F)>); -impl> OnUnbalanced> for ResolveCreditTo { - fn on_unbalanced(credit: Credit) { - let _ = F::resolve(&A::get(), credit).map_err(|c| drop(c)); - } -} - /// Allow checking in assets that have issuance > 0. pub struct NonZeroIssuance(PhantomData<(AccountId, Assets)>); impl Contains<>::AssetId> diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index fe9bb3dd99e0..54a2c19a7c48 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -300,7 +300,7 @@ impl pallet_assets::Config for Runtime { type BenchmarkHelper = (); } -/// Union fungibles implementation for `Assets`` and `ForeignAssets`. +/// Union fungibles implementation for `Assets` and `ForeignAssets`. pub type LocalAndForeignAssets = fungibles::UnionOf< Assets, ForeignAssets, @@ -312,18 +312,21 @@ pub type LocalAndForeignAssets = fungibles::UnionOf< AccountId, >; +/// Union fungibles implementation for [`LocalAndForeignAssets`] and `Balances`. +pub type NativeAndAssets = fungible::UnionOf< + Balances, + LocalAndForeignAssets, + TargetFromLeft, + MultiLocation, + AccountId, +>; + impl pallet_asset_conversion::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Balance = Balance; type HigherPrecisionBalance = sp_core::U256; type AssetKind = MultiLocation; - type Assets = fungible::UnionOf< - Balances, - LocalAndForeignAssets, - TargetFromLeft, - Self::AssetKind, - Self::AccountId, - >; + type Assets = NativeAndAssets; type PoolId = (Self::AssetKind, Self::AssetKind); type PoolLocator = pallet_asset_conversion::WithFirstAsset; diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs index 03854543d436..130cfc6a8720 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs @@ -26,12 +26,15 @@ use assets_common::{ }; use frame_support::{ match_types, parameter_types, - traits::{ConstU32, Contains, Everything, Nothing, PalletInfoAccess}, + traits::{ + tokens::imbalance::ResolveAssetTo, ConstU32, Contains, Everything, Nothing, + PalletInfoAccess, + }, }; use frame_system::EnsureRoot; use pallet_xcm::XcmPassthrough; use parachains_common::{ - impls::{ResolveCreditTo, ToStakingPot}, + impls::ToStakingPot, xcm_config::{ AssetFeeAsExistentialDepositMultiplier, ConcreteAssetFromSystem, RelayOrOtherSystemParachains, @@ -47,8 +50,8 @@ use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, CurrencyAdapter, DenyReserveTransferToRelayChain, DenyThenTry, DescribeFamily, DescribePalletTerminal, - EnsureXcmOrigin, FungiblesAdapter, HashedDescription, IsConcrete, LocalMint, NativeAsset, - NoChecking, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, + EnsureXcmOrigin, FungiblesAdapter, HashedDescription, IsConcrete, LocalMint, NoChecking, + ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, StartsWith, StartsWithExplicitGlobalConsensus, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, @@ -556,19 +559,15 @@ impl xcm_executor::Config for XcmConfig { >; type Trader = ( UsingComponents>, - // cumulus_primitives_utility::SwapFirstAssetTrader< - // AccountId, - // crate::AssetConversion, - // crate::MultiLocationConverter< - // WestendLocation, - // LocalAndForeignAssetsMultiLocationMatcher, - // >, - // WeightToFee, - // super::LocalAndTrustedForeignAssets, - // ForeignAssetsConvertedConcreteId, - // crate::Balances, - // ResolveCreditTo, - // >, + cumulus_primitives_utility::SwapFirstAssetTrader< + WestendLocation, + crate::AssetConversion, + WeightToFee, + crate::NativeAndAssets, + ForeignAssetsConvertedConcreteId, + ResolveAssetTo, + AccountId, + >, ); type ResponseHandler = PolkadotXcm; type AssetTrap = PolkadotXcm; diff --git a/cumulus/primitives/utility/src/lib.rs b/cumulus/primitives/utility/src/lib.rs index dd754d134549..639bebc21b36 100644 --- a/cumulus/primitives/utility/src/lib.rs +++ b/cumulus/primitives/utility/src/lib.rs @@ -22,15 +22,7 @@ use codec::Encode; use cumulus_primitives_core::{MessageSendError, UpwardMessageSender}; use frame_support::{ - traits::{ - tokens::{ - fungible, - fungible::Credit as FungibleCredit, - fungibles, - fungibles::{Credit as FungiblesCredit, Inspect}, - }, - Get, Imbalance, OnUnbalanced as OnUnbalancedT, - }, + traits::{tokens::fungibles, Get, OnUnbalanced as OnUnbalancedT}, weights::{Weight, WeightToFee as WeightToFeeT}, }; use pallet_asset_conversion::SwapCredit as SwapCreditT; @@ -295,84 +287,85 @@ impl< /// in such assetId for that amount of weight pub trait ChargeWeightInFungibles> { fn charge_weight_in_fungibles( - asset_id: >::AssetId, + asset_id: >::AssetId, weight: Weight, - ) -> Result<>::Balance, XcmError>; + ) -> Result<>::Balance, XcmError>; } -/* /// Provides an implementation of [`WeightTrader`] to charge for weight using the first asset /// specified in the `payment` argument. /// -/// Note: The asset utilized for weight must be non-native and must be exchangeable for a native -/// asset through `SwapCredit`. -// TODO: decouple implementation from the native asset concept, allowing the `TargetAsset` -// argument as an exchange asset. Refer to https://github.com/paritytech/polkadot-sdk/issues/1842. +/// The asset used to pay for the weight must differ from the `Target` asset and be exchangeable for +/// the same `Target` asset through `SwapCredit`. +/// +/// ### Parameters: +/// - `Target`: the asset into which the user's payment will be exchanged using `SwapCredit`. +/// - `SwapCredit`: mechanism used for the exchange of the user's payment asset into the `Target`. +/// - `WeightToFee`: weight to fee calculator. +/// - `Fungibles`: registry of fungible assets. +/// - `FungiblesAssetMatcher`: utility for mapping [`MultiAsset`] to `Fungibles::AssetId` and +/// `Fungibles::Balance`. +/// - `OnUnbalanced`: handler for the fee payment. +/// - `AccountId`: the account identifier type. pub struct SwapFirstAssetTrader< - AccountId, - SwapCredit: SwapCreditT, - SwapAssetMatcher: SwapAssetMatcherT, - WeightToFee: WeightToFeeT, - Fungibles: fungibles::Balanced, + Target: Get, + SwapCredit: SwapCreditT< + AccountId, + Balance = Fungibles::Balance, + AssetKind = Fungibles::AssetId, + Credit = fungibles::Credit, + >, + WeightToFee: WeightToFeeT, + Fungibles: fungibles::Balanced, FungiblesAssetMatcher: MatchesFungibles, - Fungible: fungible::Balanced, - OnUnbalanced: OnUnbalancedT>, + OnUnbalanced: OnUnbalancedT>, + AccountId, > where - SwapCredit::MultiAssetId: From, - SwapCredit::Credit: From> - + From> - + TryInto> - + TryInto>, - Fungible::Balance: Into, Fungibles::Balance: Into, { /// Accumulated fee paid for XCM execution. - total_fee: FungibleCredit, + total_fee: fungibles::Credit, /// Last asset utilized by a client to settle a fee. last_fee_asset: Option, _phantom_data: PhantomData<( - AccountId, + Target, SwapCredit, - SwapAssetMatcher, WeightToFee, Fungibles, FungiblesAssetMatcher, - Fungible, OnUnbalanced, + AccountId, )>, } impl< - AccountId, - SwapCredit: SwapCreditT, - SwapAssetMatcher: SwapAssetMatcherT, - WeightToFee: WeightToFeeT, - Fungibles: fungibles::Balanced, + Target: Get, + SwapCredit: SwapCreditT< + AccountId, + Balance = Fungibles::Balance, + AssetKind = Fungibles::AssetId, + Credit = fungibles::Credit, + >, + WeightToFee: WeightToFeeT, + Fungibles: fungibles::Balanced, FungiblesAssetMatcher: MatchesFungibles, - Fungible: fungible::Balanced, - OnUnbalanced: OnUnbalancedT>, + OnUnbalanced: OnUnbalancedT>, + AccountId, > WeightTrader for SwapFirstAssetTrader< - AccountId, + Target, SwapCredit, - SwapAssetMatcher, WeightToFee, Fungibles, FungiblesAssetMatcher, - Fungible, OnUnbalanced, + AccountId, > where - SwapCredit::MultiAssetId: From, - SwapCredit::Credit: From> - + From> - + TryInto> - + TryInto>, - Fungible::Balance: Into, Fungibles::Balance: Into, { fn new() -> Self { Self { - total_fee: FungibleCredit::::zero(), + total_fee: fungibles::Credit::::zero(Target::get()), last_fee_asset: None, _phantom_data: PhantomData, } @@ -390,16 +383,18 @@ impl< .map_err(|_| XcmError::FeesNotMet)?; let swap_asset = fungibles_asset.clone().into(); - if SwapAssetMatcher::is_native(&swap_asset) { + if Target::get().eq(&swap_asset) { + // current trader is not applicable. return Err(XcmError::FeesNotMet) } let credit_in = Fungibles::issue(fungibles_asset, balance); let fee = WeightToFee::weight_to_fee(&weight); + // swap the user's asset for the `Target` asset. let (credit_out, credit_change) = SwapCredit::swap_tokens_for_exact_tokens( - vec![swap_asset, SwapAssetMatcher::get_native()], - credit_in.into(), + vec![swap_asset, Target::get()], + credit_in, fee, ) .map_err(|(credit_in, _)| { @@ -408,17 +403,9 @@ impl< })?; // the execution below must be infallible to keep this operation transactional. - self.total_fee.subsume( - // should never fail since `credit_out` is `FungibleCredit`. - // TODO: make this infallible. Refer to https://github.com/paritytech/polkadot-sdk/issues/1842. - credit_out.try_into().map_err(|_| XcmError::FeesNotMet)?, - ); - self.last_fee_asset = Some(first_asset.id); - let credit_change: FungiblesCredit = - // should never fail since `credit_change` is `FungiblesCredit`. - // TODO: make this infallible. Refer to https://github.com/paritytech/polkadot-sdk/issues/1842. - credit_change.try_into().map_err(|_| XcmError::FeesNotMet)?; + self.total_fee.subsume(credit_out); + self.last_fee_asset = Some(first_asset.id); payment.fungible.insert(first_asset.id, credit_change.peek().into()); drop(credit_change); @@ -427,12 +414,14 @@ impl< fn refund_weight(&mut self, weight: Weight, _context: &XcmContext) -> Option { if self.total_fee.peek().is_zero() { + // noting yet paid to refund. return None } let mut refund_asset = if let Some(asset) = &self.last_fee_asset { (*asset, 0).into() } else { return None }; let refund_amount = WeightToFee::weight_to_fee(&weight); if refund_amount >= self.total_fee.peek() { + // not enough was paid to refund the `weight`. return None } @@ -441,24 +430,21 @@ impl< .ok()?; let refund = self.total_fee.extract(refund_amount); - let refund: FungibleCredit = - match SwapCredit::swap_exact_tokens_for_tokens( - vec![SwapAssetMatcher::get_native(), refund_swap_asset], - refund.into(), - None, - ) { - Ok(r) => r.try_into().ok()?, - Err((c, _)) => { - self.total_fee.subsume( - // should never fail since `credit_in` is `FungibleCredit`. - // TODO: make this infallible. Refer to https://github.com/paritytech/polkadot-sdk/issues/1842. - c.try_into().ok()?, - ); - return None - }, - }; + let refund = match SwapCredit::swap_exact_tokens_for_tokens( + vec![Target::get(), refund_swap_asset], + refund, + None, + ) { + Ok(r) => r, + Err((c, _)) => { + // return an attempted refund back to the `total_fee`. + self.total_fee.subsume(c); + return None + }, + }; // the execution below must be infallible to keep this operation atomic. + refund_asset.fun = refund.peek().into().into(); drop(refund); Some(refund_asset) @@ -466,31 +452,28 @@ impl< } impl< - AccountId, - SwapCredit: SwapCreditT, - SwapAssetMatcher: SwapAssetMatcherT, - WeightToFee: WeightToFeeT, - Fungibles: fungibles::Balanced, + Target: Get, + SwapCredit: SwapCreditT< + AccountId, + Balance = Fungibles::Balance, + AssetKind = Fungibles::AssetId, + Credit = fungibles::Credit, + >, + WeightToFee: WeightToFeeT, + Fungibles: fungibles::Balanced, FungiblesAssetMatcher: MatchesFungibles, - Fungible: fungible::Balanced, - OnUnbalanced: OnUnbalancedT>, + OnUnbalanced: OnUnbalancedT>, + AccountId, > Drop for SwapFirstAssetTrader< - AccountId, + Target, SwapCredit, - SwapAssetMatcher, WeightToFee, Fungibles, FungiblesAssetMatcher, - Fungible, OnUnbalanced, + AccountId, > where - SwapCredit::MultiAssetId: From, - SwapCredit::Credit: From> - + From> - + TryInto> - + TryInto>, - Fungible::Balance: Into, Fungibles::Balance: Into, { fn drop(&mut self) { @@ -502,8 +485,6 @@ impl< } } -*/ - #[cfg(test)] mod tests { use super::*; From 964afa0fe51923c6399bc5320d33099b54af9f66 Mon Sep 17 00:00:00 2001 From: muharem Date: Tue, 31 Oct 2023 16:30:45 +0100 Subject: [PATCH 74/98] revert changes in impls --- cumulus/parachains/common/src/impls.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cumulus/parachains/common/src/impls.rs b/cumulus/parachains/common/src/impls.rs index 1d9c29040047..81d78baba548 100644 --- a/cumulus/parachains/common/src/impls.rs +++ b/cumulus/parachains/common/src/impls.rs @@ -17,7 +17,7 @@ //! Taken from polkadot/runtime/common (at a21cd64) and adapted for parachains. use frame_support::traits::{ - fungibles::{self, Balanced as FungiblesBalanced, Credit as FungiblesCredit}, + fungibles::{self, Balanced, Credit}, Contains, ContainsPair, Currency, Get, Imbalance, OnUnbalanced, }; use pallet_asset_tx_payment::HandleCredit; @@ -75,7 +75,7 @@ where R: pallet_authorship::Config + pallet_assets::Config, AccountIdOf: From + Into, { - fn handle_credit(credit: FungiblesCredit, pallet_assets::Pallet>) { + fn handle_credit(credit: Credit, pallet_assets::Pallet>) { if let Some(author) = pallet_authorship::Pallet::::author() { // In case of error: Will drop the result triggering the `OnDrop` of the imbalance. let _ = pallet_assets::Pallet::::resolve(&author, credit); @@ -251,8 +251,8 @@ mod tests { #[test] fn test_fees_and_tip_split() { new_test_ext().execute_with(|| { - let fee = >::issue(10); - let tip = >::issue(20); + let fee = Balances::issue(10); + let tip = Balances::issue(20); assert_eq!(Balances::free_balance(TEST_ACCOUNT), 0); From 04091d82f32037338f2d8d6c29c5596a3ea8075a Mon Sep 17 00:00:00 2001 From: muharem Date: Wed, 1 Nov 2023 16:07:33 +0100 Subject: [PATCH 75/98] logs --- cumulus/primitives/utility/src/lib.rs | 51 ++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 8 deletions(-) diff --git a/cumulus/primitives/utility/src/lib.rs b/cumulus/primitives/utility/src/lib.rs index 639bebc21b36..aae96e1c90c8 100644 --- a/cumulus/primitives/utility/src/lib.rs +++ b/cumulus/primitives/utility/src/lib.rs @@ -22,6 +22,7 @@ use codec::Encode; use cumulus_primitives_core::{MessageSendError, UpwardMessageSender}; use frame_support::{ + defensive, traits::{tokens::fungibles, Get, OnUnbalanced as OnUnbalancedT}, weights::{Weight, WeightToFee as WeightToFeeT}, }; @@ -377,6 +378,12 @@ impl< mut payment: xcm_executor::Assets, _context: &XcmContext, ) -> Result { + log::trace!( + target: "xcm::weight", + "SwapFirstAssetTrader::buy_weight weight: {:?}, payment: {:?}", + weight, + payment, + ); let first_asset: MultiAsset = payment.fungible.pop_first().ok_or(XcmError::AssetNotFound)?.into(); let (fungibles_asset, balance) = FungiblesAssetMatcher::matches_fungibles(&first_asset) @@ -404,7 +411,18 @@ impl< // the execution below must be infallible to keep this operation transactional. - self.total_fee.subsume(credit_out); + match self.total_fee.subsume(credit_out) { + Err(credit_out) => { + // error may occur if `total_fee.asset` differs from `credit_out.asset`, which does + // not apply in this context. + defensive!( + "`total_fee.asset` must be equal to `credit_out.asset`", + (self.total_fee.asset(), credit_out.asset()) + ); + return Ok(payment) + }, + _ => (), + }; self.last_fee_asset = Some(first_asset.id); payment.fungible.insert(first_asset.id, credit_change.peek().into()); @@ -413,12 +431,22 @@ impl< } fn refund_weight(&mut self, weight: Weight, _context: &XcmContext) -> Option { + log::trace!( + target: "xcm::weight", + "SwapFirstAssetTrader::refund_weight weight: {:?}, self.total_fee: {:?}", + weight, + self.total_fee, + ); if self.total_fee.peek().is_zero() { // noting yet paid to refund. return None } - let mut refund_asset = - if let Some(asset) = &self.last_fee_asset { (*asset, 0).into() } else { return None }; + let mut refund_asset = if let Some(asset) = &self.last_fee_asset { + // create an initial zero refund in the asset used in the last `buy_weight`. + (*asset, 0).into() + } else { + return None + }; let refund_amount = WeightToFee::weight_to_fee(&weight); if refund_amount >= self.total_fee.peek() { // not enough was paid to refund the `weight`. @@ -435,10 +463,17 @@ impl< refund, None, ) { - Ok(r) => r, - Err((c, _)) => { + Ok(refund_in_target) => refund_in_target, + Err((refund, _)) => { // return an attempted refund back to the `total_fee`. - self.total_fee.subsume(c); + let _ = self.total_fee.subsume(refund).map_err(|refund| { + // error may occur if `total_fee.asset` differs from `refund.asset`, which does + // not apply in this context. + defensive!( + "`total_fee.asset` must be equal to `refund.asset`", + (self.total_fee.asset(), refund.asset()) + ); + }); return None }, }; @@ -685,9 +720,9 @@ mod tests { struct FeeChargerAssetsHandleRefund; impl ChargeWeightInFungibles for FeeChargerAssetsHandleRefund { fn charge_weight_in_fungibles( - _: >::AssetId, + _: >::AssetId, _: Weight, - ) -> Result<>::Balance, XcmError> { + ) -> Result<>::Balance, XcmError> { Ok(AMOUNT) } } From bf3ce63e21b2d108ec014907135145f6aea6de35 Mon Sep 17 00:00:00 2001 From: muharem Date: Wed, 1 Nov 2023 16:14:01 +0100 Subject: [PATCH 76/98] docs --- .../runtimes/assets/asset-hub-westend/src/xcm_config.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs index 130cfc6a8720..70bd1e16b266 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs @@ -73,8 +73,8 @@ parameter_types! { pub PoolAssetsPalletLocation: MultiLocation = PalletInstance(::index() as u8).into(); pub CheckingAccount: AccountId = PolkadotXcm::check_account(); - /// Relay Chain Treasury pallet account derived via [`PalletLocationToAccountId`]. - // proof: [`self::ensure_relay_treasury_account_correct()`]. + /// The Relay Chain Treasury pallet's account on the current chain, derived using [`PalletLocationToAccountId`]. + // Proof of correctness: [`self::ensure_relay_treasury_account_correct()`]. pub RelayTreasuryAccount: AccountId = AccountId::from(hex_literal::hex!("8bb46fbc6413c0f273b1b09af7b83f30695801958b9151a8f899f468f80064b1")); pub TreasuryAccount: Option = Some(TREASURY_PALLET_ID.into_account_truncating()); From 33a7730ca987b2095d5af43a5655cdcd2961de34 Mon Sep 17 00:00:00 2001 From: muharem Date: Thu, 2 Nov 2023 10:58:03 +0100 Subject: [PATCH 77/98] remove comment --- cumulus/primitives/utility/src/lib.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cumulus/primitives/utility/src/lib.rs b/cumulus/primitives/utility/src/lib.rs index aae96e1c90c8..575c7c664cef 100644 --- a/cumulus/primitives/utility/src/lib.rs +++ b/cumulus/primitives/utility/src/lib.rs @@ -409,8 +409,6 @@ impl< XcmError::FeesNotMet })?; - // the execution below must be infallible to keep this operation transactional. - match self.total_fee.subsume(credit_out) { Err(credit_out) => { // error may occur if `total_fee.asset` differs from `credit_out.asset`, which does @@ -478,8 +476,6 @@ impl< }, }; - // the execution below must be infallible to keep this operation atomic. - refund_asset.fun = refund.peek().into().into(); drop(refund); Some(refund_asset) From 6c17db2120881a3feac5d0f519268630edbe6c73 Mon Sep 17 00:00:00 2001 From: muharem Date: Tue, 14 Nov 2023 16:39:54 +0100 Subject: [PATCH 78/98] tests --- cumulus/primitives/utility/src/lib.rs | 27 +- cumulus/primitives/utility/src/tests/mod.rs | 17 + .../utility/src/tests/swap_first.rs | 316 ++++++++++++++++++ 3 files changed, 350 insertions(+), 10 deletions(-) create mode 100644 cumulus/primitives/utility/src/tests/mod.rs create mode 100644 cumulus/primitives/utility/src/tests/swap_first.rs diff --git a/cumulus/primitives/utility/src/lib.rs b/cumulus/primitives/utility/src/lib.rs index 575c7c664cef..d379f02f54b9 100644 --- a/cumulus/primitives/utility/src/lib.rs +++ b/cumulus/primitives/utility/src/lib.rs @@ -37,6 +37,9 @@ use xcm::{latest::prelude::*, WrapVersion}; use xcm_builder::TakeRevenue; use xcm_executor::traits::{MatchesFungibles, TransactAsset, WeightTrader}; +#[cfg(test)] +mod tests; + /// Xcm router which recognises the `Parent` destination and handles it by sending the message into /// the given UMP `UpwardMessageSender` implementation. Thus this essentially adapts an /// `UpwardMessageSender` trait impl into a `SendXcm` trait impl. @@ -302,7 +305,7 @@ pub trait ChargeWeightInFungibles(dest.into(), message) ); } +} +#[cfg(test)] +mod test_trader { + use super::*; + use frame_support::{ + assert_ok, + traits::tokens::{ + DepositConsequence, Fortitude, Preservation, Provenance, WithdrawConsequence, + }, + }; + use sp_runtime::DispatchError; + use xcm_executor::{traits::Error, Assets}; #[test] fn take_first_asset_trader_buy_weight_called_twice_throws_error() { diff --git a/cumulus/primitives/utility/src/tests/mod.rs b/cumulus/primitives/utility/src/tests/mod.rs new file mode 100644 index 000000000000..e0ad8718b89e --- /dev/null +++ b/cumulus/primitives/utility/src/tests/mod.rs @@ -0,0 +1,17 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// 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 Cumulus. If not, see . + +mod swap_first; diff --git a/cumulus/primitives/utility/src/tests/swap_first.rs b/cumulus/primitives/utility/src/tests/swap_first.rs new file mode 100644 index 000000000000..5f433ae5ca34 --- /dev/null +++ b/cumulus/primitives/utility/src/tests/swap_first.rs @@ -0,0 +1,316 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// 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 Cumulus. If not, see . + +pub mod mock { + use crate::*; + use core::cell::RefCell; + use frame_support::{ + ensure, + traits::{ + fungibles::{Balanced, DecreaseIssuance, Dust, IncreaseIssuance, Inspect, Unbalanced}, + tokens::{ + DepositConsequence, Fortitude, Preservation, Provenance, WithdrawConsequence, + }, + }, + }; + use sp_runtime::DispatchError; + use std::collections::HashMap; + use xcm::latest::Junction; + + pub type AccountId = u64; + pub type AssetId = u32; + pub type Balance = u128; + pub type Credit = fungibles::Credit; + pub type Debt = fungibles::Debt; + + thread_local! { + pub static TOTAL_ISSUANCE: RefCell> = RefCell::new(HashMap::new()); + pub static SWAP: RefCell> = RefCell::new(HashMap::new()); + } + + pub struct Swap {} + impl SwapCreditT for Swap { + type Balance = Balance; + type AssetKind = AssetId; + type Credit = Credit; + fn max_path_len() -> u32 { + 2 + } + fn swap_exact_tokens_for_tokens( + path: Vec, + credit_in: Self::Credit, + _amount_out_min: Option, + ) -> Result { + ensure!(2 == path.len(), (credit_in, DispatchError::Unavailable)); + let swap_res = SWAP.with(|b| b.borrow_mut().remove(&(path[0], path[1]))); + let (debt, credit_out) = match swap_res { + Some((d, c)) => (d, c), + None => return Err((credit_in, DispatchError::Unavailable)), + }; + drop(debt); + drop(credit_in); + Ok(credit_out) + } + fn swap_tokens_for_exact_tokens( + path: Vec, + credit_in: Self::Credit, + _amount_out: Self::Balance, + ) -> Result<(Self::Credit, Self::Credit), (Self::Credit, DispatchError)> { + ensure!(2 == path.len(), (credit_in, DispatchError::Unavailable)); + let swap_res = SWAP.with(|b| b.borrow_mut().remove(&(path[0], path[1]))); + let (debt, credit_out) = match swap_res { + Some((d, c)) => (d, c), + None => return Err((credit_in, DispatchError::Unavailable)), + }; + let (credit_debt, change) = credit_in.split(debt.peek()); + drop(debt); + drop(credit_debt); + Ok((credit_out, change)) + } + } + + pub fn prepare_swap( + asset_in: AssetId, + max_amount_in: Balance, + asset_out: AssetId, + amount_out: Balance, + ) { + let debt = Fungibles::rescind(asset_in, max_amount_in); + let credit_out = Fungibles::issue(asset_out, amount_out); + SWAP.with(|b| b.borrow_mut().insert((asset_in, asset_out), (debt, credit_out))); + } + + pub struct WeightToFee; + impl WeightToFeeT for WeightToFee { + type Balance = Balance; + fn weight_to_fee(weight: &Weight) -> Self::Balance { + (weight.ref_time() + weight.proof_size()).into() + } + } + + pub struct Fungibles {} + + impl Inspect for Fungibles { + type AssetId = AssetId; + type Balance = Balance; + fn total_issuance(asset: Self::AssetId) -> Self::Balance { + TOTAL_ISSUANCE.with(|b| b.borrow().get(&asset).map_or(Self::Balance::zero(), |b| *b)) + } + fn minimum_balance(_: Self::AssetId) -> Self::Balance { + unimplemented!() + } + fn total_balance(_: Self::AssetId, _: &AccountId) -> Self::Balance { + unimplemented!() + } + fn balance(_: Self::AssetId, _: &AccountId) -> Self::Balance { + unimplemented!() + } + fn reducible_balance( + _: Self::AssetId, + _: &AccountId, + _: Preservation, + _: Fortitude, + ) -> Self::Balance { + unimplemented!() + } + fn can_deposit( + _: Self::AssetId, + _: &AccountId, + _: Self::Balance, + _: Provenance, + ) -> DepositConsequence { + unimplemented!() + } + fn can_withdraw( + _: Self::AssetId, + _: &AccountId, + _: Self::Balance, + ) -> WithdrawConsequence { + unimplemented!() + } + fn asset_exists(_: Self::AssetId) -> bool { + unimplemented!() + } + } + + impl Unbalanced for Fungibles { + fn set_total_issuance(asset: Self::AssetId, amount: Self::Balance) { + TOTAL_ISSUANCE.with(|b| b.borrow_mut().insert(asset, amount)); + } + fn handle_dust(_: Dust) { + unimplemented!() + } + fn write_balance( + _: Self::AssetId, + _: &AccountId, + _: Self::Balance, + ) -> Result, DispatchError> { + unimplemented!() + } + } + + impl Balanced for Fungibles { + type OnDropCredit = DecreaseIssuance; + type OnDropDebt = IncreaseIssuance; + } + + pub struct FungiblesMatcher; + impl MatchesFungibles for FungiblesMatcher { + fn matches_fungibles( + a: &MultiAsset, + ) -> core::result::Result<(AssetId, Balance), xcm_executor::traits::Error> { + match a { + MultiAsset { + fun: Fungible(amount), + id: + Concrete(MultiLocation { parents: 0, interior: X1(Junction::GeneralIndex(id)) }), + } => Ok(((*id).try_into().unwrap(), *amount)), + _ => Err(xcm_executor::traits::Error::AssetNotHandled), + } + } + } +} + +use crate::*; +use frame_support::{ + parameter_types, + traits::fungibles::{Inspect, Unbalanced}, +}; +use mock::{AccountId, AssetId, Balance, Fungibles}; +use xcm_executor::Assets as HoldingAsset; + +fn create_exchangeable_holding_asset( + amount: Balance, + exchange_amount: Balance, + target_amount: Balance, +) -> (HoldingAsset, HoldingAsset) { + assert!(amount >= exchange_amount); + mock::prepare_swap(CLIENT_ASSET, exchange_amount, TARGET_ASSET, target_amount); + ( + create_holding_asset(CLIENT_ASSET, amount), + create_holding_asset(CLIENT_ASSET, amount - exchange_amount), + ) +} + +fn create_holding_asset(asset_id: AssetId, amount: Balance) -> HoldingAsset { + MultiAsset { + id: Concrete(MultiLocation::new(0, X1(GeneralIndex(asset_id.into())))), + fun: Fungible(amount), + } + .into() +} + +fn xcm_context() -> XcmContext { + XcmContext { origin: None, message_id: [0u8; 32], topic: None } +} + +// const UNKNOWN_ASSET: AssetId = 0; +const TARGET_ASSET: AssetId = 1; +const CLIENT_ASSET: AssetId = 2; + +parameter_types! { + pub const TargetAsset: AssetId = TARGET_ASSET; +} + +pub type Trader = SwapFirstAssetTrader< + TargetAsset, + mock::Swap, + mock::WeightToFee, + mock::Fungibles, + mock::FungiblesMatcher, + (), + AccountId, +>; + +#[test] +fn holding_asset_swap_for_target() { + Fungibles::set_total_issuance(TARGET_ASSET, 10000); + Fungibles::set_total_issuance(CLIENT_ASSET, 10000); + + let client_asset_total = 15; + let client_asset_fee = 5; + let target_asset_fee = 30; + + let (holding_asset, holding_change) = + create_exchangeable_holding_asset(client_asset_total, client_asset_fee, target_asset_fee); + + let target_total = Fungibles::total_issuance(TARGET_ASSET); + let client_total = Fungibles::total_issuance(CLIENT_ASSET); + + let mut trader = Trader::new(); + assert_eq!( + trader.buy_weight(Weight::from_all(10), holding_asset, &xcm_context()).unwrap(), + holding_change + ); + + assert_eq!(Fungibles::total_issuance(TARGET_ASSET), target_total); + assert_eq!(Fungibles::total_issuance(CLIENT_ASSET), client_total + client_asset_fee); +} + +#[test] +fn holding_asset_not_exchangeable_for_target() { + let holding_asset = create_holding_asset(CLIENT_ASSET, 10); + + let target_total = Fungibles::total_issuance(TARGET_ASSET); + let client_total = Fungibles::total_issuance(CLIENT_ASSET); + + let mut trader = Trader::new(); + assert_eq!( + trader + .buy_weight(Weight::from_all(10), holding_asset, &xcm_context()) + .unwrap_err(), + XcmError::FeesNotMet + ); + + assert_eq!(Fungibles::total_issuance(TARGET_ASSET), target_total); + assert_eq!(Fungibles::total_issuance(CLIENT_ASSET), client_total); +} + +#[test] +fn empty_holding_asset() { + let mut trader = Trader::new(); + assert_eq!( + trader + .buy_weight(Weight::from_all(10), HoldingAsset::new(), &xcm_context()) + .unwrap_err(), + XcmError::AssetNotFound + ); +} + +#[test] +fn fails_to_match_holding_asset() { + let mut trader = Trader::new(); + let holding_asset = + MultiAsset { id: Concrete(MultiLocation::new(1, X1(Parachain(1)))), fun: Fungible(10) }; + assert_eq!( + trader + .buy_weight(Weight::from_all(10), holding_asset.into(), &xcm_context()) + .unwrap_err(), + XcmError::FeesNotMet + ); +} + +#[test] +fn holding_asset_equal_to_target_asset() { + let mut trader = Trader::new(); + let holding_asset = create_holding_asset(TargetAsset::get(), 10); + assert_eq!( + trader + .buy_weight(Weight::from_all(10), holding_asset, &xcm_context()) + .unwrap_err(), + XcmError::FeesNotMet + ); +} From f9c3397b3f7dba8239a7b5811cbf4ad24ad59ea4 Mon Sep 17 00:00:00 2001 From: muharem Date: Wed, 15 Nov 2023 13:54:19 +0100 Subject: [PATCH 79/98] more tests --- cumulus/primitives/utility/src/lib.rs | 4 +- .../utility/src/tests/swap_first.rs | 376 ++++++++++++++---- 2 files changed, 308 insertions(+), 72 deletions(-) diff --git a/cumulus/primitives/utility/src/lib.rs b/cumulus/primitives/utility/src/lib.rs index d379f02f54b9..29e5b0d40937 100644 --- a/cumulus/primitives/utility/src/lib.rs +++ b/cumulus/primitives/utility/src/lib.rs @@ -390,7 +390,7 @@ impl< let first_asset: MultiAsset = payment.fungible.pop_first().ok_or(XcmError::AssetNotFound)?.into(); let (fungibles_asset, balance) = FungiblesAssetMatcher::matches_fungibles(&first_asset) - .map_err(|_| XcmError::FeesNotMet)?; + .map_err(|_| XcmError::AssetNotFound)?; let swap_asset = fungibles_asset.clone().into(); if Target::get().eq(&swap_asset) { @@ -444,7 +444,7 @@ impl< } let mut refund_asset = if let Some(asset) = &self.last_fee_asset { // create an initial zero refund in the asset used in the last `buy_weight`. - (*asset, 0).into() + (*asset, Fungible(0)).into() } else { return None }; diff --git a/cumulus/primitives/utility/src/tests/swap_first.rs b/cumulus/primitives/utility/src/tests/swap_first.rs index 5f433ae5ca34..f9fc50aee72c 100644 --- a/cumulus/primitives/utility/src/tests/swap_first.rs +++ b/cumulus/primitives/utility/src/tests/swap_first.rs @@ -22,11 +22,12 @@ pub mod mock { traits::{ fungibles::{Balanced, DecreaseIssuance, Dust, IncreaseIssuance, Inspect, Unbalanced}, tokens::{ - DepositConsequence, Fortitude, Preservation, Provenance, WithdrawConsequence, + DepositConsequence, Fortitude, Fortitude::Polite, Precision::Exact, Preservation, + Preservation::Preserve, Provenance, WithdrawConsequence, }, }, }; - use sp_runtime::DispatchError; + use sp_runtime::{traits::One, DispatchError}; use std::collections::HashMap; use xcm::latest::Junction; @@ -34,11 +35,11 @@ pub mod mock { pub type AssetId = u32; pub type Balance = u128; pub type Credit = fungibles::Credit; - pub type Debt = fungibles::Debt; thread_local! { pub static TOTAL_ISSUANCE: RefCell> = RefCell::new(HashMap::new()); - pub static SWAP: RefCell> = RefCell::new(HashMap::new()); + pub static ACCOUNT: RefCell> = RefCell::new(HashMap::new()); + pub static SWAP: RefCell> = RefCell::new(HashMap::new()); } pub struct Swap {} @@ -52,45 +53,74 @@ pub mod mock { fn swap_exact_tokens_for_tokens( path: Vec, credit_in: Self::Credit, - _amount_out_min: Option, + amount_out_min: Option, ) -> Result { ensure!(2 == path.len(), (credit_in, DispatchError::Unavailable)); - let swap_res = SWAP.with(|b| b.borrow_mut().remove(&(path[0], path[1]))); - let (debt, credit_out) = match swap_res { - Some((d, c)) => (d, c), + ensure!( + credit_in.peek() >= amount_out_min.unwrap_or(Self::Balance::zero()), + (credit_in, DispatchError::Unavailable) + ); + let swap_res = SWAP.with(|b| b.borrow().get(&(path[0], path[1])).map(|v| v.clone())); + let pool_account = match swap_res { + Some(a) => a, None => return Err((credit_in, DispatchError::Unavailable)), }; - drop(debt); - drop(credit_in); + let credit_out = match Fungibles::withdraw( + path[1], + &pool_account, + credit_in.peek(), + Exact, + Preserve, + Polite, + ) { + Ok(c) => c, + Err(_) => return Err((credit_in, DispatchError::Unavailable)), + }; + let _ = Fungibles::resolve(&pool_account, credit_in) + .map_err(|c| (c, DispatchError::Unavailable))?; Ok(credit_out) } fn swap_tokens_for_exact_tokens( path: Vec, credit_in: Self::Credit, - _amount_out: Self::Balance, + amount_out: Self::Balance, ) -> Result<(Self::Credit, Self::Credit), (Self::Credit, DispatchError)> { ensure!(2 == path.len(), (credit_in, DispatchError::Unavailable)); - let swap_res = SWAP.with(|b| b.borrow_mut().remove(&(path[0], path[1]))); - let (debt, credit_out) = match swap_res { - Some((d, c)) => (d, c), + ensure!(credit_in.peek() >= amount_out, (credit_in, DispatchError::Unavailable)); + let swap_res = SWAP.with(|b| b.borrow().get(&(path[0], path[1])).map(|v| v.clone())); + let pool_account = match swap_res { + Some(a) => a, None => return Err((credit_in, DispatchError::Unavailable)), }; - let (credit_debt, change) = credit_in.split(debt.peek()); - drop(debt); - drop(credit_debt); + let credit_out = match Fungibles::withdraw( + path[1], + &pool_account, + amount_out, + Exact, + Preserve, + Polite, + ) { + Ok(c) => c, + Err(_) => return Err((credit_in, DispatchError::Unavailable)), + }; + let (credit_in, change) = credit_in.split(amount_out); + let _ = Fungibles::resolve(&pool_account, credit_in) + .map_err(|c| (c, DispatchError::Unavailable))?; Ok((credit_out, change)) } } - pub fn prepare_swap( - asset_in: AssetId, - max_amount_in: Balance, - asset_out: AssetId, - amount_out: Balance, - ) { - let debt = Fungibles::rescind(asset_in, max_amount_in); - let credit_out = Fungibles::issue(asset_out, amount_out); - SWAP.with(|b| b.borrow_mut().insert((asset_in, asset_out), (debt, credit_out))); + pub fn pool_account(asset1: AssetId, asset2: AssetId) -> AccountId { + (1000 + asset1 * 10 + asset2 * 100).into() + } + + pub fn setup_pool(asset1: AssetId, liquidity1: Balance, asset2: AssetId, liquidity2: Balance) { + let account = pool_account(asset1, asset2); + SWAP.with(|b| b.borrow_mut().insert((asset1, asset2), account)); + let debt1 = Fungibles::deposit(asset1, &account, liquidity1, Exact); + let debt2 = Fungibles::deposit(asset2, &account, liquidity2, Exact); + drop(debt1); + drop(debt2); } pub struct WeightToFee; @@ -110,21 +140,21 @@ pub mod mock { TOTAL_ISSUANCE.with(|b| b.borrow().get(&asset).map_or(Self::Balance::zero(), |b| *b)) } fn minimum_balance(_: Self::AssetId) -> Self::Balance { - unimplemented!() + Self::Balance::one() } - fn total_balance(_: Self::AssetId, _: &AccountId) -> Self::Balance { - unimplemented!() + fn total_balance(asset: Self::AssetId, who: &AccountId) -> Self::Balance { + ACCOUNT.with(|b| b.borrow().get(&(asset, *who)).map_or(Self::Balance::zero(), |b| *b)) } - fn balance(_: Self::AssetId, _: &AccountId) -> Self::Balance { - unimplemented!() + fn balance(asset: Self::AssetId, who: &AccountId) -> Self::Balance { + ACCOUNT.with(|b| b.borrow().get(&(asset, *who)).map_or(Self::Balance::zero(), |b| *b)) } fn reducible_balance( - _: Self::AssetId, - _: &AccountId, + asset: Self::AssetId, + who: &AccountId, _: Preservation, _: Fortitude, ) -> Self::Balance { - unimplemented!() + ACCOUNT.with(|b| b.borrow().get(&(asset, *who)).map_or(Self::Balance::zero(), |b| *b)) } fn can_deposit( _: Self::AssetId, @@ -154,11 +184,12 @@ pub mod mock { unimplemented!() } fn write_balance( - _: Self::AssetId, - _: &AccountId, - _: Self::Balance, + asset: Self::AssetId, + who: &AccountId, + amount: Self::Balance, ) -> Result, DispatchError> { - unimplemented!() + let _ = ACCOUNT.with(|b| b.borrow_mut().insert((asset, *who), amount)); + Ok(None) } } @@ -185,41 +216,34 @@ pub mod mock { } use crate::*; -use frame_support::{ - parameter_types, - traits::fungibles::{Inspect, Unbalanced}, -}; -use mock::{AccountId, AssetId, Balance, Fungibles}; +use frame_support::{parameter_types, traits::fungibles::Inspect}; +use mock::{setup_pool, AccountId, AssetId, Balance, Fungibles}; +use xcm::latest::AssetId as XcmAssetId; use xcm_executor::Assets as HoldingAsset; -fn create_exchangeable_holding_asset( - amount: Balance, - exchange_amount: Balance, - target_amount: Balance, -) -> (HoldingAsset, HoldingAsset) { - assert!(amount >= exchange_amount); - mock::prepare_swap(CLIENT_ASSET, exchange_amount, TARGET_ASSET, target_amount); - ( - create_holding_asset(CLIENT_ASSET, amount), - create_holding_asset(CLIENT_ASSET, amount - exchange_amount), - ) +fn create_holding_asset(asset_id: AssetId, amount: Balance) -> HoldingAsset { + create_asset(asset_id, amount).into() } -fn create_holding_asset(asset_id: AssetId, amount: Balance) -> HoldingAsset { - MultiAsset { - id: Concrete(MultiLocation::new(0, X1(GeneralIndex(asset_id.into())))), - fun: Fungible(amount), - } - .into() +fn create_asset(asset_id: AssetId, amount: Balance) -> MultiAsset { + MultiAsset { id: create_asset_id(asset_id), fun: Fungible(amount) } +} + +fn create_asset_id(asset_id: AssetId) -> XcmAssetId { + Concrete(MultiLocation::new(0, X1(GeneralIndex(asset_id.into())))) } fn xcm_context() -> XcmContext { XcmContext { origin: None, message_id: [0u8; 32], topic: None } } -// const UNKNOWN_ASSET: AssetId = 0; +fn weight_worth_of(fee: Balance) -> Weight { + Weight::from_parts(fee.try_into().unwrap(), 0) +} + const TARGET_ASSET: AssetId = 1; const CLIENT_ASSET: AssetId = 2; +const CLIENT_ASSET_2: AssetId = 3; parameter_types! { pub const TargetAsset: AssetId = TARGET_ASSET; @@ -237,27 +261,239 @@ pub type Trader = SwapFirstAssetTrader< #[test] fn holding_asset_swap_for_target() { - Fungibles::set_total_issuance(TARGET_ASSET, 10000); - Fungibles::set_total_issuance(CLIENT_ASSET, 10000); + let client_asset_total = 15; + let fee = 5; + + setup_pool(CLIENT_ASSET, 1000, TARGET_ASSET, 1000); + + let holding_asset = create_holding_asset(CLIENT_ASSET, client_asset_total); + let holding_change = create_holding_asset(CLIENT_ASSET, client_asset_total - fee); + + let target_total = Fungibles::total_issuance(TARGET_ASSET); + let client_total = Fungibles::total_issuance(CLIENT_ASSET); + + let mut trader = Trader::new(); + assert_eq!( + trader.buy_weight(weight_worth_of(fee), holding_asset, &xcm_context()).unwrap(), + holding_change + ); + + assert_eq!(trader.total_fee.peek(), fee); + assert_eq!(trader.last_fee_asset, Some(create_asset_id(CLIENT_ASSET))); + assert_eq!(Fungibles::total_issuance(TARGET_ASSET), target_total); + assert_eq!(Fungibles::total_issuance(CLIENT_ASSET), client_total + fee); +} + +#[test] +fn holding_asset_swap_for_target_twice() { + let client_asset_total = 20; + let fee1 = 5; + let fee2 = 6; + + setup_pool(CLIENT_ASSET, 1000, TARGET_ASSET, 1000); + + let holding_asset = create_holding_asset(CLIENT_ASSET, client_asset_total); + let holding_change1 = create_holding_asset(CLIENT_ASSET, client_asset_total - fee1); + let holding_change2 = create_holding_asset(CLIENT_ASSET, client_asset_total - fee1 - fee2); + + let target_total = Fungibles::total_issuance(TARGET_ASSET); + let client_total = Fungibles::total_issuance(CLIENT_ASSET); + + let mut trader = Trader::new(); + assert_eq!( + trader.buy_weight(weight_worth_of(fee1), holding_asset, &xcm_context()).unwrap(), + holding_change1 + ); + assert_eq!( + trader + .buy_weight(weight_worth_of(fee2), holding_change1, &xcm_context()) + .unwrap(), + holding_change2 + ); + + assert_eq!(trader.total_fee.peek(), fee1 + fee2); + assert_eq!(trader.last_fee_asset, Some(create_asset_id(CLIENT_ASSET))); + + assert_eq!(Fungibles::total_issuance(TARGET_ASSET), target_total); + assert_eq!(Fungibles::total_issuance(CLIENT_ASSET), client_total + fee1 + fee2); +} + +#[test] +fn buy_and_refund_twice_for_target() { let client_asset_total = 15; - let client_asset_fee = 5; - let target_asset_fee = 30; + let fee = 5; + let refund1 = 4; + let refund2 = 2; + + setup_pool(CLIENT_ASSET, 1000, TARGET_ASSET, 1000); + // create pool for refund swap. + setup_pool(TARGET_ASSET, 1000, CLIENT_ASSET, 1000); - let (holding_asset, holding_change) = - create_exchangeable_holding_asset(client_asset_total, client_asset_fee, target_asset_fee); + let holding_asset = create_holding_asset(CLIENT_ASSET, client_asset_total); + let holding_change = create_holding_asset(CLIENT_ASSET, client_asset_total - fee); + let refund_asset = create_asset(CLIENT_ASSET, refund1); let target_total = Fungibles::total_issuance(TARGET_ASSET); let client_total = Fungibles::total_issuance(CLIENT_ASSET); let mut trader = Trader::new(); assert_eq!( - trader.buy_weight(Weight::from_all(10), holding_asset, &xcm_context()).unwrap(), + trader.buy_weight(weight_worth_of(fee), holding_asset, &xcm_context()).unwrap(), holding_change ); + assert_eq!(trader.total_fee.peek(), fee); + assert_eq!(trader.last_fee_asset, Some(create_asset_id(CLIENT_ASSET))); + + assert_eq!(trader.refund_weight(weight_worth_of(refund1), &xcm_context()), Some(refund_asset)); + + assert_eq!(trader.total_fee.peek(), fee - refund1); + assert_eq!(trader.last_fee_asset, Some(create_asset_id(CLIENT_ASSET))); + + assert_eq!(trader.refund_weight(weight_worth_of(refund2), &xcm_context()), None); + + assert_eq!(trader.total_fee.peek(), fee - refund1); + assert_eq!(trader.last_fee_asset, Some(create_asset_id(CLIENT_ASSET))); + assert_eq!(Fungibles::total_issuance(TARGET_ASSET), target_total); - assert_eq!(Fungibles::total_issuance(CLIENT_ASSET), client_total + client_asset_fee); + assert_eq!(Fungibles::total_issuance(CLIENT_ASSET), client_total + fee - refund1); +} + +#[test] +fn buy_with_various_assets_and_refund_for_target() { + let client_asset_total = 10; + let client_asset_2_total = 15; + let fee1 = 5; + let fee2 = 6; + let refund1 = 6; + let refund2 = 4; + + setup_pool(CLIENT_ASSET, 1000, TARGET_ASSET, 1000); + setup_pool(CLIENT_ASSET_2, 1000, TARGET_ASSET, 1000); + // create pool for refund swap. + setup_pool(TARGET_ASSET, 1000, CLIENT_ASSET_2, 1000); + + let holding_asset = create_holding_asset(CLIENT_ASSET, client_asset_total); + let holding_asset_2 = create_holding_asset(CLIENT_ASSET_2, client_asset_2_total); + let holding_change = create_holding_asset(CLIENT_ASSET, client_asset_total - fee1); + let holding_change_2 = create_holding_asset(CLIENT_ASSET_2, client_asset_2_total - fee2); + // both refunds in the latest buy asset (`CLIENT_ASSET_2`). + let refund_asset = create_asset(CLIENT_ASSET_2, refund1); + let refund_asset_2 = create_asset(CLIENT_ASSET_2, refund2); + + let target_total = Fungibles::total_issuance(TARGET_ASSET); + let client_total = Fungibles::total_issuance(CLIENT_ASSET); + let client_total_2 = Fungibles::total_issuance(CLIENT_ASSET_2); + + let mut trader = Trader::new(); + // first purchase with `CLIENT_ASSET`. + assert_eq!( + trader.buy_weight(weight_worth_of(fee1), holding_asset, &xcm_context()).unwrap(), + holding_change + ); + + assert_eq!(trader.total_fee.peek(), fee1); + assert_eq!(trader.last_fee_asset, Some(create_asset_id(CLIENT_ASSET))); + + // second purchase with `CLIENT_ASSET_2`. + assert_eq!( + trader + .buy_weight(weight_worth_of(fee2), holding_asset_2, &xcm_context()) + .unwrap(), + holding_change_2 + ); + + assert_eq!(trader.total_fee.peek(), fee1 + fee2); + assert_eq!(trader.last_fee_asset, Some(create_asset_id(CLIENT_ASSET_2))); + + // first refund in the last asset used with `buy_weight`. + assert_eq!(trader.refund_weight(weight_worth_of(refund1), &xcm_context()), Some(refund_asset)); + + assert_eq!(trader.total_fee.peek(), fee1 + fee2 - refund1); + assert_eq!(trader.last_fee_asset, Some(create_asset_id(CLIENT_ASSET_2))); + + // second refund in the last asset used with `buy_weight`. + assert_eq!( + trader.refund_weight(weight_worth_of(refund2), &xcm_context()), + Some(refund_asset_2) + ); + + assert_eq!(trader.total_fee.peek(), fee1 + fee2 - refund1 - refund2); + assert_eq!(trader.last_fee_asset, Some(create_asset_id(CLIENT_ASSET_2))); + + assert_eq!(Fungibles::total_issuance(TARGET_ASSET), target_total); + assert_eq!(Fungibles::total_issuance(CLIENT_ASSET), client_total + fee1); + assert_eq!( + Fungibles::total_issuance(CLIENT_ASSET_2), + client_total_2 + fee2 - refund1 - refund2 + ); +} + +#[test] +fn not_enough_to_refund() { + let client_asset_total = 15; + let fee = 5; + let refund = 6; + + setup_pool(CLIENT_ASSET, 1000, TARGET_ASSET, 1000); + + let holding_asset = create_holding_asset(CLIENT_ASSET, client_asset_total); + let holding_change = create_holding_asset(CLIENT_ASSET, client_asset_total - fee); + + let target_total = Fungibles::total_issuance(TARGET_ASSET); + let client_total = Fungibles::total_issuance(CLIENT_ASSET); + + let mut trader = Trader::new(); + assert_eq!( + trader.buy_weight(weight_worth_of(fee), holding_asset, &xcm_context()).unwrap(), + holding_change + ); + + assert_eq!(trader.total_fee.peek(), fee); + assert_eq!(trader.last_fee_asset, Some(create_asset_id(CLIENT_ASSET))); + + assert_eq!(trader.refund_weight(weight_worth_of(refund), &xcm_context()), None); + + assert_eq!(Fungibles::total_issuance(TARGET_ASSET), target_total); + assert_eq!(Fungibles::total_issuance(CLIENT_ASSET), client_total + fee); +} + +#[test] +fn not_exchangeable_to_refund() { + let client_asset_total = 15; + let fee = 5; + let refund = 1; + + setup_pool(CLIENT_ASSET, 1000, TARGET_ASSET, 1000); + + let holding_asset = create_holding_asset(CLIENT_ASSET, client_asset_total); + let holding_change = create_holding_asset(CLIENT_ASSET, client_asset_total - fee); + + let target_total = Fungibles::total_issuance(TARGET_ASSET); + let client_total = Fungibles::total_issuance(CLIENT_ASSET); + + let mut trader = Trader::new(); + assert_eq!( + trader.buy_weight(weight_worth_of(fee), holding_asset, &xcm_context()).unwrap(), + holding_change + ); + + assert_eq!(trader.total_fee.peek(), fee); + assert_eq!(trader.last_fee_asset, Some(create_asset_id(CLIENT_ASSET))); + + assert_eq!(trader.refund_weight(weight_worth_of(refund), &xcm_context()), None); + + assert_eq!(Fungibles::total_issuance(TARGET_ASSET), target_total); + assert_eq!(Fungibles::total_issuance(CLIENT_ASSET), client_total + fee); +} + +#[test] +fn nothing_to_refund() { + let fee = 5; + + let mut trader = Trader::new(); + assert_eq!(trader.refund_weight(weight_worth_of(fee), &xcm_context()), None); } #[test] @@ -299,7 +535,7 @@ fn fails_to_match_holding_asset() { trader .buy_weight(Weight::from_all(10), holding_asset.into(), &xcm_context()) .unwrap_err(), - XcmError::FeesNotMet + XcmError::AssetNotFound ); } From 47512eacc0b0f56e1930d6e8054d7b56b422eb1a Mon Sep 17 00:00:00 2001 From: muharem Date: Wed, 15 Nov 2023 13:57:07 +0100 Subject: [PATCH 80/98] move mock mod to the bottom --- .../utility/src/tests/swap_first.rs | 401 +++++++++--------- 1 file changed, 200 insertions(+), 201 deletions(-) diff --git a/cumulus/primitives/utility/src/tests/swap_first.rs b/cumulus/primitives/utility/src/tests/swap_first.rs index f9fc50aee72c..d7bb44a9e419 100644 --- a/cumulus/primitives/utility/src/tests/swap_first.rs +++ b/cumulus/primitives/utility/src/tests/swap_first.rs @@ -14,207 +14,6 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -pub mod mock { - use crate::*; - use core::cell::RefCell; - use frame_support::{ - ensure, - traits::{ - fungibles::{Balanced, DecreaseIssuance, Dust, IncreaseIssuance, Inspect, Unbalanced}, - tokens::{ - DepositConsequence, Fortitude, Fortitude::Polite, Precision::Exact, Preservation, - Preservation::Preserve, Provenance, WithdrawConsequence, - }, - }, - }; - use sp_runtime::{traits::One, DispatchError}; - use std::collections::HashMap; - use xcm::latest::Junction; - - pub type AccountId = u64; - pub type AssetId = u32; - pub type Balance = u128; - pub type Credit = fungibles::Credit; - - thread_local! { - pub static TOTAL_ISSUANCE: RefCell> = RefCell::new(HashMap::new()); - pub static ACCOUNT: RefCell> = RefCell::new(HashMap::new()); - pub static SWAP: RefCell> = RefCell::new(HashMap::new()); - } - - pub struct Swap {} - impl SwapCreditT for Swap { - type Balance = Balance; - type AssetKind = AssetId; - type Credit = Credit; - fn max_path_len() -> u32 { - 2 - } - fn swap_exact_tokens_for_tokens( - path: Vec, - credit_in: Self::Credit, - amount_out_min: Option, - ) -> Result { - ensure!(2 == path.len(), (credit_in, DispatchError::Unavailable)); - ensure!( - credit_in.peek() >= amount_out_min.unwrap_or(Self::Balance::zero()), - (credit_in, DispatchError::Unavailable) - ); - let swap_res = SWAP.with(|b| b.borrow().get(&(path[0], path[1])).map(|v| v.clone())); - let pool_account = match swap_res { - Some(a) => a, - None => return Err((credit_in, DispatchError::Unavailable)), - }; - let credit_out = match Fungibles::withdraw( - path[1], - &pool_account, - credit_in.peek(), - Exact, - Preserve, - Polite, - ) { - Ok(c) => c, - Err(_) => return Err((credit_in, DispatchError::Unavailable)), - }; - let _ = Fungibles::resolve(&pool_account, credit_in) - .map_err(|c| (c, DispatchError::Unavailable))?; - Ok(credit_out) - } - fn swap_tokens_for_exact_tokens( - path: Vec, - credit_in: Self::Credit, - amount_out: Self::Balance, - ) -> Result<(Self::Credit, Self::Credit), (Self::Credit, DispatchError)> { - ensure!(2 == path.len(), (credit_in, DispatchError::Unavailable)); - ensure!(credit_in.peek() >= amount_out, (credit_in, DispatchError::Unavailable)); - let swap_res = SWAP.with(|b| b.borrow().get(&(path[0], path[1])).map(|v| v.clone())); - let pool_account = match swap_res { - Some(a) => a, - None => return Err((credit_in, DispatchError::Unavailable)), - }; - let credit_out = match Fungibles::withdraw( - path[1], - &pool_account, - amount_out, - Exact, - Preserve, - Polite, - ) { - Ok(c) => c, - Err(_) => return Err((credit_in, DispatchError::Unavailable)), - }; - let (credit_in, change) = credit_in.split(amount_out); - let _ = Fungibles::resolve(&pool_account, credit_in) - .map_err(|c| (c, DispatchError::Unavailable))?; - Ok((credit_out, change)) - } - } - - pub fn pool_account(asset1: AssetId, asset2: AssetId) -> AccountId { - (1000 + asset1 * 10 + asset2 * 100).into() - } - - pub fn setup_pool(asset1: AssetId, liquidity1: Balance, asset2: AssetId, liquidity2: Balance) { - let account = pool_account(asset1, asset2); - SWAP.with(|b| b.borrow_mut().insert((asset1, asset2), account)); - let debt1 = Fungibles::deposit(asset1, &account, liquidity1, Exact); - let debt2 = Fungibles::deposit(asset2, &account, liquidity2, Exact); - drop(debt1); - drop(debt2); - } - - pub struct WeightToFee; - impl WeightToFeeT for WeightToFee { - type Balance = Balance; - fn weight_to_fee(weight: &Weight) -> Self::Balance { - (weight.ref_time() + weight.proof_size()).into() - } - } - - pub struct Fungibles {} - - impl Inspect for Fungibles { - type AssetId = AssetId; - type Balance = Balance; - fn total_issuance(asset: Self::AssetId) -> Self::Balance { - TOTAL_ISSUANCE.with(|b| b.borrow().get(&asset).map_or(Self::Balance::zero(), |b| *b)) - } - fn minimum_balance(_: Self::AssetId) -> Self::Balance { - Self::Balance::one() - } - fn total_balance(asset: Self::AssetId, who: &AccountId) -> Self::Balance { - ACCOUNT.with(|b| b.borrow().get(&(asset, *who)).map_or(Self::Balance::zero(), |b| *b)) - } - fn balance(asset: Self::AssetId, who: &AccountId) -> Self::Balance { - ACCOUNT.with(|b| b.borrow().get(&(asset, *who)).map_or(Self::Balance::zero(), |b| *b)) - } - fn reducible_balance( - asset: Self::AssetId, - who: &AccountId, - _: Preservation, - _: Fortitude, - ) -> Self::Balance { - ACCOUNT.with(|b| b.borrow().get(&(asset, *who)).map_or(Self::Balance::zero(), |b| *b)) - } - fn can_deposit( - _: Self::AssetId, - _: &AccountId, - _: Self::Balance, - _: Provenance, - ) -> DepositConsequence { - unimplemented!() - } - fn can_withdraw( - _: Self::AssetId, - _: &AccountId, - _: Self::Balance, - ) -> WithdrawConsequence { - unimplemented!() - } - fn asset_exists(_: Self::AssetId) -> bool { - unimplemented!() - } - } - - impl Unbalanced for Fungibles { - fn set_total_issuance(asset: Self::AssetId, amount: Self::Balance) { - TOTAL_ISSUANCE.with(|b| b.borrow_mut().insert(asset, amount)); - } - fn handle_dust(_: Dust) { - unimplemented!() - } - fn write_balance( - asset: Self::AssetId, - who: &AccountId, - amount: Self::Balance, - ) -> Result, DispatchError> { - let _ = ACCOUNT.with(|b| b.borrow_mut().insert((asset, *who), amount)); - Ok(None) - } - } - - impl Balanced for Fungibles { - type OnDropCredit = DecreaseIssuance; - type OnDropDebt = IncreaseIssuance; - } - - pub struct FungiblesMatcher; - impl MatchesFungibles for FungiblesMatcher { - fn matches_fungibles( - a: &MultiAsset, - ) -> core::result::Result<(AssetId, Balance), xcm_executor::traits::Error> { - match a { - MultiAsset { - fun: Fungible(amount), - id: - Concrete(MultiLocation { parents: 0, interior: X1(Junction::GeneralIndex(id)) }), - } => Ok(((*id).try_into().unwrap(), *amount)), - _ => Err(xcm_executor::traits::Error::AssetNotHandled), - } - } - } -} - use crate::*; use frame_support::{parameter_types, traits::fungibles::Inspect}; use mock::{setup_pool, AccountId, AssetId, Balance, Fungibles}; @@ -550,3 +349,203 @@ fn holding_asset_equal_to_target_asset() { XcmError::FeesNotMet ); } + +pub mod mock { + use crate::*; + use core::cell::RefCell; + use frame_support::{ + ensure, + traits::{ + fungibles::{Balanced, DecreaseIssuance, Dust, IncreaseIssuance, Inspect, Unbalanced}, + tokens::{ + DepositConsequence, Fortitude, Fortitude::Polite, Precision::Exact, Preservation, + Preservation::Preserve, Provenance, WithdrawConsequence, + }, + }, + }; + use sp_runtime::{traits::One, DispatchError}; + use std::collections::HashMap; + use xcm::latest::Junction; + + pub type AccountId = u64; + pub type AssetId = u32; + pub type Balance = u128; + pub type Credit = fungibles::Credit; + + thread_local! { + pub static TOTAL_ISSUANCE: RefCell> = RefCell::new(HashMap::new()); + pub static ACCOUNT: RefCell> = RefCell::new(HashMap::new()); + pub static SWAP: RefCell> = RefCell::new(HashMap::new()); + } + + pub struct Swap {} + impl SwapCreditT for Swap { + type Balance = Balance; + type AssetKind = AssetId; + type Credit = Credit; + fn max_path_len() -> u32 { + 2 + } + fn swap_exact_tokens_for_tokens( + path: Vec, + credit_in: Self::Credit, + amount_out_min: Option, + ) -> Result { + ensure!(2 == path.len(), (credit_in, DispatchError::Unavailable)); + ensure!( + credit_in.peek() >= amount_out_min.unwrap_or(Self::Balance::zero()), + (credit_in, DispatchError::Unavailable) + ); + let swap_res = SWAP.with(|b| b.borrow().get(&(path[0], path[1])).map(|v| v.clone())); + let pool_account = match swap_res { + Some(a) => a, + None => return Err((credit_in, DispatchError::Unavailable)), + }; + let credit_out = match Fungibles::withdraw( + path[1], + &pool_account, + credit_in.peek(), + Exact, + Preserve, + Polite, + ) { + Ok(c) => c, + Err(_) => return Err((credit_in, DispatchError::Unavailable)), + }; + let _ = Fungibles::resolve(&pool_account, credit_in) + .map_err(|c| (c, DispatchError::Unavailable))?; + Ok(credit_out) + } + fn swap_tokens_for_exact_tokens( + path: Vec, + credit_in: Self::Credit, + amount_out: Self::Balance, + ) -> Result<(Self::Credit, Self::Credit), (Self::Credit, DispatchError)> { + ensure!(2 == path.len(), (credit_in, DispatchError::Unavailable)); + ensure!(credit_in.peek() >= amount_out, (credit_in, DispatchError::Unavailable)); + let swap_res = SWAP.with(|b| b.borrow().get(&(path[0], path[1])).map(|v| v.clone())); + let pool_account = match swap_res { + Some(a) => a, + None => return Err((credit_in, DispatchError::Unavailable)), + }; + let credit_out = match Fungibles::withdraw( + path[1], + &pool_account, + amount_out, + Exact, + Preserve, + Polite, + ) { + Ok(c) => c, + Err(_) => return Err((credit_in, DispatchError::Unavailable)), + }; + let (credit_in, change) = credit_in.split(amount_out); + let _ = Fungibles::resolve(&pool_account, credit_in) + .map_err(|c| (c, DispatchError::Unavailable))?; + Ok((credit_out, change)) + } + } + + pub fn pool_account(asset1: AssetId, asset2: AssetId) -> AccountId { + (1000 + asset1 * 10 + asset2 * 100).into() + } + + pub fn setup_pool(asset1: AssetId, liquidity1: Balance, asset2: AssetId, liquidity2: Balance) { + let account = pool_account(asset1, asset2); + SWAP.with(|b| b.borrow_mut().insert((asset1, asset2), account)); + let debt1 = Fungibles::deposit(asset1, &account, liquidity1, Exact); + let debt2 = Fungibles::deposit(asset2, &account, liquidity2, Exact); + drop(debt1); + drop(debt2); + } + + pub struct WeightToFee; + impl WeightToFeeT for WeightToFee { + type Balance = Balance; + fn weight_to_fee(weight: &Weight) -> Self::Balance { + (weight.ref_time() + weight.proof_size()).into() + } + } + + pub struct Fungibles {} + impl Inspect for Fungibles { + type AssetId = AssetId; + type Balance = Balance; + fn total_issuance(asset: Self::AssetId) -> Self::Balance { + TOTAL_ISSUANCE.with(|b| b.borrow().get(&asset).map_or(Self::Balance::zero(), |b| *b)) + } + fn minimum_balance(_: Self::AssetId) -> Self::Balance { + Self::Balance::one() + } + fn total_balance(asset: Self::AssetId, who: &AccountId) -> Self::Balance { + ACCOUNT.with(|b| b.borrow().get(&(asset, *who)).map_or(Self::Balance::zero(), |b| *b)) + } + fn balance(asset: Self::AssetId, who: &AccountId) -> Self::Balance { + ACCOUNT.with(|b| b.borrow().get(&(asset, *who)).map_or(Self::Balance::zero(), |b| *b)) + } + fn reducible_balance( + asset: Self::AssetId, + who: &AccountId, + _: Preservation, + _: Fortitude, + ) -> Self::Balance { + ACCOUNT.with(|b| b.borrow().get(&(asset, *who)).map_or(Self::Balance::zero(), |b| *b)) + } + fn can_deposit( + _: Self::AssetId, + _: &AccountId, + _: Self::Balance, + _: Provenance, + ) -> DepositConsequence { + unimplemented!() + } + fn can_withdraw( + _: Self::AssetId, + _: &AccountId, + _: Self::Balance, + ) -> WithdrawConsequence { + unimplemented!() + } + fn asset_exists(_: Self::AssetId) -> bool { + unimplemented!() + } + } + + impl Unbalanced for Fungibles { + fn set_total_issuance(asset: Self::AssetId, amount: Self::Balance) { + TOTAL_ISSUANCE.with(|b| b.borrow_mut().insert(asset, amount)); + } + fn handle_dust(_: Dust) { + unimplemented!() + } + fn write_balance( + asset: Self::AssetId, + who: &AccountId, + amount: Self::Balance, + ) -> Result, DispatchError> { + let _ = ACCOUNT.with(|b| b.borrow_mut().insert((asset, *who), amount)); + Ok(None) + } + } + + impl Balanced for Fungibles { + type OnDropCredit = DecreaseIssuance; + type OnDropDebt = IncreaseIssuance; + } + + pub struct FungiblesMatcher; + impl MatchesFungibles for FungiblesMatcher { + fn matches_fungibles( + a: &MultiAsset, + ) -> core::result::Result<(AssetId, Balance), xcm_executor::traits::Error> { + match a { + MultiAsset { + fun: Fungible(amount), + id: + Concrete(MultiLocation { parents: 0, interior: X1(Junction::GeneralIndex(id)) }), + } => Ok(((*id).try_into().unwrap(), *amount)), + _ => Err(xcm_executor::traits::Error::AssetNotHandled), + } + } + } +} From a7bdc8144096c4d3348cc9246805bdb373c5f031 Mon Sep 17 00:00:00 2001 From: muharem Date: Wed, 15 Nov 2023 18:48:23 +0100 Subject: [PATCH 81/98] more tests --- .../asset-hub-westend/src/xcm_config.rs | 6 +- .../assets/asset-hub-westend/tests/tests.rs | 159 +++++++++++++++++- .../runtimes/assets/common/src/lib.rs | 10 ++ 3 files changed, 169 insertions(+), 6 deletions(-) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs index 70bd1e16b266..b0bb9c5199fa 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs @@ -23,6 +23,7 @@ use crate::ForeignAssets; use assets_common::{ local_and_foreign_assets::MatchesLocalAndForeignAssetsMultiLocation, matching::{FromSiblingParachain, IsForeignConcreteAsset}, + TrustBackedAssetsAsMultiLocation, }; use frame_support::{ match_types, parameter_types, @@ -564,7 +565,10 @@ impl xcm_executor::Config for XcmConfig { crate::AssetConversion, WeightToFee, crate::NativeAndAssets, - ForeignAssetsConvertedConcreteId, + ( + TrustBackedAssetsAsMultiLocation, + ForeignAssetsConvertedConcreteId, + ), ResolveAssetTo, AccountId, >, diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs index eeb8858fbd4c..3a42f07b0836 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs @@ -20,26 +20,33 @@ use asset_hub_westend_runtime::{ xcm_config::{ AssetFeeAsExistentialDepositMultiplierFeeCharger, ForeignCreatorsSovereignAccountOf, - WestendLocation, + RelayTreasuryAccount, WestendLocation, }, AllPalletsWithoutSystem, MetadataDepositBase, MetadataDepositPerByte, RuntimeCall, RuntimeEvent, }; pub use asset_hub_westend_runtime::{ xcm_config::{CheckingAccount, TrustBackedAssetsPalletLocation, XcmConfig}, - AssetDeposit, Assets, Balances, ExistentialDeposit, ForeignAssets, ForeignAssetsInstance, - ParachainSystem, Runtime, SessionKeys, System, TrustBackedAssetsInstance, + AssetConversion, AssetDeposit, Assets, Balances, ExistentialDeposit, ForeignAssets, + ForeignAssetsInstance, ParachainSystem, Runtime, SessionKeys, System, + TrustBackedAssetsInstance, }; use asset_test_utils::{CollatorSessionKeys, ExtBuilder, XcmReceivedFrom}; use codec::{Decode, DecodeLimit, Encode}; use cumulus_primitives_utility::ChargeWeightInFungibles; use frame_support::{ assert_noop, assert_ok, - traits::fungibles::InspectEnumerable, + traits::{ + fungible::{Inspect, Mutate}, + fungibles::{ + Create, Inspect as FungiblesInspect, InspectEnumerable, Mutate as FungiblesMutate, + }, + }, weights::{Weight, WeightToFee as WeightToFeeT}, }; use parachains_common::{ - westend::fee::WeightToFee, AccountId, AssetIdForTrustBackedAssets, AuraId, Balance, + westend::{currency::UNITS, fee::WeightToFee}, + AccountId, AssetIdForTrustBackedAssets, AuraId, Balance, }; use sp_io; use sp_runtime::traits::MaybeEquivalence; @@ -66,6 +73,148 @@ fn collator_session_keys() -> CollatorSessionKeys { ) } +#[test] +fn test_asset_swap_local_xcm_trader() { + ExtBuilder::::default() + .with_collators(vec![AccountId::from(ALICE)]) + .with_session_keys(vec![( + AccountId::from(ALICE), + AccountId::from(ALICE), + SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) }, + )]) + .build() + .execute_with(|| { + let bob: AccountId = SOME_ASSET_ADMIN.into(); + let treasury = RelayTreasuryAccount::get(); + let asset_1: u32 = 1; + let native_location = WestendLocation::get(); + let asset_1_location = + AssetIdForTrustBackedAssetsConvert::convert_back(&asset_1).unwrap(); + // bob's initial balance for native and `asset1` assets. + let initial_balance = 200 * UNITS; + // liquidity for both arms of (native, asset1) pool. + let pool_liquidity = 100 * UNITS; + + assert_ok!(>::create(asset_1, bob.clone(), true, 10)); + + assert_ok!(Assets::mint_into(asset_1, &bob, initial_balance)); + assert_ok!(Balances::mint_into(&bob, initial_balance)); + assert_ok!(Balances::mint_into(&treasury, initial_balance)); + + assert_ok!(AssetConversion::create_pool( + RuntimeHelper::origin_of(bob.clone()), + Box::new(native_location), + Box::new(asset_1_location) + )); + + assert_ok!(AssetConversion::add_liquidity( + RuntimeHelper::origin_of(bob.clone()), + Box::new(native_location), + Box::new(asset_1_location), + pool_liquidity, + pool_liquidity, + 1, + 1, + bob, + )); + + let weight = Weight::from_parts(4_000_000_000, 0); + let fee = WeightToFee::weight_to_fee(&weight); + let actual_fee = + AssetConversion::get_amount_in(&fee, &pool_liquidity, &pool_liquidity).unwrap(); + let extra_amount = 100; + let asset: MultiAsset = (asset_1_location, actual_fee + extra_amount).into(); + let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None }; + let total_issuance = Assets::total_issuance(asset_1); + + let mut trader = ::Trader::new(); + let unused_assets = trader.buy_weight(weight, asset.into(), &ctx).expect("Expected Ok"); + + assert_eq!(Balances::balance(&treasury), initial_balance); + drop(trader); + assert_eq!(Balances::balance(&treasury), initial_balance + fee); + let unused_amount = + unused_assets.fungible.get(&asset_1_location.into()).map_or(0, |a| *a); + assert_eq!(unused_amount, extra_amount); + assert_eq!(Assets::total_issuance(asset_1), total_issuance + actual_fee); + }) +} + +#[test] +fn test_asset_swap_foreign_xcm_trader() { + ExtBuilder::::default() + .with_collators(vec![AccountId::from(ALICE)]) + .with_session_keys(vec![( + AccountId::from(ALICE), + AccountId::from(ALICE), + SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) }, + )]) + .build() + .execute_with(|| { + let bob: AccountId = SOME_ASSET_ADMIN.into(); + let treasury = RelayTreasuryAccount::get(); + // let asset_1: u32 = 1; + let native_location = WestendLocation::get(); + let foreign_location = + MultiLocation { parents: 1, interior: X2(Parachain(1234), GeneralIndex(12345)) }; + // bob's initial balance for native and `asset1` assets. + let initial_balance = 200 * UNITS; + // liquidity for both arms of (native, asset1) pool. + let pool_liquidity = 100 * UNITS; + + assert_ok!(>::create( + foreign_location, + bob.clone(), + true, + 10 + )); + + assert_ok!(ForeignAssets::mint_into(foreign_location, &bob, initial_balance)); + assert_ok!(Balances::mint_into(&bob, initial_balance)); + assert_ok!(Balances::mint_into(&treasury, initial_balance)); + + assert_ok!(AssetConversion::create_pool( + RuntimeHelper::origin_of(bob.clone()), + Box::new(native_location), + Box::new(foreign_location) + )); + + assert_ok!(AssetConversion::add_liquidity( + RuntimeHelper::origin_of(bob.clone()), + Box::new(native_location), + Box::new(foreign_location), + pool_liquidity, + pool_liquidity, + 1, + 1, + bob, + )); + + let weight = Weight::from_parts(4_000_000_000, 0); + let fee = WeightToFee::weight_to_fee(&weight); + let actual_fee = + AssetConversion::get_amount_in(&fee, &pool_liquidity, &pool_liquidity).unwrap(); + let extra_amount = 100; + let asset: MultiAsset = (foreign_location, actual_fee + extra_amount).into(); + let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None }; + let total_issuance = ForeignAssets::total_issuance(foreign_location); + + let mut trader = ::Trader::new(); + let unused_assets = trader.buy_weight(weight, asset.into(), &ctx).expect("Expected Ok"); + + assert_eq!(Balances::balance(&treasury), initial_balance); + drop(trader); + assert_eq!(Balances::balance(&treasury), initial_balance + fee); + let unused_amount = + unused_assets.fungible.get(&foreign_location.into()).map_or(0, |a| *a); + assert_eq!(unused_amount, extra_amount); + assert_eq!( + ForeignAssets::total_issuance(foreign_location), + total_issuance + actual_fee + ); + }) +} + // TODO: rewrite for a new weight trader #[ignore] #[test] diff --git a/cumulus/parachains/runtimes/assets/common/src/lib.rs b/cumulus/parachains/runtimes/assets/common/src/lib.rs index 15327f51b2a9..b0e471439297 100644 --- a/cumulus/parachains/runtimes/assets/common/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/common/src/lib.rs @@ -47,6 +47,16 @@ pub type TrustBackedAssetsConvertedConcreteId = + MatchedConvertedConcreteId< + MultiLocationForAssetId, + Balance, + StartsWith, + Identity, + JustTry, + >; + /// [`MatchedConvertedConcreteId`] converter dedicated for storing `AssetId` as `MultiLocation`. pub type MultiLocationConvertedConcreteId = MatchedConvertedConcreteId< From b49dce0f908bb56977652815bf129617af2a3950 Mon Sep 17 00:00:00 2001 From: muharem Date: Thu, 16 Nov 2023 12:27:14 +0100 Subject: [PATCH 82/98] more tests --- .../assets/asset-hub-westend/tests/tests.rs | 169 +++++++++++++++--- 1 file changed, 142 insertions(+), 27 deletions(-) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs index 3a42f07b0836..73df30424951 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs @@ -27,8 +27,8 @@ use asset_hub_westend_runtime::{ }; pub use asset_hub_westend_runtime::{ xcm_config::{CheckingAccount, TrustBackedAssetsPalletLocation, XcmConfig}, - AssetConversion, AssetDeposit, Assets, Balances, ExistentialDeposit, ForeignAssets, - ForeignAssetsInstance, ParachainSystem, Runtime, SessionKeys, System, + AssetConversion, AssetDeposit, Assets, Balances, CollatorSelection, ExistentialDeposit, + ForeignAssets, ForeignAssetsInstance, ParachainSystem, Runtime, SessionKeys, System, TrustBackedAssetsInstance, }; use asset_test_utils::{CollatorSessionKeys, ExtBuilder, XcmReceivedFrom}; @@ -74,7 +74,7 @@ fn collator_session_keys() -> CollatorSessionKeys { } #[test] -fn test_asset_swap_local_xcm_trader() { +fn test_buy_and_refund_weight_in_native() { ExtBuilder::::default() .with_collators(vec![AccountId::from(ALICE)]) .with_session_keys(vec![( @@ -85,7 +85,65 @@ fn test_asset_swap_local_xcm_trader() { .build() .execute_with(|| { let bob: AccountId = SOME_ASSET_ADMIN.into(); - let treasury = RelayTreasuryAccount::get(); + let staking_pot = CollatorSelection::account_id(); + let native_location = WestendLocation::get(); + let initial_balance = 200 * UNITS; + + assert_ok!(Balances::mint_into(&bob, initial_balance)); + assert_ok!(Balances::mint_into(&staking_pot, initial_balance)); + + // keep initial total issuance to assert later. + let total_issuance = Balances::total_issuance(); + + // prepare input to buy weight. + let weight = Weight::from_parts(4_000_000_000, 0); + let fee = WeightToFee::weight_to_fee(&weight); + let extra_amount = 100; + let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None }; + let payment: MultiAsset = (native_location, fee + extra_amount).into(); + + // init trader and buy weight. + let mut trader = ::Trader::new(); + let unused_asset = + trader.buy_weight(weight, payment.into(), &ctx).expect("Expected Ok"); + + // assert. + let unused_amount = + unused_asset.fungible.get(&native_location.into()).map_or(0, |a| *a); + assert_eq!(unused_amount, extra_amount); + assert_eq!(Balances::total_issuance(), total_issuance); + + // prepare input to refund weight. + let refund_weight = Weight::from_parts(1_000_000_000, 0); + let refund = WeightToFee::weight_to_fee(&refund_weight); + + // refund. + let actual_refund = trader.refund_weight(refund_weight, &ctx).unwrap(); + assert_eq!(actual_refund, (native_location, refund).into()); + + // assert. + assert_eq!(Balances::balance(&staking_pot), initial_balance); + // only after `trader` is dropped we expect the fee to be resolved into the treasury + // account. + drop(trader); + assert_eq!(Balances::balance(&staking_pot), initial_balance + fee - refund); + assert_eq!(Balances::total_issuance(), total_issuance + fee - refund); + }) +} + +#[test] +fn test_buy_and_refund_weight_with_swap_local_asset_xcm_trader() { + ExtBuilder::::default() + .with_collators(vec![AccountId::from(ALICE)]) + .with_session_keys(vec![( + AccountId::from(ALICE), + AccountId::from(ALICE), + SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) }, + )]) + .build() + .execute_with(|| { + let bob: AccountId = SOME_ASSET_ADMIN.into(); + let treasury_account = RelayTreasuryAccount::get(); let asset_1: u32 = 1; let native_location = WestendLocation::get(); let asset_1_location = @@ -95,11 +153,12 @@ fn test_asset_swap_local_xcm_trader() { // liquidity for both arms of (native, asset1) pool. let pool_liquidity = 100 * UNITS; + // init asset, balances and pool. assert_ok!(>::create(asset_1, bob.clone(), true, 10)); assert_ok!(Assets::mint_into(asset_1, &bob, initial_balance)); assert_ok!(Balances::mint_into(&bob, initial_balance)); - assert_ok!(Balances::mint_into(&treasury, initial_balance)); + assert_ok!(Balances::mint_into(&treasury_account, initial_balance)); assert_ok!(AssetConversion::create_pool( RuntimeHelper::origin_of(bob.clone()), @@ -118,30 +177,58 @@ fn test_asset_swap_local_xcm_trader() { bob, )); + // keep initial total issuance to assert later. + let asset_total_issuance = Assets::total_issuance(asset_1); + let native_total_issuance = Balances::total_issuance(); + + // prepare input to buy weight. let weight = Weight::from_parts(4_000_000_000, 0); let fee = WeightToFee::weight_to_fee(&weight); - let actual_fee = + let asset_fee = AssetConversion::get_amount_in(&fee, &pool_liquidity, &pool_liquidity).unwrap(); let extra_amount = 100; - let asset: MultiAsset = (asset_1_location, actual_fee + extra_amount).into(); let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None }; - let total_issuance = Assets::total_issuance(asset_1); + let payment: MultiAsset = (asset_1_location, asset_fee + extra_amount).into(); + // init trader and buy weight. let mut trader = ::Trader::new(); - let unused_assets = trader.buy_weight(weight, asset.into(), &ctx).expect("Expected Ok"); + let unused_asset = + trader.buy_weight(weight, payment.into(), &ctx).expect("Expected Ok"); - assert_eq!(Balances::balance(&treasury), initial_balance); - drop(trader); - assert_eq!(Balances::balance(&treasury), initial_balance + fee); + // assert. let unused_amount = - unused_assets.fungible.get(&asset_1_location.into()).map_or(0, |a| *a); + unused_asset.fungible.get(&asset_1_location.into()).map_or(0, |a| *a); assert_eq!(unused_amount, extra_amount); - assert_eq!(Assets::total_issuance(asset_1), total_issuance + actual_fee); + assert_eq!(Assets::total_issuance(asset_1), asset_total_issuance + asset_fee); + + // prepare input to refund weight. + let refund_weight = Weight::from_parts(1_000_000_000, 0); + let refund = WeightToFee::weight_to_fee(&refund_weight); + let (reserve1, reserve2) = + AssetConversion::get_reserves(native_location, asset_1_location).unwrap(); + let asset_refund = + AssetConversion::get_amount_out(&refund, &reserve1, &reserve2).unwrap(); + + // refund. + let actual_refund = trader.refund_weight(refund_weight, &ctx).unwrap(); + assert_eq!(actual_refund, (asset_1_location, asset_refund).into()); + + // assert. + assert_eq!(Balances::balance(&treasury_account), initial_balance); + // only after `trader` is dropped we expect the fee to be resolved into the treasury + // account. + drop(trader); + assert_eq!(Balances::balance(&treasury_account), initial_balance + fee - refund); + assert_eq!( + Assets::total_issuance(asset_1), + asset_total_issuance + asset_fee - asset_refund + ); + assert_eq!(Balances::total_issuance(), native_total_issuance); }) } #[test] -fn test_asset_swap_foreign_xcm_trader() { +fn test_buy_and_refund_weight_with_swap_foreign_asset_xcm_trader() { ExtBuilder::::default() .with_collators(vec![AccountId::from(ALICE)]) .with_session_keys(vec![( @@ -152,8 +239,7 @@ fn test_asset_swap_foreign_xcm_trader() { .build() .execute_with(|| { let bob: AccountId = SOME_ASSET_ADMIN.into(); - let treasury = RelayTreasuryAccount::get(); - // let asset_1: u32 = 1; + let treasury_account = RelayTreasuryAccount::get(); let native_location = WestendLocation::get(); let foreign_location = MultiLocation { parents: 1, interior: X2(Parachain(1234), GeneralIndex(12345)) }; @@ -162,6 +248,7 @@ fn test_asset_swap_foreign_xcm_trader() { // liquidity for both arms of (native, asset1) pool. let pool_liquidity = 100 * UNITS; + // init asset, balances and pool. assert_ok!(>::create( foreign_location, bob.clone(), @@ -171,7 +258,7 @@ fn test_asset_swap_foreign_xcm_trader() { assert_ok!(ForeignAssets::mint_into(foreign_location, &bob, initial_balance)); assert_ok!(Balances::mint_into(&bob, initial_balance)); - assert_ok!(Balances::mint_into(&treasury, initial_balance)); + assert_ok!(Balances::mint_into(&treasury_account, initial_balance)); assert_ok!(AssetConversion::create_pool( RuntimeHelper::origin_of(bob.clone()), @@ -190,28 +277,56 @@ fn test_asset_swap_foreign_xcm_trader() { bob, )); + // keep initial total issuance to assert later. + let asset_total_issuance = ForeignAssets::total_issuance(foreign_location); + let native_total_issuance = Balances::total_issuance(); + + // prepare input to buy weight. let weight = Weight::from_parts(4_000_000_000, 0); let fee = WeightToFee::weight_to_fee(&weight); - let actual_fee = + let asset_fee = AssetConversion::get_amount_in(&fee, &pool_liquidity, &pool_liquidity).unwrap(); let extra_amount = 100; - let asset: MultiAsset = (foreign_location, actual_fee + extra_amount).into(); let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None }; - let total_issuance = ForeignAssets::total_issuance(foreign_location); + let payment: MultiAsset = (foreign_location, asset_fee + extra_amount).into(); + // init trader and buy weight. let mut trader = ::Trader::new(); - let unused_assets = trader.buy_weight(weight, asset.into(), &ctx).expect("Expected Ok"); + let unused_asset = + trader.buy_weight(weight, payment.into(), &ctx).expect("Expected Ok"); - assert_eq!(Balances::balance(&treasury), initial_balance); - drop(trader); - assert_eq!(Balances::balance(&treasury), initial_balance + fee); + // assert. let unused_amount = - unused_assets.fungible.get(&foreign_location.into()).map_or(0, |a| *a); + unused_asset.fungible.get(&foreign_location.into()).map_or(0, |a| *a); assert_eq!(unused_amount, extra_amount); assert_eq!( ForeignAssets::total_issuance(foreign_location), - total_issuance + actual_fee + asset_total_issuance + asset_fee + ); + + // prepare input to refund weight. + let refund_weight = Weight::from_parts(1_000_000_000, 0); + let refund = WeightToFee::weight_to_fee(&refund_weight); + let (reserve1, reserve2) = + AssetConversion::get_reserves(native_location, foreign_location).unwrap(); + let asset_refund = + AssetConversion::get_amount_out(&refund, &reserve1, &reserve2).unwrap(); + + // refund. + let actual_refund = trader.refund_weight(refund_weight, &ctx).unwrap(); + assert_eq!(actual_refund, (foreign_location, asset_refund).into()); + + // assert. + assert_eq!(Balances::balance(&treasury_account), initial_balance); + // only after `trader` is dropped we expect the fee to be resolved into the treasury + // account. + drop(trader); + assert_eq!(Balances::balance(&treasury_account), initial_balance + fee - refund); + assert_eq!( + ForeignAssets::total_issuance(foreign_location), + asset_total_issuance + asset_fee - asset_refund ); + assert_eq!(Balances::total_issuance(), native_total_issuance); }) } From 34dfcce340396d84a63af1356d946db093c2f3f2 Mon Sep 17 00:00:00 2001 From: muharem Date: Thu, 16 Nov 2023 14:47:38 +0100 Subject: [PATCH 83/98] resolve to staking pot --- .../asset-hub-westend/src/xcm_config.rs | 22 +++++-------------- .../assets/asset-hub-westend/tests/tests.rs | 18 +++++++-------- 2 files changed, 14 insertions(+), 26 deletions(-) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs index b0bb9c5199fa..71fd9a4e663a 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs @@ -15,11 +15,10 @@ use super::{ AccountId, AllPalletsWithSystem, Assets, Authorship, Balance, Balances, BaseDeliveryFee, - FeeAssetId, ParachainInfo, ParachainSystem, PolkadotXcm, PoolAssets, Runtime, RuntimeCall, - RuntimeEvent, RuntimeOrigin, TransactionByteFee, TrustBackedAssetsInstance, WeightToFee, - XcmpQueue, + CollatorSelection, FeeAssetId, ForeignAssets, ParachainInfo, ParachainSystem, PolkadotXcm, + PoolAssets, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, TransactionByteFee, + TrustBackedAssetsInstance, WeightToFee, XcmpQueue, }; -use crate::ForeignAssets; use assets_common::{ local_and_foreign_assets::MatchesLocalAndForeignAssetsMultiLocation, matching::{FromSiblingParachain, IsForeignConcreteAsset}, @@ -74,10 +73,7 @@ parameter_types! { pub PoolAssetsPalletLocation: MultiLocation = PalletInstance(::index() as u8).into(); pub CheckingAccount: AccountId = PolkadotXcm::check_account(); - /// The Relay Chain Treasury pallet's account on the current chain, derived using [`PalletLocationToAccountId`]. - // Proof of correctness: [`self::ensure_relay_treasury_account_correct()`]. - pub RelayTreasuryAccount: AccountId = - AccountId::from(hex_literal::hex!("8bb46fbc6413c0f273b1b09af7b83f30695801958b9151a8f899f468f80064b1")); + pub StakingPot: AccountId = CollatorSelection::account_id(); pub TreasuryAccount: Option = Some(TREASURY_PALLET_ID.into_account_truncating()); } @@ -569,7 +565,7 @@ impl xcm_executor::Config for XcmConfig { TrustBackedAssetsAsMultiLocation, ForeignAssetsConvertedConcreteId, ), - ResolveAssetTo, + ResolveAssetTo, AccountId, >, ); @@ -660,11 +656,3 @@ impl pallet_assets::BenchmarkHelper for XcmBenchmarkHelper { MultiLocation { parents: 1, interior: X1(Parachain(id)) } } } - -#[test] -fn ensure_relay_treasury_account_correct() { - use xcm_executor::traits::ConvertLocation; - let location = MultiLocation::new(1, X1(Junction::PalletInstance(37))); - let account = PalletLocationToAccountId::convert_location(&location).unwrap(); - assert_eq!(account, RelayTreasuryAccount::get()); -} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs index 73df30424951..9805600e80ab 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs @@ -20,7 +20,7 @@ use asset_hub_westend_runtime::{ xcm_config::{ AssetFeeAsExistentialDepositMultiplierFeeCharger, ForeignCreatorsSovereignAccountOf, - RelayTreasuryAccount, WestendLocation, + WestendLocation, }, AllPalletsWithoutSystem, MetadataDepositBase, MetadataDepositPerByte, RuntimeCall, RuntimeEvent, @@ -143,7 +143,7 @@ fn test_buy_and_refund_weight_with_swap_local_asset_xcm_trader() { .build() .execute_with(|| { let bob: AccountId = SOME_ASSET_ADMIN.into(); - let treasury_account = RelayTreasuryAccount::get(); + let staking_pot = CollatorSelection::account_id(); let asset_1: u32 = 1; let native_location = WestendLocation::get(); let asset_1_location = @@ -158,7 +158,7 @@ fn test_buy_and_refund_weight_with_swap_local_asset_xcm_trader() { assert_ok!(Assets::mint_into(asset_1, &bob, initial_balance)); assert_ok!(Balances::mint_into(&bob, initial_balance)); - assert_ok!(Balances::mint_into(&treasury_account, initial_balance)); + assert_ok!(Balances::mint_into(&staking_pot, initial_balance)); assert_ok!(AssetConversion::create_pool( RuntimeHelper::origin_of(bob.clone()), @@ -214,11 +214,11 @@ fn test_buy_and_refund_weight_with_swap_local_asset_xcm_trader() { assert_eq!(actual_refund, (asset_1_location, asset_refund).into()); // assert. - assert_eq!(Balances::balance(&treasury_account), initial_balance); + assert_eq!(Balances::balance(&staking_pot), initial_balance); // only after `trader` is dropped we expect the fee to be resolved into the treasury // account. drop(trader); - assert_eq!(Balances::balance(&treasury_account), initial_balance + fee - refund); + assert_eq!(Balances::balance(&staking_pot), initial_balance + fee - refund); assert_eq!( Assets::total_issuance(asset_1), asset_total_issuance + asset_fee - asset_refund @@ -239,7 +239,7 @@ fn test_buy_and_refund_weight_with_swap_foreign_asset_xcm_trader() { .build() .execute_with(|| { let bob: AccountId = SOME_ASSET_ADMIN.into(); - let treasury_account = RelayTreasuryAccount::get(); + let staking_pot = CollatorSelection::account_id(); let native_location = WestendLocation::get(); let foreign_location = MultiLocation { parents: 1, interior: X2(Parachain(1234), GeneralIndex(12345)) }; @@ -258,7 +258,7 @@ fn test_buy_and_refund_weight_with_swap_foreign_asset_xcm_trader() { assert_ok!(ForeignAssets::mint_into(foreign_location, &bob, initial_balance)); assert_ok!(Balances::mint_into(&bob, initial_balance)); - assert_ok!(Balances::mint_into(&treasury_account, initial_balance)); + assert_ok!(Balances::mint_into(&staking_pot, initial_balance)); assert_ok!(AssetConversion::create_pool( RuntimeHelper::origin_of(bob.clone()), @@ -317,11 +317,11 @@ fn test_buy_and_refund_weight_with_swap_foreign_asset_xcm_trader() { assert_eq!(actual_refund, (foreign_location, asset_refund).into()); // assert. - assert_eq!(Balances::balance(&treasury_account), initial_balance); + assert_eq!(Balances::balance(&staking_pot), initial_balance); // only after `trader` is dropped we expect the fee to be resolved into the treasury // account. drop(trader); - assert_eq!(Balances::balance(&treasury_account), initial_balance + fee - refund); + assert_eq!(Balances::balance(&staking_pot), initial_balance + fee - refund); assert_eq!( ForeignAssets::total_issuance(foreign_location), asset_total_issuance + asset_fee - asset_refund From bc12fc182befd3adef809834fbdc3a7df01ffd2d Mon Sep 17 00:00:00 2001 From: muharem Date: Thu, 16 Nov 2023 14:49:36 +0100 Subject: [PATCH 84/98] remove outdated tests --- .../assets/asset-hub-westend/tests/tests.rs | 356 +----------------- 1 file changed, 6 insertions(+), 350 deletions(-) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs index 9805600e80ab..b8095b69f5c8 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs @@ -17,25 +17,21 @@ //! Tests for the Westmint (Westend Assets Hub) chain. -use asset_hub_westend_runtime::{ - xcm_config::{ - AssetFeeAsExistentialDepositMultiplierFeeCharger, ForeignCreatorsSovereignAccountOf, - WestendLocation, - }, - AllPalletsWithoutSystem, MetadataDepositBase, MetadataDepositPerByte, RuntimeCall, - RuntimeEvent, -}; pub use asset_hub_westend_runtime::{ xcm_config::{CheckingAccount, TrustBackedAssetsPalletLocation, XcmConfig}, AssetConversion, AssetDeposit, Assets, Balances, CollatorSelection, ExistentialDeposit, ForeignAssets, ForeignAssetsInstance, ParachainSystem, Runtime, SessionKeys, System, TrustBackedAssetsInstance, }; +use asset_hub_westend_runtime::{ + xcm_config::{ForeignCreatorsSovereignAccountOf, WestendLocation}, + AllPalletsWithoutSystem, MetadataDepositBase, MetadataDepositPerByte, RuntimeCall, + RuntimeEvent, +}; use asset_test_utils::{CollatorSessionKeys, ExtBuilder, XcmReceivedFrom}; use codec::{Decode, DecodeLimit, Encode}; -use cumulus_primitives_utility::ChargeWeightInFungibles; use frame_support::{ - assert_noop, assert_ok, + assert_ok, traits::{ fungible::{Inspect, Mutate}, fungibles::{ @@ -330,346 +326,6 @@ fn test_buy_and_refund_weight_with_swap_foreign_asset_xcm_trader() { }) } -// TODO: rewrite for a new weight trader -#[ignore] -#[test] -fn test_asset_xcm_trader() { - ExtBuilder::::default() - .with_collators(vec![AccountId::from(ALICE)]) - .with_session_keys(vec![( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) }, - )]) - .build() - .execute_with(|| { - // We need root origin to create a sufficient asset - let minimum_asset_balance = 3333333_u128; - let local_asset_id = 1; - assert_ok!(Assets::force_create( - RuntimeHelper::root_origin(), - local_asset_id.into(), - AccountId::from(ALICE).into(), - true, - minimum_asset_balance - )); - - // We first mint enough asset for the account to exist for assets - assert_ok!(Assets::mint( - RuntimeHelper::origin_of(AccountId::from(ALICE)), - local_asset_id.into(), - AccountId::from(ALICE).into(), - minimum_asset_balance - )); - - // get asset id as multilocation - let asset_multilocation = - AssetIdForTrustBackedAssetsConvert::convert_back(&local_asset_id).unwrap(); - - // Set Alice as block author, who will receive fees - RuntimeHelper::run_to_block(2, AccountId::from(ALICE)); - - // We are going to buy 4e9 weight - let bought = Weight::from_parts(4_000_000_000u64, 0); - - // Lets calculate amount needed - let asset_amount_needed = - AssetFeeAsExistentialDepositMultiplierFeeCharger::charge_weight_in_fungibles( - local_asset_id, - bought, - ) - .expect("failed to compute"); - - // Lets pay with: asset_amount_needed + asset_amount_extra - let asset_amount_extra = 100_u128; - let asset: MultiAsset = - (asset_multilocation, asset_amount_needed + asset_amount_extra).into(); - - let mut trader = ::Trader::new(); - let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None }; - - // Lets buy_weight and make sure buy_weight does not return an error - let unused_assets = trader.buy_weight(bought, asset.into(), &ctx).expect("Expected Ok"); - // Check whether a correct amount of unused assets is returned - assert_ok!( - unused_assets.ensure_contains(&(asset_multilocation, asset_amount_extra).into()) - ); - - // Drop trader - drop(trader); - - // Make sure author(Alice) has received the amount - assert_eq!( - Assets::balance(local_asset_id, AccountId::from(ALICE)), - minimum_asset_balance + asset_amount_needed - ); - - // We also need to ensure the total supply increased - assert_eq!( - Assets::total_supply(local_asset_id), - minimum_asset_balance + asset_amount_needed - ); - }); -} - -// TODO: rewrite for a new weight trader -#[ignore] -#[test] -fn test_asset_xcm_trader_with_refund() { - ExtBuilder::::default() - .with_collators(vec![AccountId::from(ALICE)]) - .with_session_keys(vec![( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) }, - )]) - .build() - .execute_with(|| { - // We need root origin to create a sufficient asset - // We set existential deposit to be identical to the one for Balances first - assert_ok!(Assets::force_create( - RuntimeHelper::root_origin(), - 1.into(), - AccountId::from(ALICE).into(), - true, - ExistentialDeposit::get() - )); - - // We first mint enough asset for the account to exist for assets - assert_ok!(Assets::mint( - RuntimeHelper::origin_of(AccountId::from(ALICE)), - 1.into(), - AccountId::from(ALICE).into(), - ExistentialDeposit::get() - )); - - let mut trader = ::Trader::new(); - let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None }; - - // Set Alice as block author, who will receive fees - RuntimeHelper::run_to_block(2, AccountId::from(ALICE)); - - // We are going to buy 4e9 weight - let bought = Weight::from_parts(4_000_000_000u64, 0); - let asset_multilocation = AssetIdForTrustBackedAssetsConvert::convert_back(&1).unwrap(); - - // lets calculate amount needed - let amount_bought = WeightToFee::weight_to_fee(&bought); - - let asset: MultiAsset = (asset_multilocation, amount_bought).into(); - - // Make sure buy_weight does not return an error - assert_ok!(trader.buy_weight(bought, asset.clone().into(), &ctx)); - - // Make sure again buy_weight does return an error - // This assert relies on the fact, that we use `TakeFirstAssetTrader` in `WeightTrader` - // tuple chain, which cannot be called twice - assert_noop!(trader.buy_weight(bought, asset.into(), &ctx), XcmError::TooExpensive); - - // We actually use half of the weight - let weight_used = bought / 2; - - // Make sure refurnd works. - let amount_refunded = WeightToFee::weight_to_fee(&(bought - weight_used)); - - assert_eq!( - trader.refund_weight(bought - weight_used, &ctx), - Some((asset_multilocation, amount_refunded).into()) - ); - - // Drop trader - drop(trader); - - // We only should have paid for half of the bought weight - let fees_paid = WeightToFee::weight_to_fee(&weight_used); - - assert_eq!( - Assets::balance(1, AccountId::from(ALICE)), - ExistentialDeposit::get() + fees_paid - ); - - // We also need to ensure the total supply increased - assert_eq!(Assets::total_supply(1), ExistentialDeposit::get() + fees_paid); - }); -} - -#[test] -fn test_asset_xcm_trader_refund_not_possible_since_amount_less_than_ed() { - ExtBuilder::::default() - .with_collators(vec![AccountId::from(ALICE)]) - .with_session_keys(vec![( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) }, - )]) - .build() - .execute_with(|| { - // We need root origin to create a sufficient asset - // We set existential deposit to be identical to the one for Balances first - assert_ok!(Assets::force_create( - RuntimeHelper::root_origin(), - 1.into(), - AccountId::from(ALICE).into(), - true, - ExistentialDeposit::get() - )); - - let mut trader = ::Trader::new(); - let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None }; - - // Set Alice as block author, who will receive fees - RuntimeHelper::run_to_block(2, AccountId::from(ALICE)); - - // We are going to buy 5e9 weight - let bought = Weight::from_parts(500_000_000u64, 0); - - let asset_multilocation = AssetIdForTrustBackedAssetsConvert::convert_back(&1).unwrap(); - - let amount_bought = WeightToFee::weight_to_fee(&bought); - - assert!( - amount_bought < ExistentialDeposit::get(), - "we are testing what happens when the amount does not exceed ED" - ); - - let asset: MultiAsset = (asset_multilocation, amount_bought).into(); - - // Buy weight should return an error - assert_noop!(trader.buy_weight(bought, asset.into(), &ctx), XcmError::TooExpensive); - - // not credited since the ED is higher than this value - assert_eq!(Assets::balance(1, AccountId::from(ALICE)), 0); - - // We also need to ensure the total supply did not increase - assert_eq!(Assets::total_supply(1), 0); - }); -} - -// TODO: rewrite for a new weight trader -#[ignore] -#[test] -fn test_that_buying_ed_refund_does_not_refund() { - ExtBuilder::::default() - .with_collators(vec![AccountId::from(ALICE)]) - .with_session_keys(vec![( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) }, - )]) - .build() - .execute_with(|| { - // We need root origin to create a sufficient asset - // We set existential deposit to be identical to the one for Balances first - assert_ok!(Assets::force_create( - RuntimeHelper::root_origin(), - 1.into(), - AccountId::from(ALICE).into(), - true, - ExistentialDeposit::get() - )); - - let mut trader = ::Trader::new(); - let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None }; - - // Set Alice as block author, who will receive fees - RuntimeHelper::run_to_block(2, AccountId::from(ALICE)); - - let bought = Weight::from_parts(500_000_000u64, 0); - - let asset_multilocation = AssetIdForTrustBackedAssetsConvert::convert_back(&1).unwrap(); - - let amount_bought = WeightToFee::weight_to_fee(&bought); - - assert!( - amount_bought < ExistentialDeposit::get(), - "we are testing what happens when the amount does not exceed ED" - ); - - // We know we will have to buy at least ED, so lets make sure first it will - // fail with a payment of less than ED - let asset: MultiAsset = (asset_multilocation, amount_bought).into(); - assert_noop!(trader.buy_weight(bought, asset.into(), &ctx), XcmError::TooExpensive); - - // Now lets buy ED at least - let asset: MultiAsset = (asset_multilocation, ExistentialDeposit::get()).into(); - - // Buy weight should work - assert_ok!(trader.buy_weight(bought, asset.into(), &ctx)); - - // Should return None. We have a specific check making sure we dont go below ED for - // drop payment - assert_eq!(trader.refund_weight(bought, &ctx), None); - - // Drop trader - drop(trader); - - // Make sure author(Alice) has received the amount - assert_eq!(Assets::balance(1, AccountId::from(ALICE)), ExistentialDeposit::get()); - - // We also need to ensure the total supply increased - assert_eq!(Assets::total_supply(1), ExistentialDeposit::get()); - }); -} - -#[test] -fn test_asset_xcm_trader_not_possible_for_non_sufficient_assets() { - ExtBuilder::::default() - .with_collators(vec![AccountId::from(ALICE)]) - .with_session_keys(vec![( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) }, - )]) - .build() - .execute_with(|| { - // Create a non-sufficient asset with specific existential deposit - let minimum_asset_balance = 1_000_000_u128; - assert_ok!(Assets::force_create( - RuntimeHelper::root_origin(), - 1.into(), - AccountId::from(ALICE).into(), - false, - minimum_asset_balance - )); - - // We first mint enough asset for the account to exist for assets - assert_ok!(Assets::mint( - RuntimeHelper::origin_of(AccountId::from(ALICE)), - 1.into(), - AccountId::from(ALICE).into(), - minimum_asset_balance - )); - - let mut trader = ::Trader::new(); - let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None }; - - // Set Alice as block author, who will receive fees - RuntimeHelper::run_to_block(2, AccountId::from(ALICE)); - - // We are going to buy 4e9 weight - let bought = Weight::from_parts(4_000_000_000u64, 0); - - // lets calculate amount needed - let asset_amount_needed = WeightToFee::weight_to_fee(&bought); - - let asset_multilocation = AssetIdForTrustBackedAssetsConvert::convert_back(&1).unwrap(); - - let asset: MultiAsset = (asset_multilocation, asset_amount_needed).into(); - - // Make sure again buy_weight does return an error - assert_noop!(trader.buy_weight(bought, asset.into(), &ctx), XcmError::TooExpensive); - - // Drop trader - drop(trader); - - // Make sure author(Alice) has NOT received the amount - assert_eq!(Assets::balance(1, AccountId::from(ALICE)), minimum_asset_balance); - - // We also need to ensure the total supply NOT increased - assert_eq!(Assets::total_supply(1), minimum_asset_balance); - }); -} - #[test] fn test_assets_balances_api_works() { use assets_common::runtime_api::runtime_decl_for_fungibles_api::FungiblesApi; From 8025a4092645865a3537b73d11a1b3f907fc5c50 Mon Sep 17 00:00:00 2001 From: muharem Date: Thu, 16 Nov 2023 16:42:04 +0100 Subject: [PATCH 85/98] remove ignore --- .../emulated/assets/asset-hub-westend/src/tests/send.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/send.rs b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/send.rs index 535b8157372d..e603af685bb5 100644 --- a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/send.rs +++ b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/send.rs @@ -30,8 +30,6 @@ fn send_transact_as_superuser_from_relay_to_system_para_works() { /// Parachain should be able to send XCM paying its fee with sufficient asset /// in the System Parachain -// TODO: rewrite for a new weight trader -#[ignore] #[test] fn send_xcm_from_para_to_system_para_paying_fee_with_assets_works() { let para_sovereign_account = AssetHubWestend::sovereign_account_id_of( From cdffd5acf80c9d95fba677177d09f2be2dcfe8a0 Mon Sep 17 00:00:00 2001 From: muharem Date: Fri, 17 Nov 2023 11:51:23 +0100 Subject: [PATCH 86/98] remove unused type alias --- .../runtimes/assets/asset-hub-westend/src/xcm_config.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs index 71fd9a4e663a..4df192796f3a 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs @@ -77,11 +77,6 @@ parameter_types! { pub TreasuryAccount: Option = Some(TREASURY_PALLET_ID.into_account_truncating()); } -// Foreign chain account alias into local accounts according to a hash of their standard -// description. -pub type PalletLocationToAccountId = - HashedDescription>; - /// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used /// when determining ownership of accounts for asset transacting and when attempting to use XCM /// `Transact` in order to determine the dispatch Origin. @@ -94,7 +89,7 @@ pub type LocationToAccountId = ( AccountId32Aliases, // Foreign chain account alias into local accounts according to a hash of their standard // description. - PalletLocationToAccountId, + HashedDescription>, ); /// Means for transacting the native currency on this chain. From 0a89934b6289845203ccfc083e9172786dbf92da Mon Sep 17 00:00:00 2001 From: muharem Date: Mon, 20 Nov 2023 17:43:51 +0100 Subject: [PATCH 87/98] return error --- cumulus/primitives/utility/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cumulus/primitives/utility/src/lib.rs b/cumulus/primitives/utility/src/lib.rs index 29e5b0d40937..a9add86c2a62 100644 --- a/cumulus/primitives/utility/src/lib.rs +++ b/cumulus/primitives/utility/src/lib.rs @@ -420,7 +420,7 @@ impl< "`total_fee.asset` must be equal to `credit_out.asset`", (self.total_fee.asset(), credit_out.asset()) ); - return Ok(payment) + return Err(XcmError::FeesNotMet) }, _ => (), }; From 673ed3ee999bb1121df4ab5e02e2525039d448a1 Mon Sep 17 00:00:00 2001 From: muharem Date: Wed, 20 Dec 2023 17:34:15 +0100 Subject: [PATCH 88/98] remove dublicate tests --- substrate/frame/assets/src/tests.rs | 333 ---------------------------- 1 file changed, 333 deletions(-) diff --git a/substrate/frame/assets/src/tests.rs b/substrate/frame/assets/src/tests.rs index 3d5688e7fa87..e09648a51ecc 100644 --- a/substrate/frame/assets/src/tests.rs +++ b/substrate/frame/assets/src/tests.rs @@ -1777,336 +1777,3 @@ fn asset_destroy_refund_existence_deposit() { assert_eq!(Balances::reserved_balance(&admin), 0); }); } - -mod sets { - use super::*; - use frame_support::{ - parameter_types, - traits::{ - fungible, - fungible::ItemOf, - fungibles, - tokens::{ - fungibles::{ - Balanced as FungiblesBalanced, Create as FungiblesCreate, - Inspect as FungiblesInspect, Mutate as FungiblesMutate, - }, - Fortitude, Precision, Preservation, - }, - }, - }; - use sp_runtime::{traits::ConvertToValue, Either}; - - const FIRST_ASSET: u32 = 0; - const UNKNOWN_ASSET: u32 = 10; - - parameter_types! { - pub const LeftAsset: Either<(), u32> = Either::Left(()); - pub const RightAsset: Either = Either::Right(()); - pub const RightUnitAsset: Either<(), ()> = Either::Right(()); - } - - /// Implementation of the `fungible` traits through the [`ItemOf`] type, specifically for a - /// single asset class from [`T`] identified by [`FIRST_ASSET`]. - type FirstFungible = ItemOf, u64>; - /// Implementation of the `fungible` traits through the [`ItemOf`] type, specifically for a - /// single asset class from [`T`] identified by [`UNKNOWN_ASSET`]. - type UnknownFungible = ItemOf, u64>; - /// Implementation of `fungibles` traits using [`fungibles::UnionOf`] that exclusively utilizes - /// the [`FirstFungible`] from the left. - type LeftFungible = - fungible::UnionOf, T, ConvertToValue, (), u64>; - /// Implementation of `fungibles` traits using [`fungibles::UnionOf`] that exclusively utilizes - /// the [`LeftFungible`] from the right. - type RightFungible = fungible::UnionOf< - UnknownFungible, - LeftFungible, - ConvertToValue, - (), - u64, - >; - /// Implementation of `fungibles` traits using [`fungibles::UnionOf`] that exclusively utilizes - /// the [`RightFungible`] from the left. - type LeftFungibles = - fungibles::UnionOf, T, ConvertToValue, (), u64>; - /// Implementation of `fungibles` traits using [`fungibles::UnionOf`] that exclusively utilizes - /// the [`LeftFungibles`] from the right. - /// - /// By using this type, we can navigate through each branch of [`fungible::UnionOf`], - /// [`fungibles::UnionOf`], and [`ItemOf`] to access the underlying `fungibles::*` - /// implementation provided by the pallet. - type First = fungibles::UnionOf, ConvertToValue, (), u64>; - - #[test] - fn deposit_from_set_types_works() { - new_test_ext().execute_with(|| { - let asset1 = 0; - let account1 = 1; - let account2 = 2; - - assert_ok!(>::create(asset1, account1, true, 1)); - assert_ok!(Assets::mint_into(asset1, &account1, 100)); - - assert_eq!(First::::total_issuance(()), 100); - assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); - - let imb = First::::deposit((), &account2, 50, Precision::Exact).unwrap(); - assert_eq!(First::::balance((), &account2), 50); - assert_eq!(First::::total_issuance(()), 100); - - assert_eq!(imb.peek(), 50); - - let (imb1, imb2) = imb.split(30); - assert_eq!(imb1.peek(), 30); - assert_eq!(imb2.peek(), 20); - - drop(imb2); - assert_eq!(First::::total_issuance(()), 120); - - assert!(First::::settle(&account1, imb1, Preservation::Preserve).is_ok()); - assert_eq!(First::::balance((), &account1), 70); - assert_eq!(First::::balance((), &account2), 50); - assert_eq!(First::::total_issuance(()), 120); - - assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); - }); - } - - #[test] - fn issue_from_set_types_works() { - new_test_ext().execute_with(|| { - let asset1: u32 = 0; - let account1: u64 = 1; - - assert_ok!(>::create(asset1, account1, true, 1)); - assert_ok!(Assets::mint_into(asset1, &account1, 100)); - - assert_eq!(First::::balance((), &account1), 100); - assert_eq!(First::::total_issuance(()), 100); - assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); - - let imb = First::::issue((), 100); - assert_eq!(First::::total_issuance(()), 200); - assert_eq!(imb.peek(), 100); - - let (imb1, imb2) = imb.split(30); - assert_eq!(imb1.peek(), 30); - assert_eq!(imb2.peek(), 70); - - drop(imb2); - assert_eq!(First::::total_issuance(()), 130); - - assert!(First::::resolve(&account1, imb1).is_ok()); - assert_eq!(First::::balance((), &account1), 130); - assert_eq!(First::::total_issuance(()), 130); - - assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); - }); - } - - #[test] - fn pair_from_set_types_works() { - new_test_ext().execute_with(|| { - let asset1: u32 = 0; - let account1: u64 = 1; - - assert_ok!(>::create(asset1, account1, true, 1)); - assert_ok!(Assets::mint_into(asset1, &account1, 100)); - - assert_eq!(First::::balance((), &account1), 100); - assert_eq!(First::::total_issuance(()), 100); - assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); - - let (debt, credit) = First::::pair((), 100); - assert_eq!(First::::total_issuance(()), 100); - assert_eq!(debt.peek(), 100); - assert_eq!(credit.peek(), 100); - - let (debt1, debt2) = debt.split(30); - assert_eq!(debt1.peek(), 30); - assert_eq!(debt2.peek(), 70); - - drop(debt2); - assert_eq!(First::::total_issuance(()), 170); - - assert!(First::::settle(&account1, debt1, Preservation::Preserve).is_ok()); - assert_eq!(First::::balance((), &account1), 70); - assert_eq!(First::::total_issuance(()), 170); - - let (credit1, credit2) = credit.split(40); - assert_eq!(credit1.peek(), 40); - assert_eq!(credit2.peek(), 60); - - drop(credit2); - assert_eq!(First::::total_issuance(()), 110); - - assert!(First::::resolve(&account1, credit1).is_ok()); - assert_eq!(First::::balance((), &account1), 110); - assert_eq!(First::::total_issuance(()), 110); - - assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); - }); - } - - #[test] - fn rescind_from_set_types_works() { - new_test_ext().execute_with(|| { - let asset1: u32 = 0; - let account1: u64 = 1; - - assert_ok!(>::create(asset1, account1, true, 1)); - assert_ok!(Assets::mint_into(asset1, &account1, 100)); - - assert_eq!(First::::total_issuance(()), 100); - assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); - - let imb = First::::rescind((), 20); - assert_eq!(First::::total_issuance(()), 80); - - assert_eq!(imb.peek(), 20); - - let (imb1, imb2) = imb.split(15); - assert_eq!(imb1.peek(), 15); - assert_eq!(imb2.peek(), 5); - - drop(imb2); - assert_eq!(First::::total_issuance(()), 85); - - assert!(First::::settle(&account1, imb1, Preservation::Preserve).is_ok()); - assert_eq!(First::::balance((), &account1), 85); - assert_eq!(First::::total_issuance(()), 85); - - assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); - }); - } - - #[test] - fn resolve_from_set_types_works() { - new_test_ext().execute_with(|| { - let asset1: u32 = 0; - let account1: u64 = 1; - let account2: u64 = 2; - let ed = 11; - - assert_ok!(>::create(asset1, account1, true, ed)); - assert_ok!(Assets::mint_into(asset1, &account1, 100)); - - assert_eq!(First::::balance((), &account1), 100); - assert_eq!(First::::total_issuance(()), 100); - assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); - - let imb = First::::issue((), 100); - assert_eq!(First::::total_issuance(()), 200); - assert_eq!(imb.peek(), 100); - - let (imb1, imb2) = imb.split(10); - assert_eq!(imb1.peek(), 10); - assert_eq!(imb2.peek(), 90); - assert_eq!(First::::total_issuance(()), 200); - - // ed requirements not met. - let imb1 = First::::resolve(&account2, imb1).unwrap_err(); - assert_eq!(imb1.peek(), 10); - drop(imb1); - assert_eq!(First::::total_issuance(()), 190); - assert_eq!(First::::balance((), &account2), 0); - - // resolve to new account `2`. - assert_ok!(First::::resolve(&account2, imb2)); - assert_eq!(First::::total_issuance(()), 190); - assert_eq!(First::::balance((), &account2), 90); - - assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); - }); - } - - #[test] - fn settle_from_set_types_works() { - new_test_ext().execute_with(|| { - let asset1: u32 = 0; - let account1: u64 = 1; - let account2: u64 = 2; - let ed = 11; - - assert_ok!(>::create(asset1, account1, true, ed)); - assert_ok!(Assets::mint_into(asset1, &account1, 100)); - assert_ok!(Assets::mint_into(asset1, &account2, 100)); - - assert_eq!(First::::balance((), &account2), 100); - assert_eq!(First::::total_issuance(()), 200); - assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); - - let imb = First::::rescind((), 100); - assert_eq!(First::::total_issuance(()), 100); - assert_eq!(imb.peek(), 100); - - let (imb1, imb2) = imb.split(10); - assert_eq!(imb1.peek(), 10); - assert_eq!(imb2.peek(), 90); - assert_eq!(First::::total_issuance(()), 100); - - // ed requirements not met. - let imb2 = - First::::settle(&account2, imb2, Preservation::Preserve).unwrap_err(); - assert_eq!(imb2.peek(), 90); - drop(imb2); - assert_eq!(First::::total_issuance(()), 190); - assert_eq!(First::::balance((), &account2), 100); - - // settle to account `1`. - assert_ok!(First::::settle(&account2, imb1, Preservation::Preserve)); - assert_eq!(First::::total_issuance(()), 190); - assert_eq!(First::::balance((), &account2), 90); - - let imb = First::::rescind((), 85); - assert_eq!(First::::total_issuance(()), 105); - assert_eq!(imb.peek(), 85); - - // settle to account `1` and expect some dust. - let imb = First::::settle(&account2, imb, Preservation::Expendable).unwrap(); - assert_eq!(imb.peek(), 5); - assert_eq!(First::::total_issuance(()), 105); - assert_eq!(First::::balance((), &account2), 0); - - drop(imb); - assert_eq!(First::::total_issuance(()), 100); - - assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); - }); - } - - #[test] - fn withdraw_from_set_types_works() { - new_test_ext().execute_with(|| { - let asset1 = 0; - let account1 = 1; - let account2 = 2; - - assert_ok!(>::create(asset1, account1, true, 1)); - assert_ok!(Assets::mint_into(asset1, &account1, 100)); - assert_ok!(Assets::mint_into(asset1, &account2, 100)); - - assert_eq!(First::::total_issuance(()), 200); - assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); - - let imb = First::::withdraw( - (), - &account2, - 50, - Precision::Exact, - Preservation::Preserve, - Fortitude::Polite, - ) - .unwrap(); - assert_eq!(First::::balance((), &account2), 50); - assert_eq!(First::::total_issuance(()), 200); - - assert_eq!(imb.peek(), 50); - drop(imb); - assert_eq!(First::::total_issuance(()), 150); - assert_eq!(First::::balance((), &account2), 50); - - assert_eq!(First::::total_issuance(()), Assets::total_issuance(asset1)); - }); - } -} From dbc6a8df7a9efd9c746e31ca564778d19fea6094 Mon Sep 17 00:00:00 2001 From: muharem Date: Wed, 20 Dec 2023 17:36:38 +0100 Subject: [PATCH 89/98] prdoc --- prdoc/pr_1845.prdoc | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 prdoc/pr_1845.prdoc diff --git a/prdoc/pr_1845.prdoc b/prdoc/pr_1845.prdoc new file mode 100644 index 000000000000..cf6cd1feadf4 --- /dev/null +++ b/prdoc/pr_1845.prdoc @@ -0,0 +1,16 @@ +# 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 WeightTrader: Swap Fee Asset for Native Asset" + +doc: + - audience: Runtime Dev + description: | + Implements an XCM executor `WeightTrader`, facilitating fee payments in any asset that can be exchanged for a native asset. + + A few constraints need to be observed: + - `buy_weight` and `refund` operations must be atomic, as another weight trader implementation might be attempted in case of failure. + - swap credit must be utilized since there isn’t an account to which an asset of some class can be deposited with a guarantee to meet the existential deposit requirement. + +crates: + - name: cumulus-primitives-utility From cb30d43f15dcee679573413383bd7aa8d25155d7 Mon Sep 17 00:00:00 2001 From: muharem Date: Wed, 20 Dec 2023 17:58:50 +0100 Subject: [PATCH 90/98] clippy fix --- cumulus/primitives/utility/src/tests/swap_first.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cumulus/primitives/utility/src/tests/swap_first.rs b/cumulus/primitives/utility/src/tests/swap_first.rs index d7bb44a9e419..222cd005b12a 100644 --- a/cumulus/primitives/utility/src/tests/swap_first.rs +++ b/cumulus/primitives/utility/src/tests/swap_first.rs @@ -396,7 +396,7 @@ pub mod mock { credit_in.peek() >= amount_out_min.unwrap_or(Self::Balance::zero()), (credit_in, DispatchError::Unavailable) ); - let swap_res = SWAP.with(|b| b.borrow().get(&(path[0], path[1])).map(|v| v.clone())); + let swap_res = SWAP.with(|b| b.borrow().get(&(path[0], path[1])).map(|v| *v)); let pool_account = match swap_res { Some(a) => a, None => return Err((credit_in, DispatchError::Unavailable)), @@ -423,7 +423,7 @@ pub mod mock { ) -> Result<(Self::Credit, Self::Credit), (Self::Credit, DispatchError)> { ensure!(2 == path.len(), (credit_in, DispatchError::Unavailable)); ensure!(credit_in.peek() >= amount_out, (credit_in, DispatchError::Unavailable)); - let swap_res = SWAP.with(|b| b.borrow().get(&(path[0], path[1])).map(|v| v.clone())); + let swap_res = SWAP.with(|b| b.borrow().get(&(path[0], path[1])).map(|v| *v)); let pool_account = match swap_res { Some(a) => a, None => return Err((credit_in, DispatchError::Unavailable)), From 8b85a3d719b3431f9f103aff32d9fdf90c9ee528 Mon Sep 17 00:00:00 2001 From: muharem Date: Wed, 20 Dec 2023 19:00:24 +0100 Subject: [PATCH 91/98] ignore test --- .../emulated/tests/assets/asset-hub-westend/src/tests/send.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/send.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/send.rs index 4b98eeb0ed33..f7cda4459885 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/send.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/send.rs @@ -30,6 +30,7 @@ fn send_transact_as_superuser_from_relay_to_system_para_works() { /// Parachain should be able to send XCM paying its fee with sufficient asset /// in the System Parachain +#[ignore] // remove the test and write new for swap trader #[test] fn send_xcm_from_para_to_system_para_paying_fee_with_assets_works() { let para_sovereign_account = AssetHubWestend::sovereign_account_id_of( From 4088a5ccc3e958f4a8fc0a1b4dc2988b0395db80 Mon Sep 17 00:00:00 2001 From: muharem Date: Wed, 20 Dec 2023 20:09:18 +0100 Subject: [PATCH 92/98] toml format --- cumulus/primitives/utility/Cargo.toml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cumulus/primitives/utility/Cargo.toml b/cumulus/primitives/utility/Cargo.toml index 7fa76c5c1833..1558295b9365 100644 --- a/cumulus/primitives/utility/Cargo.toml +++ b/cumulus/primitives/utility/Cargo.toml @@ -14,11 +14,11 @@ codec = { package = "parity-scale-codec", version = "3.0.0", default-features = log = { version = "0.4.20", default-features = false } # Substrate -frame-support = { path = "../../../substrate/frame/support", default-features = false} -sp-io = { path = "../../../substrate/primitives/io", default-features = false} -sp-runtime = { path = "../../../substrate/primitives/runtime", default-features = false} -sp-std = { path = "../../../substrate/primitives/std", default-features = false} -pallet-asset-conversion = { path = "../../../substrate/frame/asset-conversion", default-features = false} +frame-support = { path = "../../../substrate/frame/support", default-features = false } +sp-io = { path = "../../../substrate/primitives/io", default-features = false } +sp-runtime = { path = "../../../substrate/primitives/runtime", default-features = false } +sp-std = { path = "../../../substrate/primitives/std", default-features = false } +pallet-asset-conversion = { path = "../../../substrate/frame/asset-conversion", default-features = false } # Polkadot polkadot-runtime-common = { path = "../../../polkadot/runtime/common", default-features = false } @@ -32,7 +32,7 @@ pallet-xcm-benchmarks = { path = "../../../polkadot/xcm/pallet-xcm-benchmarks", cumulus-primitives-core = { path = "../core", default-features = false } [features] -default = [ "std" ] +default = ["std"] std = [ "codec/std", "cumulus-primitives-core/std", From 2ff205eb6e930ff7197e61274deda6000717243a Mon Sep 17 00:00:00 2001 From: muharem Date: Mon, 8 Jan 2024 16:53:27 +0800 Subject: [PATCH 93/98] swap trader for rococo --- .../assets/asset-hub-rococo/src/lib.rs | 19 +- .../assets/asset-hub-rococo/src/xcm_config.rs | 34 +- .../assets/asset-hub-rococo/tests/tests.rs | 497 ++++++++---------- 3 files changed, 241 insertions(+), 309 deletions(-) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs index 61939a2c80a7..0858e795ad71 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -312,7 +312,7 @@ impl pallet_assets::Config for Runtime { type BenchmarkHelper = (); } -/// Union fungibles implementation for `Assets`` and `ForeignAssets`. +/// Union fungibles implementation for `Assets` and `ForeignAssets`. pub type LocalAndForeignAssets = fungibles::UnionOf< Assets, ForeignAssets, @@ -324,18 +324,21 @@ pub type LocalAndForeignAssets = fungibles::UnionOf< AccountId, >; +/// Union fungibles implementation for [`LocalAndForeignAssets`] and `Balances`. +pub type NativeAndAssets = fungible::UnionOf< + Balances, + LocalAndForeignAssets, + TargetFromLeft, + MultiLocation, + AccountId, +>; + impl pallet_asset_conversion::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Balance = Balance; type HigherPrecisionBalance = sp_core::U256; type AssetKind = MultiLocation; - type Assets = fungible::UnionOf< - Balances, - LocalAndForeignAssets, - TargetFromLeft, - Self::AssetKind, - Self::AccountId, - >; + type Assets = NativeAndAssets; type PoolId = (Self::AssetKind, Self::AssetKind); type PoolLocator = pallet_asset_conversion::WithFirstAsset; diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs index 2826c18f3f7c..9b20a921a646 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs @@ -15,17 +15,21 @@ use super::{ AccountId, AllPalletsWithSystem, Assets, Authorship, Balance, Balances, BaseDeliveryFee, - FeeAssetId, ForeignAssets, ForeignAssetsInstance, ParachainInfo, ParachainSystem, PolkadotXcm, - PoolAssets, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, ToWestendXcmRouter, - TransactionByteFee, TrustBackedAssetsInstance, WeightToFee, XcmpQueue, + CollatorSelection, FeeAssetId, ForeignAssets, ForeignAssetsInstance, ParachainInfo, + ParachainSystem, PolkadotXcm, PoolAssets, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, + ToWestendXcmRouter, TransactionByteFee, TrustBackedAssetsInstance, WeightToFee, XcmpQueue, }; use assets_common::{ local_and_foreign_assets::MatchesLocalAndForeignAssetsMultiLocation, matching::{FromNetwork, FromSiblingParachain, IsForeignConcreteAsset}, + TrustBackedAssetsAsMultiLocation, }; use frame_support::{ match_types, parameter_types, - traits::{ConstU32, Contains, Equals, Everything, Nothing, PalletInfoAccess}, + traits::{ + tokens::imbalance::ResolveAssetTo, ConstU32, Contains, Equals, Everything, Nothing, + PalletInfoAccess, + }, }; use frame_system::EnsureRoot; use pallet_xcm::XcmPassthrough; @@ -73,6 +77,7 @@ parameter_types! { pub PoolAssetsPalletLocation: MultiLocation = PalletInstance(::index() as u8).into(); pub CheckingAccount: AccountId = PolkadotXcm::check_account(); + pub StakingPot: AccountId = CollatorSelection::account_id(); pub const GovernanceLocation: MultiLocation = MultiLocation::parent(); pub TreasuryAccount: AccountId = TREASURY_PALLET_ID.into_account_truncating(); pub RelayTreasuryLocation: MultiLocation = (Parent, PalletInstance(rococo_runtime_constants::TREASURY_PALLET_ID)).into(); @@ -550,18 +555,17 @@ impl xcm_executor::Config for XcmConfig { >; type Trader = ( UsingComponents>, - // This trader allows to pay with `is_sufficient=true` "Trust Backed" assets from dedicated - // `pallet_assets` instance - `Assets`. - cumulus_primitives_utility::TakeFirstAssetTrader< + cumulus_primitives_utility::SwapFirstAssetTrader< + TokenLocation, + crate::AssetConversion, + WeightToFee, + crate::NativeAndAssets, + ( + TrustBackedAssetsAsMultiLocation, + ForeignAssetsConvertedConcreteId, + ), + ResolveAssetTo, AccountId, - AssetFeeAsExistentialDepositMultiplierFeeCharger, - TrustBackedAssetsConvertedConcreteId, - Assets, - cumulus_primitives_utility::XcmFeesTo32ByteAccount< - FungiblesTransactor, - AccountId, - XcmAssetFeesReceiver, - >, >, // This trader allows to pay with `is_sufficient=true` "Foreign" assets from dedicated // `pallet_assets` instance - `ForeignAssets`. diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs index 42c91cc8ea69..c67374126d38 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs @@ -17,34 +17,38 @@ //! Tests for the Rococo Assets Hub chain. -use asset_hub_rococo_runtime::xcm_config::{ - AssetFeeAsExistentialDepositMultiplierFeeCharger, TokenLocation, - TrustBackedAssetsPalletLocation, +use asset_hub_rococo_runtime::{ + xcm_config, + xcm_config::{bridging, ForeignCreatorsSovereignAccountOf, LocationToAccountId, TokenLocation}, + AllPalletsWithoutSystem, MetadataDepositBase, MetadataDepositPerByte, RuntimeCall, + RuntimeEvent, ToWestendXcmRouterInstance, XcmpQueue, }; pub use asset_hub_rococo_runtime::{ - xcm_config::{ - self, bridging, CheckingAccount, ForeignCreatorsSovereignAccountOf, LocationToAccountId, - XcmConfig, - }, - AllPalletsWithoutSystem, AssetDeposit, Assets, Balances, ExistentialDeposit, ForeignAssets, - ForeignAssetsInstance, MetadataDepositBase, MetadataDepositPerByte, ParachainSystem, Runtime, - RuntimeCall, RuntimeEvent, SessionKeys, System, ToWestendXcmRouterInstance, - TrustBackedAssetsInstance, XcmpQueue, + xcm_config::{CheckingAccount, TrustBackedAssetsPalletLocation, XcmConfig}, + AssetConversion, AssetDeposit, Assets, Balances, CollatorSelection, ExistentialDeposit, + ForeignAssets, ForeignAssetsInstance, ParachainSystem, Runtime, SessionKeys, System, + TrustBackedAssetsInstance, }; use asset_test_utils::{ test_cases_over_bridge::TestBridgingConfig, CollatorSessionKey, CollatorSessionKeys, ExtBuilder, }; use codec::{Decode, Encode}; -use cumulus_primitives_utility::ChargeWeightInFungibles; use frame_support::{ - assert_noop, assert_ok, - traits::fungibles::InspectEnumerable, + assert_ok, + traits::{ + fungible::{Inspect, Mutate}, + fungibles::{ + Create, Inspect as FungiblesInspect, InspectEnumerable, Mutate as FungiblesMutate, + }, + }, weights::{Weight, WeightToFee as WeightToFeeT}, }; use parachains_common::{ - rococo::fee::WeightToFee, AccountId, AssetIdForTrustBackedAssets, AuraId, Balance, + rococo::{currency::UNITS, fee::WeightToFee}, + AccountId, AssetIdForTrustBackedAssets, AuraId, Balance, }; use sp_runtime::traits::MaybeEquivalence; +use std::convert::Into; use xcm::latest::prelude::*; use xcm_executor::traits::{Identity, JustTry, WeightTrader}; @@ -69,7 +73,7 @@ fn collator_session_keys() -> CollatorSessionKeys { } #[test] -fn test_asset_xcm_trader() { +fn test_buy_and_refund_weight_in_native() { ExtBuilder::::default() .with_collators(vec![AccountId::from(ALICE)]) .with_session_keys(vec![( @@ -79,77 +83,55 @@ fn test_asset_xcm_trader() { )]) .build() .execute_with(|| { - // We need root origin to create a sufficient asset - let minimum_asset_balance = 3333333_u128; - let local_asset_id = 1; - assert_ok!(Assets::force_create( - RuntimeHelper::root_origin(), - local_asset_id.into(), - AccountId::from(ALICE).into(), - true, - minimum_asset_balance - )); + let bob: AccountId = SOME_ASSET_ADMIN.into(); + let staking_pot = CollatorSelection::account_id(); + let native_location = TokenLocation::get(); + let initial_balance = 200 * UNITS; - // We first mint enough asset for the account to exist for assets - assert_ok!(Assets::mint( - RuntimeHelper::origin_of(AccountId::from(ALICE)), - local_asset_id.into(), - AccountId::from(ALICE).into(), - minimum_asset_balance - )); - - // get asset id as multilocation - let asset_multilocation = - AssetIdForTrustBackedAssetsConvert::convert_back(&local_asset_id).unwrap(); - - // Set Alice as block author, who will receive fees - RuntimeHelper::run_to_block(2, AccountId::from(ALICE)); - - // We are going to buy 4e9 weight - let bought = Weight::from_parts(4_000_000_000u64, 0); + assert_ok!(Balances::mint_into(&bob, initial_balance)); + assert_ok!(Balances::mint_into(&staking_pot, initial_balance)); - // Lets calculate amount needed - let asset_amount_needed = - AssetFeeAsExistentialDepositMultiplierFeeCharger::charge_weight_in_fungibles( - local_asset_id, - bought, - ) - .expect("failed to compute"); - - // Lets pay with: asset_amount_needed + asset_amount_extra - let asset_amount_extra = 100_u128; - let asset: MultiAsset = - (asset_multilocation, asset_amount_needed + asset_amount_extra).into(); + // keep initial total issuance to assert later. + let total_issuance = Balances::total_issuance(); - let mut trader = ::Trader::new(); + // prepare input to buy weight. + let weight = Weight::from_parts(4_000_000_000, 0); + let fee = WeightToFee::weight_to_fee(&weight); + let extra_amount = 100; let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None }; + let payment: MultiAsset = (native_location, fee + extra_amount).into(); - // Lets buy_weight and make sure buy_weight does not return an error - let unused_assets = trader.buy_weight(bought, asset.into(), &ctx).expect("Expected Ok"); - // Check whether a correct amount of unused assets is returned - assert_ok!( - unused_assets.ensure_contains(&(asset_multilocation, asset_amount_extra).into()) - ); - - // Drop trader + // init trader and buy weight. + let mut trader = ::Trader::new(); + let unused_asset = + trader.buy_weight(weight, payment.into(), &ctx).expect("Expected Ok"); + + // assert. + let unused_amount = + unused_asset.fungible.get(&native_location.into()).map_or(0, |a| *a); + assert_eq!(unused_amount, extra_amount); + assert_eq!(Balances::total_issuance(), total_issuance); + + // prepare input to refund weight. + let refund_weight = Weight::from_parts(1_000_000_000, 0); + let refund = WeightToFee::weight_to_fee(&refund_weight); + + // refund. + let actual_refund = trader.refund_weight(refund_weight, &ctx).unwrap(); + assert_eq!(actual_refund, (native_location, refund).into()); + + // assert. + assert_eq!(Balances::balance(&staking_pot), initial_balance); + // only after `trader` is dropped we expect the fee to be resolved into the treasury + // account. drop(trader); - - // Make sure author(Alice) has received the amount - assert_eq!( - Assets::balance(local_asset_id, AccountId::from(ALICE)), - minimum_asset_balance + asset_amount_needed - ); - - // We also need to ensure the total supply increased - assert_eq!( - Assets::total_supply(local_asset_id), - minimum_asset_balance + asset_amount_needed - ); - }); + assert_eq!(Balances::balance(&staking_pot), initial_balance + fee - refund); + assert_eq!(Balances::total_issuance(), total_issuance + fee - refund); + }) } #[test] -fn test_asset_xcm_trader_with_refund() { +fn test_buy_and_refund_weight_with_swap_local_asset_xcm_trader() { ExtBuilder::::default() .with_collators(vec![AccountId::from(ALICE)]) .with_session_keys(vec![( @@ -159,77 +141,93 @@ fn test_asset_xcm_trader_with_refund() { )]) .build() .execute_with(|| { - // We need root origin to create a sufficient asset - // We set existential deposit to be identical to the one for Balances first - assert_ok!(Assets::force_create( - RuntimeHelper::root_origin(), - 1.into(), - AccountId::from(ALICE).into(), - true, - ExistentialDeposit::get() + let bob: AccountId = SOME_ASSET_ADMIN.into(); + let staking_pot = CollatorSelection::account_id(); + let asset_1: u32 = 1; + let native_location = TokenLocation::get(); + let asset_1_location = + AssetIdForTrustBackedAssetsConvert::convert_back(&asset_1).unwrap(); + // bob's initial balance for native and `asset1` assets. + let initial_balance = 200 * UNITS; + // liquidity for both arms of (native, asset1) pool. + let pool_liquidity = 100 * UNITS; + + // init asset, balances and pool. + assert_ok!(>::create(asset_1, bob.clone(), true, 10)); + + assert_ok!(Assets::mint_into(asset_1, &bob, initial_balance)); + assert_ok!(Balances::mint_into(&bob, initial_balance)); + assert_ok!(Balances::mint_into(&staking_pot, initial_balance)); + + assert_ok!(AssetConversion::create_pool( + RuntimeHelper::origin_of(bob.clone()), + Box::new(native_location), + Box::new(asset_1_location) )); - // We first mint enough asset for the account to exist for assets - assert_ok!(Assets::mint( - RuntimeHelper::origin_of(AccountId::from(ALICE)), - 1.into(), - AccountId::from(ALICE).into(), - ExistentialDeposit::get() + assert_ok!(AssetConversion::add_liquidity( + RuntimeHelper::origin_of(bob.clone()), + Box::new(native_location), + Box::new(asset_1_location), + pool_liquidity, + pool_liquidity, + 1, + 1, + bob, )); - let mut trader = ::Trader::new(); - let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None }; - - // Set Alice as block author, who will receive fees - RuntimeHelper::run_to_block(2, AccountId::from(ALICE)); - - // We are going to buy 4e9 weight - let bought = Weight::from_parts(4_000_000_000u64, 0); - - let asset_multilocation = AssetIdForTrustBackedAssetsConvert::convert_back(&1).unwrap(); - - // lets calculate amount needed - let amount_bought = WeightToFee::weight_to_fee(&bought); - - let asset: MultiAsset = (asset_multilocation, amount_bought).into(); - - // Make sure buy_weight does not return an error - assert_ok!(trader.buy_weight(bought, asset.clone().into(), &ctx)); - - // Make sure again buy_weight does return an error - // This assert relies on the fact, that we use `TakeFirstAssetTrader` in `WeightTrader` - // tuple chain, which cannot be called twice - assert_noop!(trader.buy_weight(bought, asset.into(), &ctx), XcmError::TooExpensive); - - // We actually use half of the weight - let weight_used = bought / 2; + // keep initial total issuance to assert later. + let asset_total_issuance = Assets::total_issuance(asset_1); + let native_total_issuance = Balances::total_issuance(); - // Make sure refurnd works. - let amount_refunded = WeightToFee::weight_to_fee(&(bought - weight_used)); - - assert_eq!( - trader.refund_weight(bought - weight_used, &ctx), - Some((asset_multilocation, amount_refunded).into()) - ); + // prepare input to buy weight. + let weight = Weight::from_parts(4_000_000_000, 0); + let fee = WeightToFee::weight_to_fee(&weight); + let asset_fee = + AssetConversion::get_amount_in(&fee, &pool_liquidity, &pool_liquidity).unwrap(); + let extra_amount = 100; + let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None }; + let payment: MultiAsset = (asset_1_location, asset_fee + extra_amount).into(); - // Drop trader + // init trader and buy weight. + let mut trader = ::Trader::new(); + let unused_asset = + trader.buy_weight(weight, payment.into(), &ctx).expect("Expected Ok"); + + // assert. + let unused_amount = + unused_asset.fungible.get(&asset_1_location.into()).map_or(0, |a| *a); + assert_eq!(unused_amount, extra_amount); + assert_eq!(Assets::total_issuance(asset_1), asset_total_issuance + asset_fee); + + // prepare input to refund weight. + let refund_weight = Weight::from_parts(1_000_000_000, 0); + let refund = WeightToFee::weight_to_fee(&refund_weight); + let (reserve1, reserve2) = + AssetConversion::get_reserves(native_location, asset_1_location).unwrap(); + let asset_refund = + AssetConversion::get_amount_out(&refund, &reserve1, &reserve2).unwrap(); + + // refund. + let actual_refund = trader.refund_weight(refund_weight, &ctx).unwrap(); + assert_eq!(actual_refund, (asset_1_location, asset_refund).into()); + + // assert. + assert_eq!(Balances::balance(&staking_pot), initial_balance); + // only after `trader` is dropped we expect the fee to be resolved into the treasury + // account. drop(trader); - - // We only should have paid for half of the bought weight - let fees_paid = WeightToFee::weight_to_fee(&weight_used); - + assert_eq!(Balances::balance(&staking_pot), initial_balance + fee - refund); assert_eq!( - Assets::balance(1, AccountId::from(ALICE)), - ExistentialDeposit::get() + fees_paid + Assets::total_issuance(asset_1), + asset_total_issuance + asset_fee - asset_refund ); - - // We also need to ensure the total supply increased - assert_eq!(Assets::total_supply(1), ExistentialDeposit::get() + fees_paid); - }); + assert_eq!(Balances::total_issuance(), native_total_issuance); + }) } #[test] -fn test_asset_xcm_trader_refund_not_possible_since_amount_less_than_ed() { +fn test_buy_and_refund_weight_with_swap_foreign_asset_xcm_trader() { ExtBuilder::::default() .with_collators(vec![AccountId::from(ALICE)]) .with_session_keys(vec![( @@ -239,169 +237,96 @@ fn test_asset_xcm_trader_refund_not_possible_since_amount_less_than_ed() { )]) .build() .execute_with(|| { - // We need root origin to create a sufficient asset - // We set existential deposit to be identical to the one for Balances first - assert_ok!(Assets::force_create( - RuntimeHelper::root_origin(), - 1.into(), - AccountId::from(ALICE).into(), + let bob: AccountId = SOME_ASSET_ADMIN.into(); + let staking_pot = CollatorSelection::account_id(); + let native_location = TokenLocation::get(); + let foreign_location = + MultiLocation { parents: 1, interior: X2(Parachain(1234), GeneralIndex(12345)) }; + // bob's initial balance for native and `asset1` assets. + let initial_balance = 200 * UNITS; + // liquidity for both arms of (native, asset1) pool. + let pool_liquidity = 100 * UNITS; + + // init asset, balances and pool. + assert_ok!(>::create( + foreign_location, + bob.clone(), true, - ExistentialDeposit::get() + 10 )); - let mut trader = ::Trader::new(); - let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None }; - - // Set Alice as block author, who will receive fees - RuntimeHelper::run_to_block(2, AccountId::from(ALICE)); - - // We are going to buy small amount - let bought = Weight::from_parts(500_000_000u64, 0); - - let asset_multilocation = AssetIdForTrustBackedAssetsConvert::convert_back(&1).unwrap(); + assert_ok!(ForeignAssets::mint_into(foreign_location, &bob, initial_balance)); + assert_ok!(Balances::mint_into(&bob, initial_balance)); + assert_ok!(Balances::mint_into(&staking_pot, initial_balance)); - let amount_bought = WeightToFee::weight_to_fee(&bought); - - assert!( - amount_bought < ExistentialDeposit::get(), - "we are testing what happens when the amount does not exceed ED" - ); - - let asset: MultiAsset = (asset_multilocation, amount_bought).into(); - - // Buy weight should return an error - assert_noop!(trader.buy_weight(bought, asset.into(), &ctx), XcmError::TooExpensive); - - // not credited since the ED is higher than this value - assert_eq!(Assets::balance(1, AccountId::from(ALICE)), 0); - - // We also need to ensure the total supply did not increase - assert_eq!(Assets::total_supply(1), 0); - }); -} - -#[test] -fn test_that_buying_ed_refund_does_not_refund() { - ExtBuilder::::default() - .with_collators(vec![AccountId::from(ALICE)]) - .with_session_keys(vec![( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) }, - )]) - .build() - .execute_with(|| { - // We need root origin to create a sufficient asset - // We set existential deposit to be identical to the one for Balances first - assert_ok!(Assets::force_create( - RuntimeHelper::root_origin(), - 1.into(), - AccountId::from(ALICE).into(), - true, - ExistentialDeposit::get() + assert_ok!(AssetConversion::create_pool( + RuntimeHelper::origin_of(bob.clone()), + Box::new(native_location), + Box::new(foreign_location) )); - let mut trader = ::Trader::new(); - let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None }; - - // Set Alice as block author, who will receive fees - RuntimeHelper::run_to_block(2, AccountId::from(ALICE)); - - // We are gonna buy ED - let bought = Weight::from_parts(ExistentialDeposit::get().try_into().unwrap(), 0); - - let asset_multilocation = AssetIdForTrustBackedAssetsConvert::convert_back(&1).unwrap(); - - let amount_bought = WeightToFee::weight_to_fee(&bought); - - assert!( - amount_bought < ExistentialDeposit::get(), - "we are testing what happens when the amount does not exceed ED" - ); - - // We know we will have to buy at least ED, so lets make sure first it will - // fail with a payment of less than ED - let asset: MultiAsset = (asset_multilocation, amount_bought).into(); - assert_noop!(trader.buy_weight(bought, asset.into(), &ctx), XcmError::TooExpensive); - - // Now lets buy ED at least - let asset: MultiAsset = (asset_multilocation, ExistentialDeposit::get()).into(); - - // Buy weight should work - assert_ok!(trader.buy_weight(bought, asset.into(), &ctx)); - - // Should return None. We have a specific check making sure we dont go below ED for - // drop payment - assert_eq!(trader.refund_weight(bought, &ctx), None); - - // Drop trader - drop(trader); - - // Make sure author(Alice) has received the amount - assert_eq!(Assets::balance(1, AccountId::from(ALICE)), ExistentialDeposit::get()); - - // We also need to ensure the total supply increased - assert_eq!(Assets::total_supply(1), ExistentialDeposit::get()); - }); -} - -#[test] -fn test_asset_xcm_trader_not_possible_for_non_sufficient_assets() { - ExtBuilder::::default() - .with_collators(vec![AccountId::from(ALICE)]) - .with_session_keys(vec![( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) }, - )]) - .build() - .execute_with(|| { - // Create a non-sufficient asset with specific existential deposit - let minimum_asset_balance = 1_000_000_u128; - assert_ok!(Assets::force_create( - RuntimeHelper::root_origin(), - 1.into(), - AccountId::from(ALICE).into(), - false, - minimum_asset_balance + assert_ok!(AssetConversion::add_liquidity( + RuntimeHelper::origin_of(bob.clone()), + Box::new(native_location), + Box::new(foreign_location), + pool_liquidity, + pool_liquidity, + 1, + 1, + bob, )); - // We first mint enough asset for the account to exist for assets - assert_ok!(Assets::mint( - RuntimeHelper::origin_of(AccountId::from(ALICE)), - 1.into(), - AccountId::from(ALICE).into(), - minimum_asset_balance - )); + // keep initial total issuance to assert later. + let asset_total_issuance = ForeignAssets::total_issuance(foreign_location); + let native_total_issuance = Balances::total_issuance(); - let mut trader = ::Trader::new(); + // prepare input to buy weight. + let weight = Weight::from_parts(4_000_000_000, 0); + let fee = WeightToFee::weight_to_fee(&weight); + let asset_fee = + AssetConversion::get_amount_in(&fee, &pool_liquidity, &pool_liquidity).unwrap(); + let extra_amount = 100; let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None }; + let payment: MultiAsset = (foreign_location, asset_fee + extra_amount).into(); - // Set Alice as block author, who will receive fees - RuntimeHelper::run_to_block(2, AccountId::from(ALICE)); - - // We are going to buy 4e9 weight - let bought = Weight::from_parts(4_000_000_000u64, 0); - - // lets calculate amount needed - let asset_amount_needed = WeightToFee::weight_to_fee(&bought); - - let asset_multilocation = AssetIdForTrustBackedAssetsConvert::convert_back(&1).unwrap(); - - let asset: MultiAsset = (asset_multilocation, asset_amount_needed).into(); + // init trader and buy weight. + let mut trader = ::Trader::new(); + let unused_asset = + trader.buy_weight(weight, payment.into(), &ctx).expect("Expected Ok"); - // Make sure again buy_weight does return an error - assert_noop!(trader.buy_weight(bought, asset.into(), &ctx), XcmError::TooExpensive); + // assert. + let unused_amount = + unused_asset.fungible.get(&foreign_location.into()).map_or(0, |a| *a); + assert_eq!(unused_amount, extra_amount); + assert_eq!( + ForeignAssets::total_issuance(foreign_location), + asset_total_issuance + asset_fee + ); - // Drop trader + // prepare input to refund weight. + let refund_weight = Weight::from_parts(1_000_000_000, 0); + let refund = WeightToFee::weight_to_fee(&refund_weight); + let (reserve1, reserve2) = + AssetConversion::get_reserves(native_location, foreign_location).unwrap(); + let asset_refund = + AssetConversion::get_amount_out(&refund, &reserve1, &reserve2).unwrap(); + + // refund. + let actual_refund = trader.refund_weight(refund_weight, &ctx).unwrap(); + assert_eq!(actual_refund, (foreign_location, asset_refund).into()); + + // assert. + assert_eq!(Balances::balance(&staking_pot), initial_balance); + // only after `trader` is dropped we expect the fee to be resolved into the treasury + // account. drop(trader); - - // Make sure author(Alice) has NOT received the amount - assert_eq!(Assets::balance(1, AccountId::from(ALICE)), minimum_asset_balance); - - // We also need to ensure the total supply NOT increased - assert_eq!(Assets::total_supply(1), minimum_asset_balance); - }); + assert_eq!(Balances::balance(&staking_pot), initial_balance + fee - refund); + assert_eq!( + ForeignAssets::total_issuance(foreign_location), + asset_total_issuance + asset_fee - asset_refund + ); + assert_eq!(Balances::total_issuance(), native_total_issuance); + }) } #[test] From cc182e2e46a197c4934a538b81cc264211e40422 Mon Sep 17 00:00:00 2001 From: muharem Date: Mon, 8 Jan 2024 16:59:56 +0800 Subject: [PATCH 94/98] revert back hex-literal dep import prev changes --- .../parachains/runtimes/assets/asset-hub-westend/Cargo.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml index b31ad416b785..1a1ed0465a34 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml @@ -11,7 +11,7 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "max-encoded-len"] } -hex-literal = { version = "0.4.1" } +hex-literal = { version = "0.4.1", optional = true } log = { version = "0.4.20", default-features = false } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } smallvec = "1.11.0" @@ -90,6 +90,7 @@ bp-bridge-hub-rococo = { path = "../../../../../bridges/primitives/chain-bridge- bp-bridge-hub-westend = { path = "../../../../../bridges/primitives/chain-bridge-hub-westend", default-features = false } [dev-dependencies] +hex-literal = "0.4.1" asset-test-utils = { path = "../test-utils" } [build-dependencies] @@ -108,6 +109,7 @@ runtime-benchmarks = [ "frame-support/runtime-benchmarks", "frame-system-benchmarking/runtime-benchmarks", "frame-system/runtime-benchmarks", + "hex-literal", "pallet-asset-conversion/runtime-benchmarks", "pallet-assets/runtime-benchmarks", "pallet-balances/runtime-benchmarks", From b016d9b7f3119600b8f9ffe1dbd87b296eec00bb Mon Sep 17 00:00:00 2001 From: muharem Date: Mon, 8 Jan 2024 19:19:25 +0800 Subject: [PATCH 95/98] integration tests --- .../assets/asset-hub-rococo/src/tests/send.rs | 75 ----------- .../assets/asset-hub-rococo/src/tests/swap.rs | 127 ++++++++++++++++++ .../asset-hub-westend/src/tests/send.rs | 76 ----------- .../asset-hub-westend/src/tests/swap.rs | 127 ++++++++++++++++++ 4 files changed, 254 insertions(+), 151 deletions(-) diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/send.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/send.rs index 7be0463d2ec4..fcf4513859eb 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/send.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/send.rs @@ -27,78 +27,3 @@ fn send_transact_as_superuser_from_relay_to_system_para_works() { Some(Weight::from_parts(1_019_445_000, 200_000)), ) } - -/// Parachain should be able to send XCM paying its fee with sufficient asset -/// in the System Parachain -#[test] -fn send_xcm_from_para_to_system_para_paying_fee_with_assets_works() { - let para_sovereign_account = AssetHubRococo::sovereign_account_id_of( - AssetHubRococo::sibling_location_of(PenpalA::para_id()), - ); - - // Force create and mint assets for Parachain's sovereign account - AssetHubRococo::force_create_and_mint_asset( - ASSET_ID, - ASSET_MIN_BALANCE, - true, - para_sovereign_account.clone(), - Some(Weight::from_parts(1_019_445_000, 200_000)), - ASSET_MIN_BALANCE * 1000000000, - ); - - // We just need a call that can pass the `SafeCallFilter` - // Call values are not relevant - let call = AssetHubRococo::force_create_asset_call( - ASSET_ID, - para_sovereign_account.clone(), - true, - ASSET_MIN_BALANCE, - ); - - let origin_kind = OriginKind::SovereignAccount; - let fee_amount = ASSET_MIN_BALANCE * 1000000; - let native_asset = - (X2(PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into())), fee_amount).into(); - - let root_origin = ::RuntimeOrigin::root(); - let system_para_destination = PenpalA::sibling_location_of(AssetHubRococo::para_id()).into(); - let xcm = xcm_transact_paid_execution( - call, - origin_kind, - native_asset, - para_sovereign_account.clone(), - ); - - PenpalA::execute_with(|| { - assert_ok!(::PolkadotXcm::send( - root_origin, - bx!(system_para_destination), - bx!(xcm), - )); - - PenpalA::assert_xcm_pallet_sent(); - }); - - AssetHubRococo::execute_with(|| { - type RuntimeEvent = ::RuntimeEvent; - - AssetHubRococo::assert_xcmp_queue_success(Some(Weight::from_parts( - 15_594_564_000, - 562_893, - ))); - - assert_expected_events!( - AssetHubRococo, - vec![ - RuntimeEvent::Assets(pallet_assets::Event::Burned { asset_id, owner, balance }) => { - asset_id: *asset_id == ASSET_ID, - owner: *owner == para_sovereign_account, - balance: *balance == fee_amount, - }, - RuntimeEvent::Assets(pallet_assets::Event::Issued { asset_id, .. }) => { - asset_id: *asset_id == ASSET_ID, - }, - ] - ); - }); -} diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/swap.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/swap.rs index 3dcc51b75ccc..b1af69a1cd10 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/swap.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/swap.rs @@ -270,3 +270,130 @@ fn cannot_create_pool_from_pool_assets() { ); }); } + +#[test] +fn pay_xcm_fee_with_some_asset_swapped_for_native() { + let asset_native = asset_hub_rococo_runtime::xcm_config::TokenLocation::get(); + let asset_one = MultiLocation { + parents: 0, + interior: X2(PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into())), + }; + let penpal = AssetHubRococo::sovereign_account_id_of(AssetHubRococo::sibling_location_of( + PenpalA::para_id(), + )); + + AssetHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // set up pool with ASSET_ID <> NATIVE pair + assert_ok!(::Assets::create( + ::RuntimeOrigin::signed(AssetHubRococoSender::get()), + ASSET_ID.into(), + AssetHubRococoSender::get().into(), + ASSET_MIN_BALANCE, + )); + assert!(::Assets::asset_exists(ASSET_ID)); + + assert_ok!(::Assets::mint( + ::RuntimeOrigin::signed(AssetHubRococoSender::get()), + ASSET_ID.into(), + AssetHubRococoSender::get().into(), + 3_000_000_000_000, + )); + + assert_ok!(::AssetConversion::create_pool( + ::RuntimeOrigin::signed(AssetHubRococoSender::get()), + Box::new(asset_native), + Box::new(asset_one), + )); + + assert_expected_events!( + AssetHubRococo, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::PoolCreated { .. }) => {}, + ] + ); + + assert_ok!(::AssetConversion::add_liquidity( + ::RuntimeOrigin::signed(AssetHubRococoSender::get()), + Box::new(asset_native), + Box::new(asset_one), + 1_000_000_000_000, + 2_000_000_000_000, + 0, + 0, + AssetHubRococoSender::get().into() + )); + + assert_expected_events!( + AssetHubRococo, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::LiquidityAdded {lp_token_minted, .. }) => { lp_token_minted: *lp_token_minted == 1414213562273, }, + ] + ); + + // ensure `penpal` sovereign account has no native tokens and mint some `ASSET_ID` + assert_eq!( + ::Balances::free_balance(penpal.clone()), + 0 + ); + + assert_ok!(::Assets::touch_other( + ::RuntimeOrigin::signed(AssetHubRococoSender::get()), + ASSET_ID.into(), + penpal.clone().into(), + )); + + assert_ok!(::Assets::mint( + ::RuntimeOrigin::signed(AssetHubRococoSender::get()), + ASSET_ID.into(), + penpal.clone().into(), + 10_000_000_000_000, + )); + }); + + PenpalA::execute_with(|| { + // send xcm transact from `penpal` account which as only `ASSET_ID` tokens on + // `AssetHubRococo` + let call = AssetHubRococo::force_create_asset_call( + ASSET_ID + 1000, + penpal.clone(), + true, + ASSET_MIN_BALANCE, + ); + + let penpal_root = ::RuntimeOrigin::root(); + let fee_amount = 4_000_000_000_000u128; + let asset_one = + (X2(PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into())), fee_amount) + .into(); + let asset_hub_location = PenpalA::sibling_location_of(AssetHubRococo::para_id()).into(); + let xcm = xcm_transact_paid_execution( + call, + OriginKind::SovereignAccount, + asset_one, + penpal.clone(), + ); + + assert_ok!(::PolkadotXcm::send( + penpal_root, + bx!(asset_hub_location), + bx!(xcm), + )); + + PenpalA::assert_xcm_pallet_sent(); + }); + + AssetHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + AssetHubRococo::assert_xcmp_queue_success(None); + assert_expected_events!( + AssetHubRococo, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::SwapCreditExecuted { .. },) => {}, + RuntimeEvent::MessageQueue(pallet_message_queue::Event::Processed { success: true,.. }) => {}, + ] + ); + }); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/send.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/send.rs index f7cda4459885..d943bafc379b 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/send.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/send.rs @@ -27,79 +27,3 @@ fn send_transact_as_superuser_from_relay_to_system_para_works() { Some(Weight::from_parts(1_019_445_000, 200_000)), ) } - -/// Parachain should be able to send XCM paying its fee with sufficient asset -/// in the System Parachain -#[ignore] // remove the test and write new for swap trader -#[test] -fn send_xcm_from_para_to_system_para_paying_fee_with_assets_works() { - let para_sovereign_account = AssetHubWestend::sovereign_account_id_of( - AssetHubWestend::sibling_location_of(PenpalB::para_id()), - ); - - // Force create and mint assets for Parachain's sovereign account - AssetHubWestend::force_create_and_mint_asset( - ASSET_ID, - ASSET_MIN_BALANCE, - true, - para_sovereign_account.clone(), - Some(Weight::from_parts(1_019_445_000, 200_000)), - ASSET_MIN_BALANCE * 1000000000, - ); - - // We just need a call that can pass the `SafeCallFilter` - // Call values are not relevant - let call = AssetHubWestend::force_create_asset_call( - ASSET_ID, - para_sovereign_account.clone(), - true, - ASSET_MIN_BALANCE, - ); - - let origin_kind = OriginKind::SovereignAccount; - let fee_amount = ASSET_MIN_BALANCE * 1000000; - let native_asset = - (X2(PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into())), fee_amount).into(); - - let root_origin = ::RuntimeOrigin::root(); - let system_para_destination = PenpalB::sibling_location_of(AssetHubWestend::para_id()).into(); - let xcm = xcm_transact_paid_execution( - call, - origin_kind, - native_asset, - para_sovereign_account.clone(), - ); - - PenpalB::execute_with(|| { - assert_ok!(::PolkadotXcm::send( - root_origin, - bx!(system_para_destination), - bx!(xcm), - )); - - PenpalB::assert_xcm_pallet_sent(); - }); - - AssetHubWestend::execute_with(|| { - type RuntimeEvent = ::RuntimeEvent; - - AssetHubWestend::assert_xcmp_queue_success(Some(Weight::from_parts( - 16_290_336_000, - 562_893, - ))); - - assert_expected_events!( - AssetHubWestend, - vec![ - RuntimeEvent::Assets(pallet_assets::Event::Burned { asset_id, owner, balance }) => { - asset_id: *asset_id == ASSET_ID, - owner: *owner == para_sovereign_account, - balance: *balance == fee_amount, - }, - RuntimeEvent::Assets(pallet_assets::Event::Issued { asset_id, .. }) => { - asset_id: *asset_id == ASSET_ID, - }, - ] - ); - }); -} diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/swap.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/swap.rs index 47b6ab01e8f8..c768a1366faf 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/swap.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/swap.rs @@ -264,3 +264,130 @@ fn cannot_create_pool_from_pool_assets() { ); }); } + +#[test] +fn pay_xcm_fee_with_some_asset_swapped_for_native() { + let asset_native = asset_hub_westend_runtime::xcm_config::WestendLocation::get(); + let asset_one = MultiLocation { + parents: 0, + interior: X2(PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into())), + }; + let penpal = AssetHubWestend::sovereign_account_id_of(AssetHubWestend::sibling_location_of( + PenpalB::para_id(), + )); + + AssetHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // set up pool with ASSET_ID <> NATIVE pair + assert_ok!(::Assets::create( + ::RuntimeOrigin::signed(AssetHubWestendSender::get()), + ASSET_ID.into(), + AssetHubWestendSender::get().into(), + ASSET_MIN_BALANCE, + )); + assert!(::Assets::asset_exists(ASSET_ID)); + + assert_ok!(::Assets::mint( + ::RuntimeOrigin::signed(AssetHubWestendSender::get()), + ASSET_ID.into(), + AssetHubWestendSender::get().into(), + 3_000_000_000_000, + )); + + assert_ok!(::AssetConversion::create_pool( + ::RuntimeOrigin::signed(AssetHubWestendSender::get()), + Box::new(asset_native), + Box::new(asset_one), + )); + + assert_expected_events!( + AssetHubWestend, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::PoolCreated { .. }) => {}, + ] + ); + + assert_ok!(::AssetConversion::add_liquidity( + ::RuntimeOrigin::signed(AssetHubWestendSender::get()), + Box::new(asset_native), + Box::new(asset_one), + 1_000_000_000_000, + 2_000_000_000_000, + 0, + 0, + AssetHubWestendSender::get().into() + )); + + assert_expected_events!( + AssetHubWestend, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::LiquidityAdded {lp_token_minted, .. }) => { lp_token_minted: *lp_token_minted == 1414213562273, }, + ] + ); + + // ensure `penpal` sovereign account has no native tokens and mint some `ASSET_ID` + assert_eq!( + ::Balances::free_balance(penpal.clone()), + 0 + ); + + assert_ok!(::Assets::touch_other( + ::RuntimeOrigin::signed(AssetHubWestendSender::get()), + ASSET_ID.into(), + penpal.clone().into(), + )); + + assert_ok!(::Assets::mint( + ::RuntimeOrigin::signed(AssetHubWestendSender::get()), + ASSET_ID.into(), + penpal.clone().into(), + 10_000_000_000_000, + )); + }); + + PenpalB::execute_with(|| { + // send xcm transact from `penpal` account which as only `ASSET_ID` tokens on + // `AssetHubWestend` + let call = AssetHubWestend::force_create_asset_call( + ASSET_ID + 1000, + penpal.clone(), + true, + ASSET_MIN_BALANCE, + ); + + let penpal_root = ::RuntimeOrigin::root(); + let fee_amount = 4_000_000_000_000u128; + let asset_one = + (X2(PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into())), fee_amount) + .into(); + let asset_hub_location = PenpalB::sibling_location_of(AssetHubWestend::para_id()).into(); + let xcm = xcm_transact_paid_execution( + call, + OriginKind::SovereignAccount, + asset_one, + penpal.clone(), + ); + + assert_ok!(::PolkadotXcm::send( + penpal_root, + bx!(asset_hub_location), + bx!(xcm), + )); + + PenpalB::assert_xcm_pallet_sent(); + }); + + AssetHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + AssetHubWestend::assert_xcmp_queue_success(None); + assert_expected_events!( + AssetHubWestend, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::SwapCreditExecuted { .. },) => {}, + RuntimeEvent::MessageQueue(pallet_message_queue::Event::Processed { success: true,.. }) => {}, + ] + ); + }); +} From 6a5f665cf801dd85802fe76ef47366d7cecf23b6 Mon Sep 17 00:00:00 2001 From: muharem Date: Tue, 9 Jan 2024 16:46:26 +0800 Subject: [PATCH 96/98] remove TakeFirstAssetTrader trader and update affected tests --- Cargo.lock | 1 + .../assets/asset-hub-rococo/src/xcm_config.rs | 13 ---- .../asset-hub-westend/src/xcm_config.rs | 13 ---- .../runtimes/assets/test-utils/Cargo.toml | 2 + .../test-utils/src/test_cases_over_bridge.rs | 77 ++++++++++++++----- 5 files changed, 59 insertions(+), 47 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 72e28ff07e32..a75ae827e182 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1067,6 +1067,7 @@ dependencies = [ "frame-support", "frame-system", "hex-literal", + "pallet-asset-conversion", "pallet-assets", "pallet-balances", "pallet-collator-selection", diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs index 9b20a921a646..631c93ea7f03 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs @@ -567,19 +567,6 @@ impl xcm_executor::Config for XcmConfig { ResolveAssetTo, AccountId, >, - // This trader allows to pay with `is_sufficient=true` "Foreign" assets from dedicated - // `pallet_assets` instance - `ForeignAssets`. - cumulus_primitives_utility::TakeFirstAssetTrader< - AccountId, - ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger, - ForeignAssetsConvertedConcreteId, - ForeignAssets, - cumulus_primitives_utility::XcmFeesTo32ByteAccount< - ForeignFungiblesTransactor, - AccountId, - XcmAssetFeesReceiver, - >, - >, ); type ResponseHandler = PolkadotXcm; type AssetTrap = PolkadotXcm; diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs index 356ba04e70b8..28876d1d4542 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs @@ -584,19 +584,6 @@ impl xcm_executor::Config for XcmConfig { ResolveAssetTo, AccountId, >, - // This trader allows to pay with `is_sufficient=true` "Foreign" assets from dedicated - // `pallet_assets` instance - `ForeignAssets`. - cumulus_primitives_utility::TakeFirstAssetTrader< - AccountId, - ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger, - ForeignAssetsConvertedConcreteId, - ForeignAssets, - cumulus_primitives_utility::XcmFeesTo32ByteAccount< - ForeignFungiblesTransactor, - AccountId, - XcmAssetFeesReceiver, - >, - >, ); type ResponseHandler = PolkadotXcm; type AssetTrap = PolkadotXcm; diff --git a/cumulus/parachains/runtimes/assets/test-utils/Cargo.toml b/cumulus/parachains/runtimes/assets/test-utils/Cargo.toml index a3ed37596002..8410ca73633b 100644 --- a/cumulus/parachains/runtimes/assets/test-utils/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/test-utils/Cargo.toml @@ -16,6 +16,7 @@ codec = { package = "parity-scale-codec", version = "3.0.0", default-features = frame-support = { path = "../../../../../substrate/frame/support", default-features = false } frame-system = { path = "../../../../../substrate/frame/system", default-features = false } pallet-assets = { path = "../../../../../substrate/frame/assets", default-features = false } +pallet-asset-conversion = { path = "../../../../../substrate/frame/asset-conversion", default-features = false } pallet-balances = { path = "../../../../../substrate/frame/balances", default-features = false } pallet-session = { path = "../../../../../substrate/frame/session", default-features = false } sp-consensus-aura = { path = "../../../../../substrate/primitives/consensus/aura", default-features = false } @@ -65,6 +66,7 @@ std = [ "frame-support/std", "frame-system/std", "pallet-assets/std", + "pallet-asset-conversion/std", "pallet-balances/std", "pallet-collator-selection/std", "pallet-session/std", diff --git a/cumulus/parachains/runtimes/assets/test-utils/src/test_cases_over_bridge.rs b/cumulus/parachains/runtimes/assets/test-utils/src/test_cases_over_bridge.rs index 8007b275cb51..d7441880426d 100644 --- a/cumulus/parachains/runtimes/assets/test-utils/src/test_cases_over_bridge.rs +++ b/cumulus/parachains/runtimes/assets/test-utils/src/test_cases_over_bridge.rs @@ -30,6 +30,7 @@ use parachains_runtimes_test_utils::{ ValidatorIdOf, XcmReceivedFrom, }; use sp_runtime::{traits::StaticLookup, Saturating}; +use sp_std::ops::Mul; use xcm::{latest::prelude::*, VersionedMultiAssets}; use xcm_builder::{CreateMatcher, MatchXcm}; use xcm_executor::{traits::ConvertLocation, XcmExecutor}; @@ -336,12 +337,13 @@ pub fn receive_reserve_asset_deposited_from_different_consensus_works< + pallet_collator_selection::Config + cumulus_pallet_parachain_system::Config + cumulus_pallet_xcmp_queue::Config - + pallet_assets::Config, + + pallet_assets::Config + + pallet_asset_conversion::Config, AllPalletsWithoutSystem: OnInitialize> + OnFinalize>, - AccountIdOf: Into<[u8; 32]>, + AccountIdOf: Into<[u8; 32]> + From<[u8; 32]>, ValidatorIdOf: From>, - BalanceOf: From, + BalanceOf: From + Into, XcmConfig: xcm_executor::Config, LocationToAccountId: ConvertLocation>, >::AssetId: @@ -354,6 +356,9 @@ pub fn receive_reserve_asset_deposited_from_different_consensus_works< + Into, <::Lookup as StaticLookup>::Source: From<::AccountId>, + ::AssetKind: + From + Into, + ::Balance: From, ForeignAssetsPalletInstance: 'static, { ExtBuilder::::default() @@ -400,6 +405,43 @@ pub fn receive_reserve_asset_deposited_from_different_consensus_works< ) ); + // setup a pool to pay fees with `foreign_asset_id_multilocation` tokens + let pool_owner: AccountIdOf = [1u8; 32].into(); + let native_asset = MultiLocation::parent(); + let pool_liquidity: u128 = + existential_deposit.into().max(foreign_asset_id_minimum_balance).mul(100_000); + + let _ = >::deposit_creating( + &pool_owner, + (existential_deposit.into() + pool_liquidity).mul(2).into(), + ); + + assert_ok!(>::mint( + RuntimeHelper::::origin_of( + sovereign_account_as_owner_of_foreign_asset + ), + foreign_asset_id_multilocation.into(), + pool_owner.clone().into(), + (foreign_asset_id_minimum_balance + pool_liquidity).mul(2).into(), + )); + + assert_ok!(>::create_pool( + RuntimeHelper::::origin_of(pool_owner.clone()), + Box::new(native_asset.into()), + Box::new(foreign_asset_id_multilocation.into()) + )); + + assert_ok!(>::add_liquidity( + RuntimeHelper::::origin_of(pool_owner.clone()), + Box::new(native_asset.into()), + Box::new(foreign_asset_id_multilocation.into()), + pool_liquidity.into(), + pool_liquidity.into(), + 1.into(), + 1.into(), + pool_owner, + )); + // Balances before assert_eq!( >::free_balance(&target_account), @@ -485,14 +527,12 @@ pub fn receive_reserve_asset_deposited_from_different_consensus_works< ); assert_ok!(outcome.ensure_complete()); - // author actual balance after (received fees from Trader for ForeignAssets) - let author_received_fees = - >::balance( - foreign_asset_id_multilocation.into(), - &block_author_account, - ); - - // Balances after (untouched) + // Balances after + // staking pot receives xcm fees in dot + assert!( + >::free_balance(&staking_pot) != + existential_deposit + ); assert_eq!( >::free_balance(&target_account), existential_deposit.clone() @@ -501,30 +541,25 @@ pub fn receive_reserve_asset_deposited_from_different_consensus_works< >::free_balance(&block_author_account), 0.into() ); - assert_eq!( - >::free_balance(&staking_pot), - existential_deposit.clone() - ); // ForeignAssets balances after - assert_eq!( + assert!( >::balance( foreign_asset_id_multilocation.into(), &target_account - ), - (transfered_foreign_asset_id_amount - author_received_fees.into()).into() + ) > 0.into() ); assert_eq!( >::balance( foreign_asset_id_multilocation.into(), - &block_author_account + &staking_pot ), - author_received_fees + 0.into() ); assert_eq!( >::balance( foreign_asset_id_multilocation.into(), - &staking_pot + &block_author_account ), 0.into() ); From b22702f908cba4316c41383daea0cff426de8f9c Mon Sep 17 00:00:00 2001 From: muharem Date: Tue, 9 Jan 2024 19:27:22 +0800 Subject: [PATCH 97/98] fix tests --- Cargo.lock | 2 + .../bridges/bridge-hub-rococo/Cargo.toml | 1 + .../bridges/bridge-hub-rococo/src/lib.rs | 3 +- .../src/tests/asset_transfers.rs | 45 ++++++++++++++++++- .../bridges/bridge-hub-westend/Cargo.toml | 1 + .../bridges/bridge-hub-westend/src/lib.rs | 3 +- .../src/tests/asset_transfers.rs | 43 ++++++++++++++++++ 7 files changed, 95 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a75ae827e182..6585c116301a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1975,6 +1975,7 @@ dependencies = [ "frame-support", "hex", "hex-literal", + "pallet-asset-conversion", "pallet-assets", "pallet-balances", "pallet-bridge-messages", @@ -2168,6 +2169,7 @@ dependencies = [ "cumulus-pallet-xcmp-queue", "emulated-integration-tests-common", "frame-support", + "pallet-asset-conversion", "pallet-assets", "pallet-balances", "pallet-bridge-messages", diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/Cargo.toml index e75187bea95e..a5177028afe1 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/Cargo.toml @@ -20,6 +20,7 @@ hex-literal = "0.4.1" sp-core = { path = "../../../../../../../substrate/primitives/core", default-features = false } frame-support = { path = "../../../../../../../substrate/frame/support", default-features = false } pallet-assets = { path = "../../../../../../../substrate/frame/assets", default-features = false } +pallet-asset-conversion = { path = "../../../../../../../substrate/frame/asset-conversion", default-features = false } pallet-balances = { path = "../../../../../../../substrate/frame/balances", default-features = false } pallet-message-queue = { path = "../../../../../../../substrate/frame/message-queue" } sp-runtime = { path = "../../../../../../../substrate/primitives/runtime", default-features = false } diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs index 5127bd759dc6..608aa388ce81 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs @@ -61,7 +61,8 @@ pub use rococo_westend_system_emulated_network::{ rococo_emulated_chain::{genesis::ED as ROCOCO_ED, RococoRelayPallet as RococoPallet}, AssetHubRococoPara as AssetHubRococo, AssetHubRococoParaReceiver as AssetHubRococoReceiver, AssetHubRococoParaSender as AssetHubRococoSender, AssetHubWestendPara as AssetHubWestend, - AssetHubWestendParaReceiver as AssetHubWestendReceiver, BridgeHubRococoPara as BridgeHubRococo, + AssetHubWestendParaReceiver as AssetHubWestendReceiver, + AssetHubWestendParaSender as AssetHubWestendSender, BridgeHubRococoPara as BridgeHubRococo, BridgeHubRococoParaSender as BridgeHubRococoSender, BridgeHubWestendPara as BridgeHubWestend, RococoRelay as Rococo, RococoRelayReceiver as RococoReceiver, RococoRelaySender as RococoSender, diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/asset_transfers.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/asset_transfers.rs index 5a2111a9be94..8b5a72a8e62c 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/asset_transfers.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/asset_transfers.rs @@ -49,6 +49,49 @@ fn send_rocs_from_asset_hub_rococo_to_asset_hub_westend() { AssetHubWestend::para_id(), ); + AssetHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // setup a pool to pay xcm fees with `roc_at_asset_hub_westend` tokens + assert_ok!(::ForeignAssets::mint( + ::RuntimeOrigin::signed(AssetHubWestendSender::get()), + roc_at_asset_hub_westend.into(), + AssetHubWestendSender::get().into(), + 3_000_000_000_000, + )); + + assert_ok!(::AssetConversion::create_pool( + ::RuntimeOrigin::signed(AssetHubWestendSender::get()), + Box::new(Parent.into()), + Box::new(roc_at_asset_hub_westend), + )); + + assert_expected_events!( + AssetHubWestend, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::PoolCreated { .. }) => {}, + ] + ); + + assert_ok!(::AssetConversion::add_liquidity( + ::RuntimeOrigin::signed(AssetHubWestendSender::get()), + Box::new(Parent.into()), + Box::new(roc_at_asset_hub_westend), + 1_000_000_000_000, + 2_000_000_000_000, + 1, + 1, + AssetHubWestendSender::get().into() + )); + + assert_expected_events!( + AssetHubWestend, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::LiquidityAdded {..}) => {}, + ] + ); + }); + let rocs_in_reserve_on_ahr_before = ::account_data_of(sov_ahw_on_ahr.clone()).free; let sender_rocs_before = @@ -58,7 +101,7 @@ fn send_rocs_from_asset_hub_rococo_to_asset_hub_westend() { >::balance(roc_at_asset_hub_westend, &AssetHubWestendReceiver::get()) }); - let amount = ASSET_HUB_ROCOCO_ED * 1_000; + let amount = ASSET_HUB_ROCOCO_ED * 1_000_000; send_asset_from_asset_hub_rococo_to_asset_hub_westend(roc_at_asset_hub_rococo, amount); AssetHubWestend::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/Cargo.toml index 6dcb57f41610..e9b4f8fb1800 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/Cargo.toml @@ -16,6 +16,7 @@ codec = { package = "parity-scale-codec", version = "3.4.0", default-features = # Substrate frame-support = { path = "../../../../../../../substrate/frame/support", default-features = false } pallet-assets = { path = "../../../../../../../substrate/frame/assets", default-features = false } +pallet-asset-conversion = { path = "../../../../../../../substrate/frame/asset-conversion", default-features = false } pallet-balances = { path = "../../../../../../../substrate/frame/balances", default-features = false } pallet-message-queue = { path = "../../../../../../../substrate/frame/message-queue" } sp-runtime = { path = "../../../../../../../substrate/primitives/runtime", default-features = false } diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs index 90a11d38f777..17255320f1de 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs @@ -55,7 +55,8 @@ pub use rococo_westend_system_emulated_network::{ }, westend_emulated_chain::WestendRelayPallet as WestendPallet, AssetHubRococoPara as AssetHubRococo, AssetHubRococoParaReceiver as AssetHubRococoReceiver, - AssetHubWestendPara as AssetHubWestend, AssetHubWestendParaReceiver as AssetHubWestendReceiver, + AssetHubRococoParaSender as AssetHubRococoSender, AssetHubWestendPara as AssetHubWestend, + AssetHubWestendParaReceiver as AssetHubWestendReceiver, AssetHubWestendParaSender as AssetHubWestendSender, BridgeHubRococoPara as BridgeHubRococo, BridgeHubWestendPara as BridgeHubWestend, BridgeHubWestendParaSender as BridgeHubWestendSender, WestendRelay as Westend, diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs index 21f4b4ee2356..a4b2719e9fdd 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs @@ -48,6 +48,49 @@ fn send_wnds_from_asset_hub_westend_to_asset_hub_rococo() { AssetHubRococo::para_id(), ); + AssetHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // setup a pool to pay xcm fees with `wnd_at_asset_hub_rococo` tokens + assert_ok!(::ForeignAssets::mint( + ::RuntimeOrigin::signed(AssetHubRococoSender::get()), + wnd_at_asset_hub_rococo.into(), + AssetHubRococoSender::get().into(), + 3_000_000_000_000, + )); + + assert_ok!(::AssetConversion::create_pool( + ::RuntimeOrigin::signed(AssetHubRococoSender::get()), + Box::new(Parent.into()), + Box::new(wnd_at_asset_hub_rococo), + )); + + assert_expected_events!( + AssetHubRococo, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::PoolCreated { .. }) => {}, + ] + ); + + assert_ok!(::AssetConversion::add_liquidity( + ::RuntimeOrigin::signed(AssetHubRococoSender::get()), + Box::new(Parent.into()), + Box::new(wnd_at_asset_hub_rococo), + 1_000_000_000_000, + 2_000_000_000_000, + 1, + 1, + AssetHubRococoSender::get().into() + )); + + assert_expected_events!( + AssetHubRococo, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::LiquidityAdded {..}) => {}, + ] + ); + }); + let wnds_in_reserve_on_ahw_before = ::account_data_of(sov_ahr_on_ahw.clone()).free; let sender_wnds_before = From 4401b7a51c3d85cfa1037b175010c1431cafe5b3 Mon Sep 17 00:00:00 2001 From: muharem Date: Wed, 10 Jan 2024 11:22:25 +0800 Subject: [PATCH 98/98] toml format --- cumulus/parachains/runtimes/assets/test-utils/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cumulus/parachains/runtimes/assets/test-utils/Cargo.toml b/cumulus/parachains/runtimes/assets/test-utils/Cargo.toml index 8410ca73633b..1b863499f04d 100644 --- a/cumulus/parachains/runtimes/assets/test-utils/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/test-utils/Cargo.toml @@ -65,8 +65,8 @@ std = [ "cumulus-test-relay-sproof-builder/std", "frame-support/std", "frame-system/std", - "pallet-assets/std", "pallet-asset-conversion/std", + "pallet-assets/std", "pallet-balances/std", "pallet-collator-selection/std", "pallet-session/std",