Skip to content

Commit

Permalink
Fix Liquidity proxy path selection (#1056)
Browse files Browse the repository at this point in the history
* Fix Liquidity proxy path selection

* Update pallets/liquidity-proxy/src/lib.rs

Co-authored-by: Mikhail Tagirov <dev.mikhail.tagirov@outlook.com>

* Apply suggestions

---------

Co-authored-by: Mikhail Tagirov <dev.mikhail.tagirov@outlook.com>
  • Loading branch information
vovac12 and wer1st committed May 27, 2024
1 parent 8eaeffe commit ee07be8
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 92 deletions.
31 changes: 9 additions & 22 deletions pallets/liquidity-proxy/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T: Config>(Vec<AssetIdOf<T>>);

#[derive(Debug, Eq, PartialEq)]
Expand Down Expand Up @@ -214,13 +215,14 @@ impl<T: Config> ExchangePath<T> {
}
}

#[derive(Eq, PartialEq, Encode, Decode)]
#[derive(Debug, Eq, PartialEq, Encode, Decode)]
pub struct QuoteInfo<AssetId: Ord, LiquiditySource> {
pub outcome: SwapOutcome<Balance, AssetId>,
pub amount_without_impact: Option<Balance>,
pub rewards: Rewards<AssetId>,
pub liquidity_sources: Vec<LiquiditySource>,
pub path: Vec<AssetId>,
pub route: ExchangeRoute<AssetId>,
}

fn merge_two_vectors_unique<T: PartialEq>(vec_1: &mut Vec<T>, vec_2: Vec<T>) {
Expand Down Expand Up @@ -397,7 +399,7 @@ impl<T: Config> Pallet<T> {
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)| {
Expand Down Expand Up @@ -587,14 +589,7 @@ impl<T: Config> Pallet<T> {
filter: LiquiditySourceFilter<T::DEXId, LiquiditySourceType>,
skip_info: bool,
deduce_fee: bool,
) -> Result<
(
QuoteInfo<AssetIdOf<T>, LiquiditySourceIdOf<T>>,
ExchangeRoute<AssetIdOf<T>>,
Weight,
),
DispatchError,
> {
) -> Result<(QuoteInfo<AssetIdOf<T>, LiquiditySourceIdOf<T>>, Weight), DispatchError> {
ensure!(
input_asset_id != output_asset_id,
Error::<T>::UnavailableExchangePath
Expand Down Expand Up @@ -629,16 +624,8 @@ impl<T: Config> Pallet<T> {
filter: &LiquiditySourceFilter<T::DEXId, LiquiditySourceType>,
skip_info: bool,
deduce_fee: bool,
) -> Result<
(
QuoteInfo<AssetIdOf<T>, LiquiditySourceIdOf<T>>,
ExchangeRoute<AssetIdOf<T>>,
Weight,
),
DispatchError,
> {
) -> Result<(QuoteInfo<AssetIdOf<T>, LiquiditySourceIdOf<T>>, Weight), DispatchError> {
let mut weight = Weight::zero();
let mut swaps = ExchangeRoute::<AssetIdOf<T>>::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(
Expand Down Expand Up @@ -667,13 +654,13 @@ impl<T: Config> Pallet<T> {
),
};
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,
}
})
Expand All @@ -700,7 +687,7 @@ impl<T: Config> Pallet<T> {
}
_ => acc,
})
.map(|quote| (quote, swaps, weight))
.map(|quote| (quote, weight))
}

/// Quote given pairs of assets using `amount_ctr` to construct [`QuoteAmount`] for each pair.
Expand Down Expand Up @@ -2236,7 +2223,7 @@ impl<T: Config> LiquidityProxyTrait<T::DEXId, T::AccountId, AssetIdOf<T>> 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.
Expand Down
22 changes: 22 additions & 0 deletions pallets/liquidity-proxy/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,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,
Expand Down Expand Up @@ -976,6 +992,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))),
Expand Down
58 changes: 35 additions & 23 deletions pallets/liquidity-proxy/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
use crate::mock::*;
use crate::test_utils::calculate_swap_batch_input_amount_with_adar_commission;
use crate::weights::WeightInfo;
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,
};
Expand Down Expand Up @@ -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::<Runtime>::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::<Runtime>::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");
Expand Down Expand Up @@ -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::<Runtime>::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);
});
}
2 changes: 1 addition & 1 deletion runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2926,7 +2926,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::<Balance, AssetId> {
).ok().map(|(quote_info, _)| liquidity_proxy_runtime_api::SwapOutcomeInfo::<Balance, AssetId> {
amount: quote_info.outcome.amount,
amount_without_impact: quote_info.amount_without_impact.unwrap_or(0),
fee: quote_info.outcome.fee,
Expand Down
85 changes: 39 additions & 46 deletions utils/remote-ext/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<B>(client: Arc<WsClient>) -> AnyResult<RemoteExternalities<B>>
async fn create_ext<B>(
client: Arc<WsClient>,
at: Option<H256>,
snapshot_path: Option<PathBuf>,
) -> AnyResult<RemoteExternalities<B>>
where
B: DeserializeOwned + BlockT,
B: DeserializeOwned + BlockT<Hash = H256>,
<B as 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::<B>::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()
},
))
Expand All @@ -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<H256>,
/// Sora snapshot path.
#[clap(long)]
snapshot_path: Option<PathBuf>,
/// Encoded extrinsic
#[clap(long)]
xt: String,
}

#[tokio::main]
Expand All @@ -50,44 +72,15 @@ async fn main() -> AnyResult<()> {
.build(cli.uri)
.await?;
let client = Arc::new(client);
let mut ext = create_ext::<framenode_runtime::Block>(client.clone()).await?;
let mut ext =
create_ext::<framenode_runtime::Block>(client.clone(), cli.at, cli.snapshot_path).await?;
let _res: AnyResult<()> = ext.execute_with(|| {
fn get_path_weight(input: AssetIdOf<Runtime>, output: AssetIdOf<Runtime>) -> Weight {
liquidity_proxy::Pallet::<Runtime>::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 =
<Runtime as framenode_runtime::order_book::Config>::WeightInfo::execute_market_order();
dbg!(execute_order_weight);

println!("\nexchange weights:");
let max_orders = <Runtime as framenode_runtime::order_book::Config>::HARD_MIN_MAX_RATIO
.try_into()
.unwrap();
for n in [1, max_orders / 2, max_orders] {
let exchange =
<Runtime as framenode_runtime::order_book::Config>::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(())
});
Expand Down

0 comments on commit ee07be8

Please sign in to comment.