Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Liquidity proxy path selection #1056

Merged
merged 6 commits into from
May 27, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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(Eq, PartialEq, Encode, Decode, Debug)]
vovac12 marked this conversation as resolved.
Show resolved Hide resolved
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 @@ -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,
Expand Down Expand Up @@ -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))),
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 @@ -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};
vovac12 marked this conversation as resolved.
Show resolved Hide resolved
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::<Vec<_>>();
vovac12 marked this conversation as resolved.
Show resolved Hide resolved
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 @@ -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::<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
Loading