From d97efab325259606a823ee8b79d384e2ed8aaa57 Mon Sep 17 00:00:00 2001 From: Vladimir Stepanenko Date: Fri, 24 May 2024 17:52:35 +0700 Subject: [PATCH 1/3] Fix Liquidity proxy path selection --- pallets/liquidity-proxy/src/lib.rs | 31 +++------- pallets/liquidity-proxy/src/mock.rs | 22 +++++++ pallets/liquidity-proxy/src/tests.rs | 58 +++++++++++-------- runtime/src/lib.rs | 2 +- utils/remote-ext/src/main.rs | 85 +++++++++++++--------------- 5 files changed, 106 insertions(+), 92 deletions(-) diff --git a/pallets/liquidity-proxy/src/lib.rs b/pallets/liquidity-proxy/src/lib.rs index 006936b5be..401c730dd3 100644 --- a/pallets/liquidity-proxy/src/lib.rs +++ b/pallets/liquidity-proxy/src/lib.rs @@ -100,6 +100,7 @@ pub const TECH_ACCOUNT_MAIN: &[u8] = b"main"; const REJECTION_WEIGHT: Weight = Weight::from_parts(u64::MAX, u64::MAX); /// Possible exchange paths for two assets. +#[derive(Clone)] pub struct ExchangePath(Vec>); #[derive(Debug, Eq, PartialEq)] @@ -214,13 +215,14 @@ impl ExchangePath { } } -#[derive(Eq, PartialEq, Encode, Decode)] +#[derive(Eq, PartialEq, Encode, Decode, Debug)] pub struct QuoteInfo { pub outcome: SwapOutcome, pub amount_without_impact: Option, pub rewards: Rewards, pub liquidity_sources: Vec, pub path: Vec, + pub route: ExchangeRoute, } fn merge_two_vectors_unique(vec_1: &mut Vec, vec_2: Vec) { @@ -397,7 +399,7 @@ impl Pallet { true, true, ) - .map(|(info, swaps, weight)| (info.path, swaps, weight))?; + .map(|(info, weight)| (info.path, info.route, weight))?; Self::exchange_sequence_with_desired_amount(dex_info, sender, receiver, &route, filter) .and_then(|(mut swap, sources, weight)| { @@ -587,14 +589,7 @@ impl Pallet { filter: LiquiditySourceFilter, skip_info: bool, deduce_fee: bool, - ) -> Result< - ( - QuoteInfo, LiquiditySourceIdOf>, - ExchangeRoute>, - Weight, - ), - DispatchError, - > { + ) -> Result<(QuoteInfo, LiquiditySourceIdOf>, Weight), DispatchError> { ensure!( input_asset_id != output_asset_id, Error::::UnavailableExchangePath @@ -629,16 +624,8 @@ impl Pallet { filter: &LiquiditySourceFilter, skip_info: bool, deduce_fee: bool, - ) -> Result< - ( - QuoteInfo, LiquiditySourceIdOf>, - ExchangeRoute>, - Weight, - ), - DispatchError, - > { + ) -> Result<(QuoteInfo, LiquiditySourceIdOf>, Weight), DispatchError> { let mut weight = Weight::zero(); - let mut swaps = ExchangeRoute::>::new(); let mut path_quote_iter = asset_paths.into_iter().map(|ExchangePath(atomic_path)| { let quote = match swap_variant { SwapVariant::WithDesiredInput => Self::quote_pairs_with_flexible_amount( @@ -667,13 +654,13 @@ impl Pallet { ), }; quote.map(|x| { - swaps = x.4; weight = weight.saturating_add(x.5); QuoteInfo { outcome: x.0, amount_without_impact: x.1, rewards: x.2, liquidity_sources: x.3, + route: x.4, path: atomic_path, } }) @@ -700,7 +687,7 @@ impl Pallet { } _ => acc, }) - .map(|quote| (quote, swaps, weight)) + .map(|quote| (quote, weight)) } /// Quote given pairs of assets using `amount_ctr` to construct [`QuoteAmount`] for each pair. @@ -2236,7 +2223,7 @@ impl LiquidityProxyTrait> for Pa true, deduce_fee, ) - .map(|(quote_info, _, _)| quote_info.outcome) + .map(|(quote_info, _)| quote_info.outcome) } /// Applies trivial routing (via Base Asset), resulting in a poly-swap which may contain several individual swaps. diff --git a/pallets/liquidity-proxy/src/mock.rs b/pallets/liquidity-proxy/src/mock.rs index 6eee815231..5c0190a851 100644 --- a/pallets/liquidity-proxy/src/mock.rs +++ b/pallets/liquidity-proxy/src/mock.rs @@ -573,6 +573,22 @@ impl Default for ExtBuilder { AssetName(b"Tether".to_vec()), DEFAULT_BALANCE_PRECISION, ), + ( + alice(), + XSTUSD, + balance!(0), + AssetSymbol(b"XSTUSD".to_vec()), + AssetName(b"XSTUSD".to_vec()), + DEFAULT_BALANCE_PRECISION, + ), + ( + alice(), + XST, + balance!(0), + AssetSymbol(b"XST".to_vec()), + AssetName(b"XST".to_vec()), + DEFAULT_BALANCE_PRECISION, + ), ( alice(), KSM, @@ -975,6 +991,12 @@ impl ExtBuilder { } } + pub fn with_xyk_pool_xstusd(mut self) -> Self { + self.xyk_reserves + .push((DEX_D_ID, XSTUSD, (balance!(1000), balance!(1000)))); + self + } + pub fn with_xyk_pool(mut self) -> Self { self.xyk_reserves = vec![ (DEX_A_ID, USDT, (balance!(1000), balance!(1000))), diff --git a/pallets/liquidity-proxy/src/tests.rs b/pallets/liquidity-proxy/src/tests.rs index c4216dc82f..a57bba57d3 100644 --- a/pallets/liquidity-proxy/src/tests.rs +++ b/pallets/liquidity-proxy/src/tests.rs @@ -28,9 +28,9 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::mock::*; use crate::test_utils::calculate_swap_batch_input_amount_with_adar_commission; use crate::weights::WeightInfo; +use crate::{mock::*, ExchangePath}; use crate::{test_utils, BatchReceiverInfo, Error, QuoteInfo, SwapBatchInfo}; use common::prelude::{ AssetName, AssetSymbol, Balance, FixedWrapper, OutcomeFee, QuoteAmount, SwapAmount, @@ -1503,28 +1503,6 @@ fn test_is_path_available_should_pass_5() { let mut ext = ExtBuilder::default().build(); ext.execute_with(|| { use LiquiditySourceType::*; - assets::Pallet::::register_asset_id( - alice(), - XST.into(), - AssetSymbol(b"XST".to_vec()), - AssetName(b"SORA Synthetics".to_vec()), - 0, - Balance::from(0u32), - true, - None, - None, - ).expect("failed to register XST asset"); - assets::Pallet::::register_asset_id( - alice(), - XSTUSD.into(), - AssetSymbol(b"XSTUSD".to_vec()), - AssetName(b"SORA Synthetic USD".to_vec()), - 0, - Balance::from(0u32), - true, - None, - None, - ).expect("failed to register XSTUSD asset"); TradingPair::register(RuntimeOrigin::signed(alice()), 0, XOR, VAL).expect("failed to register pair"); TradingPair::register(RuntimeOrigin::signed(alice()), 0, XOR, PSWAP).expect("failed to register pair"); TradingPair::register(RuntimeOrigin::signed(alice()), 0, XOR, XST).expect("failed to register pair"); @@ -3642,3 +3620,37 @@ fn test_xorless_transfer_fails_on_transfer() { ); }); } + +#[test] +fn test_select_best_path() { + let mut ext = ExtBuilder::default() + .with_xyk_pool() + .with_xyk_pool_xstusd() + .build(); + ext.execute_with(|| { + let dex_info = DexManager::dex_id(DEX_D_ID).unwrap(); + let asset_paths = ExchangePath::::new_trivial(&dex_info, XSTUSD, XST).unwrap(); + let reversed_paths = asset_paths.iter().cloned().rev().collect::>(); + let result = LiquidityProxy::select_best_path( + &dex_info, + asset_paths, + common::prelude::SwapVariant::WithDesiredInput, + balance!(1), + &mcbc_excluding_filter(DEX_D_ID), + false, + true, + ) + .unwrap(); + let reversed_result = LiquidityProxy::select_best_path( + &dex_info, + reversed_paths, + common::prelude::SwapVariant::WithDesiredInput, + balance!(1), + &mcbc_excluding_filter(DEX_D_ID), + false, + true, + ) + .unwrap(); + assert_eq!(result, reversed_result); + }); +} diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 8a8a41311f..bd89252b92 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -2925,7 +2925,7 @@ impl_runtime_apis! { LiquiditySourceFilter::with_mode(dex_id, filter_mode, selected_source_types), false, true, - ).ok().map(|(quote_info, _, _)| liquidity_proxy_runtime_api::SwapOutcomeInfo:: { + ).ok().map(|(quote_info, _)| liquidity_proxy_runtime_api::SwapOutcomeInfo:: { amount: quote_info.outcome.amount, amount_without_impact: quote_info.amount_without_impact.unwrap_or(0), fee: quote_info.outcome.fee, diff --git a/utils/remote-ext/src/main.rs b/utils/remote-ext/src/main.rs index d1ec9ad23d..220361c281 100644 --- a/utils/remote-ext/src/main.rs +++ b/utils/remote-ext/src/main.rs @@ -2,30 +2,42 @@ #![allow(clippy::all)] use clap::Parser; -use common::{DEXId, FilterMode}; -use frame_remote_externalities::{Builder, Mode, OfflineConfig, OnlineConfig, RemoteExternalities}; +use codec::Decode; +use frame_remote_externalities::{ + Builder, Mode, OfflineConfig, OnlineConfig, RemoteExternalities, SnapshotConfig, +}; use jsonrpsee::ws_client::{WsClient, WsClientBuilder}; +use sp_core::H256; use sp_runtime::{traits::Block as BlockT, DeserializeOwned}; use anyhow::Result as AnyResult; -use framenode_runtime::assets::AssetIdOf; -use framenode_runtime::order_book::WeightInfo; -use framenode_runtime::{Runtime, Weight}; +use sp_runtime::traits::Dispatchable; +use std::path::PathBuf; use std::sync::Arc; -async fn create_ext(client: Arc) -> AnyResult> +async fn create_ext( + client: Arc, + at: Option, + snapshot_path: Option, +) -> AnyResult> where - B: DeserializeOwned + BlockT, + B: DeserializeOwned + BlockT, ::Header: DeserializeOwned, { + let snapshot_path = snapshot_path.unwrap_or_else(|| match at { + Some(at) => format!("state_snapshot_{}", at).into(), + None => "state_snapshot".to_string().into(), + }); + let state_snapshot = SnapshotConfig::new(&snapshot_path); let res = Builder::::new() .mode(Mode::OfflineOrElseOnline( OfflineConfig { - state_snapshot: "state_snapshot".to_string().into(), + state_snapshot: state_snapshot.clone(), }, OnlineConfig { transport: client.into(), - state_snapshot: Some("state_snapshot".to_string().into()), + state_snapshot: Some(state_snapshot), + at, ..Default::default() }, )) @@ -38,7 +50,17 @@ where #[derive(Debug, Clone, Parser)] struct Cli { /// Sora node endpoint. + #[clap(long)] uri: String, + /// Sora block hash. + #[clap(long)] + at: Option, + /// Sora snapshot path. + #[clap(long)] + snapshot_path: Option, + /// Encoded extrinsic + #[clap(long)] + xt: String, } #[tokio::main] @@ -50,44 +72,15 @@ async fn main() -> AnyResult<()> { .build(cli.uri) .await?; let client = Arc::new(client); - let mut ext = create_ext::(client.clone()).await?; + let mut ext = + create_ext::(client.clone(), cli.at, cli.snapshot_path).await?; let _res: AnyResult<()> = ext.execute_with(|| { - fn get_path_weight(input: AssetIdOf, output: AssetIdOf) -> Weight { - liquidity_proxy::Pallet::::swap_weight( - &DEXId::Polkaswap.into(), - &input, - &output, - &Vec::new(), - &FilterMode::Disabled, - ) - } - // Base -> Basic - let path_2_weight = get_path_weight(common::XOR, common::PSWAP); - - // Basic -> Basic - let path_3_weight = get_path_weight(common::VAL, common::PSWAP); - - // Synthetic -> Basic - let path_4_weight = get_path_weight(common::XSTUSD, common::PSWAP); - - dbg!(path_2_weight); - dbg!(path_3_weight); - dbg!(path_4_weight); - - let execute_order_weight = - ::WeightInfo::execute_market_order(); - dbg!(execute_order_weight); - - println!("\nexchange weights:"); - let max_orders = ::HARD_MIN_MAX_RATIO - .try_into() - .unwrap(); - for n in [1, max_orders / 2, max_orders] { - let exchange = - ::WeightInfo::exchange(n); - println!("{n} order(-s):"); - dbg!(exchange); - println!(); + let xt_encoded = hex::decode(&cli.xt).unwrap(); + let xt = framenode_runtime::UncheckedExtrinsic::decode(&mut &xt_encoded[..]).unwrap(); + if let Some((account, _signature, _extra)) = xt.signature { + xt.function + .dispatch(framenode_runtime::RuntimeOrigin::signed(account)) + .unwrap(); } Ok(()) }); From 74d2b56878563ac778a02a38926784432b8767d4 Mon Sep 17 00:00:00 2001 From: Vladimir Stepanenko Date: Mon, 27 May 2024 07:20:01 +0700 Subject: [PATCH 2/3] Update pallets/liquidity-proxy/src/lib.rs Co-authored-by: Mikhail Tagirov --- pallets/liquidity-proxy/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/liquidity-proxy/src/lib.rs b/pallets/liquidity-proxy/src/lib.rs index 401c730dd3..aa4a8f52f7 100644 --- a/pallets/liquidity-proxy/src/lib.rs +++ b/pallets/liquidity-proxy/src/lib.rs @@ -215,7 +215,7 @@ impl ExchangePath { } } -#[derive(Eq, PartialEq, Encode, Decode, Debug)] +#[derive(Debug, Eq, PartialEq, Encode, Decode)] pub struct QuoteInfo { pub outcome: SwapOutcome, pub amount_without_impact: Option, From befc5242ff859172126e925f1302d870599f1dae Mon Sep 17 00:00:00 2001 From: Vladimir Stepanenko Date: Mon, 27 May 2024 16:44:13 +0700 Subject: [PATCH 3/3] Apply suggestions --- pallets/liquidity-proxy/src/tests.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pallets/liquidity-proxy/src/tests.rs b/pallets/liquidity-proxy/src/tests.rs index a57bba57d3..de24f03804 100644 --- a/pallets/liquidity-proxy/src/tests.rs +++ b/pallets/liquidity-proxy/src/tests.rs @@ -28,10 +28,10 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::mock::*; use crate::test_utils::calculate_swap_batch_input_amount_with_adar_commission; use crate::weights::WeightInfo; -use crate::{mock::*, ExchangePath}; -use crate::{test_utils, BatchReceiverInfo, Error, QuoteInfo, SwapBatchInfo}; +use crate::{test_utils, BatchReceiverInfo, Error, ExchangePath, QuoteInfo, SwapBatchInfo}; use common::prelude::{ AssetName, AssetSymbol, Balance, FixedWrapper, OutcomeFee, QuoteAmount, SwapAmount, }; @@ -3630,7 +3630,7 @@ fn test_select_best_path() { ext.execute_with(|| { let dex_info = DexManager::dex_id(DEX_D_ID).unwrap(); let asset_paths = ExchangePath::::new_trivial(&dex_info, XSTUSD, XST).unwrap(); - let reversed_paths = asset_paths.iter().cloned().rev().collect::>(); + let reversed_paths = asset_paths.iter().cloned().rev().collect(); let result = LiquidityProxy::select_best_path( &dex_info, asset_paths,