diff --git a/crates/driver/src/domain/quote.rs b/crates/driver/src/domain/quote.rs index ce2b903c79..23c7d2fa2c 100644 --- a/crates/driver/src/domain/quote.rs +++ b/crates/driver/src/domain/quote.rs @@ -151,9 +151,7 @@ impl Order { solver::Liquidity::Skip => Default::default(), }; - let auction = self - .single_order_auction(eth, tokens, solver.quote_using_limit_orders()) - .await?; + let auction = self.single_order_auction(eth, tokens).await?; let auction = competition .risk_detector .filter_unsupported_orders_in_auction(auction) @@ -193,7 +191,6 @@ impl Order { &self, eth: &Ethereum, tokens: &infra::tokens::Fetcher, - quote_using_limit_orders: bool, ) -> Result { let tokens = tokens.get(&[self.buy().token, self.sell().token]).await; @@ -213,11 +210,9 @@ impl Order { buy: self.buy(), sell: self.sell(), side: self.side, - kind: if quote_using_limit_orders { - competition::order::Kind::Limit - } else { - competition::order::Kind::Market - }, + // Quotes always use limit orders so that the engine + // determines the fee (see `Order::solver_determines_fee`). + kind: competition::order::Kind::Limit, pre_interactions: Default::default(), post_interactions: Default::default(), sell_token_balance: competition::order::SellTokenBalance::Erc20, diff --git a/crates/driver/src/infra/config/file/load.rs b/crates/driver/src/infra/config/file/load.rs index 4b53756e4e..72ef5ef486 100644 --- a/crates/driver/src/infra/config/file/load.rs +++ b/crates/driver/src/infra/config/file/load.rs @@ -77,7 +77,6 @@ pub async fn load(chain: Chain, path: &Path) -> infra::Config { }, request_headers: solver_config.request_headers, fee_handler: solver_config.fee_handler, - quote_using_limit_orders: solver_config.quote_using_limit_orders, fast_path_enabled: solver_config.fast_path_enabled, merge_solutions: match solver_config.merge_solutions { true => SolutionMerging::Allowed { diff --git a/crates/driver/src/infra/config/file/mod.rs b/crates/driver/src/infra/config/file/mod.rs index fdd3f9502a..a3e8ccb0f0 100644 --- a/crates/driver/src/infra/config/file/mod.rs +++ b/crates/driver/src/infra/config/file/mod.rs @@ -275,10 +275,6 @@ struct SolverConfig { #[serde(default)] fee_handler: FeeHandler, - /// Use limit orders for quoting - #[serde(default)] - quote_using_limit_orders: bool, - /// Whether this solver supports fast-path (out-of-competition) execution. #[serde(default)] fast_path_enabled: bool, diff --git a/crates/driver/src/infra/solver/mod.rs b/crates/driver/src/infra/solver/mod.rs index 3271018b64..e7e3fcb03e 100644 --- a/crates/driver/src/infra/solver/mod.rs +++ b/crates/driver/src/infra/solver/mod.rs @@ -186,9 +186,6 @@ pub struct Config { pub request_headers: HashMap, /// Determines whether the `solver` or the `driver` handles the fees pub fee_handler: FeeHandler, - /// Use limit orders for quoting - /// TODO: Remove once all solvers are moved to use limit orders for quoting - pub quote_using_limit_orders: bool, /// Whether this solver supports fast-path (out-of-competition) execution. pub fast_path_enabled: bool, pub merge_solutions: SolutionMerging, @@ -320,11 +317,6 @@ impl Solver { self.config.timeouts } - /// Use limit orders for quoting instead of market orders - pub fn quote_using_limit_orders(&self) -> bool { - self.config.quote_using_limit_orders - } - /// Whether this solver supports fast-path (out-of-competition) execution. pub fn fast_path_enabled(&self) -> bool { self.config.fast_path_enabled @@ -601,7 +593,6 @@ mod tests { }, request_headers: Default::default(), fee_handler: FeeHandler::Driver, - quote_using_limit_orders: false, fast_path_enabled: false, merge_solutions: SolutionMerging::Forbidden, s3: None, diff --git a/crates/driver/src/tests/cases/quote.rs b/crates/driver/src/tests/cases/quote.rs index e7c46d91fd..911596be59 100644 --- a/crates/driver/src/tests/cases/quote.rs +++ b/crates/driver/src/tests/cases/quote.rs @@ -30,26 +30,25 @@ fn extract_buy_amount(response_body: &str, sell_amount: eth::U256) -> eth::U256 sell_amount * price_low / price_high } -/// Run a matrix of tests for all meaningful combinations of order kind and -/// side, verifying that they get quoted successfully. +/// Run a matrix of tests for all meaningful order sides, verifying that they +/// get quoted successfully. Quotes always use limit orders, so there is no +/// order-kind dimension. #[tokio::test] #[ignore] async fn matrix() { for side in [order::Side::Buy, order::Side::Sell] { - for kind in [order::Kind::Market, order::Kind::Limit] { - let test = tests::setup() - .name(format!("{side:?} {kind:?}")) - .pool(ab_pool()) - .order(ab_order().side(side).kind(kind)) - .solution(ab_solution()) - .quote() - .done() - .await; - - let quote = test.quote().await; - - quote.ok().amount().interactions(); - } + let test = tests::setup() + .name(format!("{side:?}")) + .pool(ab_pool()) + .order(ab_order().side(side)) + .solution(ab_solution()) + .quote() + .done() + .await; + + let quote = test.quote().await; + + quote.ok().amount().interactions(); } } diff --git a/crates/driver/src/tests/setup/mod.rs b/crates/driver/src/tests/setup/mod.rs index 624ad3d5d2..8236a3e7c7 100644 --- a/crates/driver/src/tests/setup/mod.rs +++ b/crates/driver/src/tests/setup/mod.rs @@ -318,7 +318,7 @@ impl Default for Order { partial: Default::default(), created: u32::MIN, valid_to: u32::MAX, - kind: order::Kind::Market, + kind: order::Kind::Limit, solver_fee: Default::default(), name: Default::default(), surplus_factor: DEFAULT_SURPLUS_FACTOR.ether().into_wei(), diff --git a/crates/driver/src/tests/setup/solver.rs b/crates/driver/src/tests/setup/solver.rs index f701a4b7d0..5810560969 100644 --- a/crates/driver/src/tests/setup/solver.rs +++ b/crates/driver/src/tests/setup/solver.rs @@ -172,7 +172,6 @@ impl Solver { }, "partiallyFillable": matches!(quote.order.partial, Partial::Yes { .. }), "class": match quote.order.kind { - _ if config.quote => "market", order::Kind::Market => "market", order::Kind::Limit => "limit", }, diff --git a/crates/e2e/src/setup/colocation.rs b/crates/e2e/src/setup/colocation.rs index fc63d1c8fd..36da366f9d 100644 --- a/crates/e2e/src/setup/colocation.rs +++ b/crates/e2e/src/setup/colocation.rs @@ -128,22 +128,14 @@ pub fn start_driver( contracts: &Contracts, solvers: Vec, liquidity: LiquidityProvider, - quote_using_limit_orders: bool, ) -> JoinHandle<()> { - start_driver_with_config_override( - contracts, - solvers, - liquidity, - quote_using_limit_orders, - None, - ) + start_driver_with_config_override(contracts, solvers, liquidity, None) } pub fn start_driver_with_config_override( contracts: &Contracts, solvers: Vec, liquidity: LiquidityProvider, - quote_using_limit_orders: bool, config_override: Option<&str>, ) -> JoinHandle<()> { let base_tokens: HashSet<_> = solvers @@ -181,7 +173,6 @@ endpoint = "{endpoint}" relative-slippage = "0.1" account = "{account}" merge-solutions = {merge_solutions} -quote-using-limit-orders = {quote_using_limit_orders} enable-simulation-bad-token-detection = true enable-metrics-bad-order-detection = true http-time-buffer = "100ms" diff --git a/crates/e2e/src/setup/services.rs b/crates/e2e/src/setup/services.rs index b464105f60..60ac37d49a 100644 --- a/crates/e2e/src/setup/services.rs +++ b/crates/e2e/src/setup/services.rs @@ -273,7 +273,6 @@ impl<'a> Services<'a> { .await, ], colocation::LiquidityProvider::UniswapV2, - false, ); let test_quoter = ExternalSolver::new("test_quoter", "http://localhost:11088/test_solver"); @@ -406,7 +405,6 @@ impl<'a> Services<'a> { self.contracts, solvers, colocation::LiquidityProvider::UniswapV2, - false, ); self.start_autopilot(Some(Duration::from_secs(11)), autopilot_config) diff --git a/crates/e2e/tests/e2e/autopilot_leader.rs b/crates/e2e/tests/e2e/autopilot_leader.rs index f4870c6174..e6d9ec50d3 100644 --- a/crates/e2e/tests/e2e/autopilot_leader.rs +++ b/crates/e2e/tests/e2e/autopilot_leader.rs @@ -76,7 +76,6 @@ async fn dual_autopilot_only_leader_produces_auctions(web3: Web3) { .await, ], colocation::LiquidityProvider::UniswapV2, - false, ); let services = Services::new(&onchain).await; diff --git a/crates/e2e/tests/e2e/buffers.rs b/crates/e2e/tests/e2e/buffers.rs index 87f23c1b6e..18d3c4fbdf 100644 --- a/crates/e2e/tests/e2e/buffers.rs +++ b/crates/e2e/tests/e2e/buffers.rs @@ -62,7 +62,6 @@ async fn onchain_settlement_without_liquidity(web3: Web3) { .await, ], colocation::LiquidityProvider::UniswapV2, - false, ); let services = Services::new(&onchain).await; services diff --git a/crates/e2e/tests/e2e/ethflow.rs b/crates/e2e/tests/e2e/ethflow.rs index db830d2a07..a0e32fc44f 100644 --- a/crates/e2e/tests/e2e/ethflow.rs +++ b/crates/e2e/tests/e2e/ethflow.rs @@ -545,7 +545,8 @@ async fn eth_flow_indexing_after_refund(web3: Web3) { let services = Services::new(&onchain).await; services.start_protocol(solver).await; - // Create an order that only exists to be cancelled. + // Create an order that only exists to be cancelled. It still needs a + // realistic amount: solvers don't quote dust that can't cover the gas fee. let valid_to = timestamp_of_current_block_in_seconds(&web3.provider) .await .unwrap() @@ -554,7 +555,7 @@ async fn eth_flow_indexing_after_refund(web3: Web3) { &test_submit_quote( &services, &(EthFlowTradeIntent { - sell_amount: alloy::primitives::U256::from(42), + sell_amount: 1u64.eth(), buy_token: *dai.address(), receiver: Address::repeat_byte(42), }) diff --git a/crates/e2e/tests/e2e/jit_orders.rs b/crates/e2e/tests/e2e/jit_orders.rs index 2b3ed9d862..dd94bc8380 100644 --- a/crates/e2e/tests/e2e/jit_orders.rs +++ b/crates/e2e/tests/e2e/jit_orders.rs @@ -88,7 +88,6 @@ async fn single_limit_order_test(web3: Web3) { }, ], colocation::LiquidityProvider::UniswapV2, - false, ); // We start the quoter as the baseline solver, and the mock solver as the diff --git a/crates/e2e/tests/e2e/limit_orders.rs b/crates/e2e/tests/e2e/limit_orders.rs index 9564afd274..628cdbc816 100644 --- a/crates/e2e/tests/e2e/limit_orders.rs +++ b/crates/e2e/tests/e2e/limit_orders.rs @@ -503,7 +503,6 @@ async fn two_limit_orders_multiple_winners_test(web3: Web3) { .await, ], colocation::LiquidityProvider::UniswapV2, - false, ); let services = Services::new(&onchain).await; @@ -746,7 +745,6 @@ async fn too_many_limit_orders_test(web3: Web3) { .await, ], colocation::LiquidityProvider::UniswapV2, - false, ); services @@ -849,7 +847,6 @@ async fn limit_does_not_apply_to_in_market_orders_test(web3: Web3) { .await, ], colocation::LiquidityProvider::UniswapV2, - false, ); services diff --git a/crates/e2e/tests/e2e/liquidity.rs b/crates/e2e/tests/e2e/liquidity.rs index 5133e5e4e1..c345a4fde3 100644 --- a/crates/e2e/tests/e2e/liquidity.rs +++ b/crates/e2e/tests/e2e/liquidity.rs @@ -193,7 +193,6 @@ async fn zero_ex_liquidity(web3: Web3) { colocation::LiquidityProvider::ZeroEx { api_port: zeroex_api_port, }, - false, ); services diff --git a/crates/e2e/tests/e2e/liquidity_source_notification.rs b/crates/e2e/tests/e2e/liquidity_source_notification.rs index 38c7fa9194..5ded7a7987 100644 --- a/crates/e2e/tests/e2e/liquidity_source_notification.rs +++ b/crates/e2e/tests/e2e/liquidity_source_notification.rs @@ -193,7 +193,6 @@ async fn liquidity_source_notification(web3: Web3) { }, ], colocation::LiquidityProvider::UniswapV2, - false, Some(&format!( r#" [liquidity-sources-notifier] diff --git a/crates/e2e/tests/e2e/order_cancellation.rs b/crates/e2e/tests/e2e/order_cancellation.rs index fdbf0c2c45..f4bd46eb7a 100644 --- a/crates/e2e/tests/e2e/order_cancellation.rs +++ b/crates/e2e/tests/e2e/order_cancellation.rs @@ -68,7 +68,6 @@ async fn order_cancellation(web3: Web3) { .await, ], colocation::LiquidityProvider::UniswapV2, - false, ); services .start_autopilot( diff --git a/crates/e2e/tests/e2e/parallel_settlement.rs b/crates/e2e/tests/e2e/parallel_settlement.rs index 42d1a96d41..5b6beafc88 100644 --- a/crates/e2e/tests/e2e/parallel_settlement.rs +++ b/crates/e2e/tests/e2e/parallel_settlement.rs @@ -87,7 +87,6 @@ async fn test_parallel_settlement_submission(web3: Web3) { onchain.contracts(), vec![solver_engine], colocation::LiquidityProvider::UniswapV2, - false, ); // Wait for the driver to become available. diff --git a/crates/e2e/tests/e2e/place_order_with_quote.rs b/crates/e2e/tests/e2e/place_order_with_quote.rs index b62b6d6591..f5efa37f31 100644 --- a/crates/e2e/tests/e2e/place_order_with_quote.rs +++ b/crates/e2e/tests/e2e/place_order_with_quote.rs @@ -240,7 +240,6 @@ async fn fallback_native_price_estimator(web3: Web3) { .await, ], colocation::LiquidityProvider::UniswapV2, - false, ); let (manual_shutdown, control) = ShutdownController::new_manual_shutdown(); diff --git a/crates/e2e/tests/e2e/pool_indexer.rs b/crates/e2e/tests/e2e/pool_indexer.rs index c27a08051a..9256577aaa 100644 --- a/crates/e2e/tests/e2e/pool_indexer.rs +++ b/crates/e2e/tests/e2e/pool_indexer.rs @@ -463,7 +463,6 @@ max-pools-to-initialize = 10 onchain.contracts(), vec![baseline_solver], colocation::LiquidityProvider::UniswapV2, - false, Some(&config_override), ); diff --git a/crates/e2e/tests/e2e/quoting.rs b/crates/e2e/tests/e2e/quoting.rs index 44e993d3dd..bd0572ee10 100644 --- a/crates/e2e/tests/e2e/quoting.rs +++ b/crates/e2e/tests/e2e/quoting.rs @@ -348,7 +348,6 @@ async fn quote_timeout(web3: Web3) { }, ], colocation::LiquidityProvider::UniswapV2, - false, ); /// The default quote timeout used when the user does not override it. @@ -530,7 +529,6 @@ async fn quote_custom_solver_errors(web3: Web3) { }, ], colocation::LiquidityProvider::UniswapV2, - false, ); services @@ -656,7 +654,6 @@ async fn native_price_custom_solver_errors(web3: Web3) { }, ], colocation::LiquidityProvider::UniswapV2, - false, ); services @@ -770,7 +767,6 @@ async fn quote_custom_solver_errors_prioritized(web3: Web3) { }, ], colocation::LiquidityProvider::UniswapV2, - false, ); services diff --git a/crates/e2e/tests/e2e/solver_competition.rs b/crates/e2e/tests/e2e/solver_competition.rs index 945de18798..561c315cb8 100644 --- a/crates/e2e/tests/e2e/solver_competition.rs +++ b/crates/e2e/tests/e2e/solver_competition.rs @@ -93,7 +93,6 @@ async fn solver_competition(web3: Web3) { .await, ], colocation::LiquidityProvider::UniswapV2, - false, ); let services = Services::new(&onchain).await; @@ -279,7 +278,6 @@ async fn wrong_solution_submission_address(web3: Web3) { .await, ], colocation::LiquidityProvider::UniswapV2, - false, ); let services = Services::new(&onchain).await; @@ -446,7 +444,6 @@ async fn store_filtered_solutions(web3: Web3) { }, ], colocation::LiquidityProvider::UniswapV2, - false, ); // We start the quoter as the baseline solver, and the mock solver as the @@ -734,7 +731,6 @@ async fn cannot_replace_order_bid_on_by_non_winning_solution(web3: Web3) { }, ], colocation::LiquidityProvider::UniswapV2, - false, ); let config = Configuration::test_no_drivers(); diff --git a/crates/solvers/src/domain/dex/mod.rs b/crates/solvers/src/domain/dex/mod.rs index a5e34c63aa..d60c1b3779 100644 --- a/crates/solvers/src/domain/dex/mod.rs +++ b/crates/solvers/src/domain/dex/mod.rs @@ -116,7 +116,11 @@ impl Swap { simulator: &infra::dex::Simulator, gas_offset: eth::Gas, ) -> Option { - let gas = if order.class == order::Class::Limit { + // Only simulate gas for limit orders (whose fee depends on it) in + // proper auctions, i.e. when the auction has a sell token price. For + // quotes and market orders we use the gas indicated by the DEX to save + // time. + let gas = if order.class == order::Class::Limit && sell_token.is_some() { match simulator.gas(order.owner(), &self).await { Ok(value) => value, Err(infra::dex::simulator::Error::SettlementContractIsOwner) => self.gas, @@ -126,8 +130,6 @@ impl Swap { } } } else { - // We are fine with just using heuristic gas for market orders, - // since it doesn't really play a role in the final solution. self.gas }; diff --git a/crates/solvers/src/domain/solution.rs b/crates/solvers/src/domain/solution.rs index 769695344c..71fc4f1bd3 100644 --- a/crates/solvers/src/domain/solution.rs +++ b/crates/solvers/src/domain/solution.rs @@ -230,18 +230,18 @@ impl Single { } let fee = if order.solver_determines_fee() { - // TODO: If the order has signed `fee` amount already, we should - // discount it from the surplus fee. ATM, users would pay both a - // full order fee as well as a solver computed fee. Note that this - // is fine for now, since there is no way to create limit orders - // with non-zero fees. - eth::SellTokenAmount( - sell_token?.ether_value(eth::Ether( - swap.0 - .checked_add(gas_offset.0)? - .checked_mul(gas_price.0.0)?, - ))?, - ) + match sell_token { + Some(price) => eth::SellTokenAmount( + price.ether_value(eth::Ether( + swap.0 + .checked_add(gas_offset.0)? + .checked_mul(gas_price.0.0)?, + ))?, + ), + // For quote auctions (which don't contain native prices) we fall back to a zero + // fee. The orderbook API will estimate a proper fee itself. + None => Default::default(), + } } else { // Orders whose fee the solver doesn't determine (market orders, // whose fee was pre-determined by the protocol) are not charged diff --git a/crates/solvers/src/tests/okx/limit_order_quoting.rs b/crates/solvers/src/tests/okx/limit_order_quoting.rs new file mode 100644 index 0000000000..004255a3e2 --- /dev/null +++ b/crates/solvers/src/tests/okx/limit_order_quoting.rs @@ -0,0 +1,257 @@ +//! This test ensures that dex solvers can quote limit-class orders even when +//! the auction carries no token reference prices, which is the shape of quote +//! auctions. Without a reference price the solver cannot convert its gas cost +//! into a sell-token fee, so it falls back to a zero fee (which is never +//! charged on the quote path) and skips the gas simulation (whose result the +//! orderbook replaces with its own measurement). + +use { + crate::tests::{self, mock}, + serde_json::json, +}; + +#[tokio::test] +async fn sell() { + let api = mock::http::setup(vec![ + mock::http::Expectation::Get { + path: mock::http::Path::exact( + "swap?chainIndex=1\ + &amount=1000000000000000000\ + &fromTokenAddress=0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2\ + &toTokenAddress=0xe41d2489571d322189246dafa5ebde1f4699f498\ + &slippagePercent=0.01\ + &userWalletAddress=0x9008d19f58aabd9ed0d60971565aa8510560ab41\ + &swapReceiverAddress=0x9008d19f58aabd9ed0d60971565aa8510560ab41\ + &swapMode=exactIn\ + &priceImpactProtectionPercent=1" + ), + res: json!( + { + "code":"0", + "data":[ + { + "routerResult":{ + "chainId":"1", + "dexRouterList":[ + { + "router":"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2--0xe41d2489571d322189246dafa5ebde1f4699f498", + "routerPercent":"100", + "subRouterList":[ + { + "dexProtocol":[ + { + "dexName":"Uniswap V3", + "percent":"100" + } + ], + "fromToken":{ + "decimal":"18", + "isHoneyPot":false, + "taxRate":"0", + "tokenContractAddress":"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "tokenSymbol":"WETH", + "tokenUnitPrice":"3315.553196726842565048" + }, + "toToken":{ + "decimal":"18", + "isHoneyPot":false, + "taxRate":"0", + "tokenContractAddress":"0xe41d2489571d322189246dafa5ebde1f4699f498", + "tokenSymbol":"ZRX", + "tokenUnitPrice":"0.504455838152300152" + } + } + ] + } + ], + "estimateGasFee":"135000", + "fromToken":{ + "decimal":"18", + "isHoneyPot":false, + "taxRate":"0", + "tokenContractAddress":"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "tokenSymbol":"WETH", + "tokenUnitPrice":"3315.553196726842565048" + }, + "fromTokenAmount":"1000000000000000000", + "priceImpactPercentage":"-0.25", + "quoteCompareList":[ + { + "amountOut":"6556.259156432631386442", + "dexLogo":"https://static.okx.com/cdn/wallet/logo/UNI.png", + "dexName":"Uniswap V3", + "tradeFee":"2.3554356342513966" + }, + { + "amountOut":"6375.198002761542738881", + "dexLogo":"https://static.okx.com/cdn/wallet/logo/UNI.png", + "dexName":"Uniswap V2", + "tradeFee":"3.34995290204643072" + }, + { + "amountOut":"4456.799978982369793812", + "dexLogo":"https://static.okx.com/cdn/wallet/logo/UNI.png", + "dexName":"Uniswap V1", + "tradeFee":"4.64638467513839940864" + }, + { + "amountOut":"2771.072269036022134969", + "dexLogo":"https://static.okx.com/cdn/wallet/logo/SUSHI.png", + "dexName":"SushiSwap", + "tradeFee":"3.34995290204643072" + } + ], + "toToken":{ + "decimal":"18", + "isHoneyPot":false, + "taxRate":"0", + "tokenContractAddress":"0xe41d2489571d322189246dafa5ebde1f4699f498", + "tokenSymbol":"ZRX", + "tokenUnitPrice":"0.504455838152300152" + }, + "toTokenAmount":"6556259156432631386442", + "tradeFee":"2.3554356342513966" + }, + "tx":{ + "data":"0x0d5f0e3b00000000000000000001a0cf2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a0000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000015fdc8278903f7f31c10000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000100000000000000000000000014424eeecbff345b38187d0b8b749e56faa68539", + "from":"0x9008d19f58aabd9ed0d60971565aa8510560ab41", + "gas":"202500", + "gasPrice":"6756286873", + "maxPriorityFeePerGas":"1000000000", + "minReceiveAmount":"6490696564868305072578", + "signatureData":[ + "" + ], + "slippage":"0.01", + "to":"0x7D0CcAa3Fac1e5A943c5168b6CEd828691b46B36", + "value":"0" + } + } + ], + "msg":"" + }), + }, + mock::http::Expectation::Get { + path: mock::http::Path::exact( + "approve-transaction?chainIndex=1\ + &tokenContractAddress=0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2\ + &approveAmount=1000000000000000000" + ), + res: json!( + { + "code":"0", + "data":[{"data":"0x095ea7b300000000000000000000000040aa958dd87fc8305b97f2ba922cddca374bcd7f000000000000000000000000000000000000000000000000000009184e72a000","dexContractAddress":"0x40aA958dd87FC8305b97f2BA922CDdCa374bcD7f","gasLimit":"70000","gasPrice":"7424402761"}], + "msg":"" + } + ) + }, + ]) + .await; + + let engine = tests::SolverEngine::new("okx", super::config(&api.address)).await; + + let solution = engine + .solve(json!({ + "id": "1", + "tokens": { + "0xe41d2489571d322189246dafa5ebde1f4699f498": { + "decimals": 18, + "symbol": "ZRX", + "referencePrice": null, + "availableBalance": "1583034704488033979459", + "trusted": true, + }, + "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2": { + "decimals": 18, + "symbol": "WETH", + "referencePrice": null, + "availableBalance": "482725140468789680", + "trusted": true, + }, + }, + "orders": [ + { + "uid": "0x2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a\ + 2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a\ + 2a2a2a2a", + "sellToken": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "buyToken": "0xe41d2489571d322189246dafa5ebde1f4699f498", + "sellAmount": "1000000000000000000", + "buyAmount": "1", + "fullSellAmount": "1000000000000000000", + "fullBuyAmount": "1", + "kind": "sell", + "partiallyFillable": false, + "class": "limit", + "sellTokenSource": "erc20", + "buyTokenDestination": "erc20", + "preInteractions": [], + "postInteractions": [], + "owner": "0x5b1e2c2762667331bc91648052f646d1b0d35984", + "validTo": 0, + "appData": "0x0000000000000000000000000000000000000000000000000000000000000000", + "signingScheme": "presign", + "signature": "0x", + } + ], + "liquidity": [], + "effectiveGasPrice": "15000000000", + "deadline": "2106-01-01T00:00:00.000Z", + "surplusCapturingJitOrderOwners": [] + })) + .await; + + assert_eq!( + solution, + json!({ + "solutions":[ + { + "gas":410141, + "id":0, + "interactions":[ + { + "allowances":[ + { + "amount":"1000000000000000000", + "spender":"0x40aa958dd87fc8305b97f2ba922cddca374bcd7f", + "token":"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" + } + ], + "callData":"0x0d5f0e3b00000000000000000001a0cf2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a0000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000015fdc8278903f7f31c10000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000100000000000000000000000014424eeecbff345b38187d0b8b749e56faa68539", + "inputs":[ + { + "amount":"1000000000000000000", + "token":"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" + } + ], + "internalize":false, + "kind":"custom", + "outputs":[ + { + "amount":"6556259156432631386442", + "token":"0xe41d2489571d322189246dafa5ebde1f4699f498" + } + ], + "target":"0x7d0ccaa3fac1e5a943c5168b6ced828691b46b36", + "value":"0" + } + ], + "postInteractions":[], + "preInteractions":[], + "prices":{ + "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2":"6556259156432631386442", + "0xe41d2489571d322189246dafa5ebde1f4699f498":"1000000000000000000" + }, + "trades":[ + { + "executedAmount":"1000000000000000000", + "fee":"0", + "kind":"fulfillment", + "order":"0x2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a" + } + ] + } + ] + }), + ); +} diff --git a/crates/solvers/src/tests/okx/mod.rs b/crates/solvers/src/tests/okx/mod.rs index e132d7ee5c..a3a4d49312 100644 --- a/crates/solvers/src/tests/okx/mod.rs +++ b/crates/solvers/src/tests/okx/mod.rs @@ -1,6 +1,7 @@ use {crate::tests, std::net::SocketAddr}; mod api_calls; +mod limit_order_quoting; mod market_order; mod not_found; mod out_of_price;