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

Driver sends quote response with JIT orders #3103

Merged
merged 67 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from 66 commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
a423e57
Quote with JIT orders API
squadgazzz Oct 24, 2024
0bf5393
Updated description
squadgazzz Oct 24, 2024
9cc9abe
Redundant defaults
squadgazzz Oct 25, 2024
898e5e7
Redundant amount
squadgazzz Oct 25, 2024
5e1d001
Support jit orders quote
squadgazzz Oct 25, 2024
0937d4e
Address rc
squadgazzz Oct 28, 2024
e759f42
Restore the custom quote deserializer
squadgazzz Oct 28, 2024
e7b756a
Address rc
squadgazzz Oct 28, 2024
f40fafb
Serde untagged
squadgazzz Oct 28, 2024
1dddcf5
Merge branch '3082/jit-orders-quote-api' into 3082/trade-verifier-jit…
squadgazzz Oct 28, 2024
18686fe
Fixes after merge
squadgazzz Oct 28, 2024
67af897
Address rc
squadgazzz Oct 28, 2024
8bcb18a
Merge branch 'main' into 3082/trade-verifier-jit-orders-support
squadgazzz Oct 28, 2024
1ee64ba
Compute expected amounts
squadgazzz Oct 28, 2024
f676a96
Compute jit orders net changes
squadgazzz Oct 30, 2024
941647c
Combine tests
squadgazzz Oct 30, 2024
9d522d2
Avoid redundant clone
squadgazzz Oct 30, 2024
81836ec
Use checked_ceil_div
squadgazzz Oct 30, 2024
bb83789
Merge branch 'main' into 3082/trade-verifier-jit-orders-support
squadgazzz Oct 30, 2024
a37b3ac
Redundant modulus
squadgazzz Oct 30, 2024
265e598
Comment
squadgazzz Oct 30, 2024
ac2dbd4
Avoid tokens and prices duplicates
squadgazzz Oct 30, 2024
8c7503a
Tests comment
squadgazzz Oct 30, 2024
e68e0df
Encode jit orders function
squadgazzz Oct 31, 2024
0afb473
Encode fake trade function
squadgazzz Oct 31, 2024
09e6330
BigRational::neg()
squadgazzz Oct 31, 2024
2e71436
Use BigDecimal in the config
squadgazzz Oct 31, 2024
85be4d6
Send new quote response
squadgazzz Oct 31, 2024
c3a7a86
Fixed tests
squadgazzz Nov 1, 2024
a44b518
Less public
squadgazzz Nov 1, 2024
a308910
Avoid calculation with empty jit orders
squadgazzz Nov 1, 2024
cd8bf51
Merge branch '3082/trade-verifier-jit-orders-support' into 3082/drive…
squadgazzz Nov 1, 2024
3dd052e
e2e tests
squadgazzz Nov 1, 2024
3067cd9
Address comments
squadgazzz Nov 5, 2024
d2887bc
Merge branch '3082/trade-verifier-jit-orders-support' into 3082/drive…
squadgazzz Nov 5, 2024
799fd66
Fix the test
squadgazzz Nov 5, 2024
2ace2eb
Combine e2e test
squadgazzz Nov 5, 2024
4a0767c
Naming
squadgazzz Nov 5, 2024
e97a65b
Merge branch '3082/trade-verifier-jit-orders-support' into 3082/drive…
squadgazzz Nov 5, 2024
bfc45cb
Update solver's openapi
squadgazzz Nov 5, 2024
e051528
Fetch the balances in the solver contract
squadgazzz Nov 7, 2024
2bcb3ac
Refactor the unit test
squadgazzz Nov 7, 2024
bea5183
Revert balance fetching changes
squadgazzz Nov 7, 2024
365c131
Merge branch '3082/trade-verifier-jit-orders-support' into 3082/drive…
squadgazzz Nov 7, 2024
7b29d96
Avoid using IR optimizer
squadgazzz Nov 7, 2024
a2f4d11
Merge branch '3082/trade-verifier-jit-orders-support' into 3082/drive…
squadgazzz Nov 7, 2024
0be2814
Revert "Revert balance fetching changes"
squadgazzz Nov 18, 2024
277b4e3
Adjust balance fetching
squadgazzz Nov 18, 2024
1dfbdd4
Merge branch '3082/trade-verifier-jit-orders-support' into 3082/drive…
squadgazzz Nov 18, 2024
4d41063
Migrate to hashmap
squadgazzz Nov 18, 2024
b21df8f
Interaction from domain
squadgazzz Nov 19, 2024
c494eb3
Redundant change
squadgazzz Nov 27, 2024
f1e1806
Pr comments
squadgazzz Nov 28, 2024
8718d83
Ceil div
squadgazzz Nov 28, 2024
4ec514e
Merge branch 'main' into 3082/trade-verifier-jit-orders-support
squadgazzz Nov 28, 2024
078185e
Merge branch '3082/trade-verifier-jit-orders-support' into 3082/drive…
squadgazzz Nov 28, 2024
8324c32
Redundant ceil
squadgazzz Nov 28, 2024
47d2642
Merge branch 'main' into 3082/trade-verifier-jit-orders-support
squadgazzz Dec 2, 2024
9b0e906
Redundant changes
squadgazzz Dec 2, 2024
fc0ad49
Comment
squadgazzz Dec 2, 2024
5276213
Merge branch '3082/trade-verifier-jit-orders-support' into 3082/drive…
squadgazzz Dec 2, 2024
36c3e97
Comment
squadgazzz Dec 2, 2024
0374181
Comment
squadgazzz Dec 2, 2024
8636356
Typo
squadgazzz Dec 2, 2024
150a848
Comment
squadgazzz Dec 2, 2024
523ea2a
Comment
squadgazzz Dec 2, 2024
fdba85c
Merge branch 'main' into 3082/driver-sends-quote-jit-orders
squadgazzz Dec 2, 2024
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/autopilot/src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,7 @@ pub async fn run(args: Arguments) {
code_fetcher: code_fetcher.clone(),
},
)
.await
.expect("failed to initialize price estimator factory");

let native_price_estimator = price_estimator_factory
Expand Down
2 changes: 1 addition & 1 deletion crates/contracts/artifacts/Solver.json

Large diffs are not rendered by default.

41 changes: 32 additions & 9 deletions crates/contracts/solidity/Solver.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ contract Solver {
/// @param trader - address of the order owner doing the trade
/// @param sellToken - address of the token being sold
/// @param sellAmount - amount being sold
/// @param buyToken - address of the token being bought
/// @param nativeToken - ERC20 version of the chain's token
/// @param tokens - list of tokens used in the trade
/// @param receiver - address receiving the bought tokens
/// @param settlementCall - the calldata of the `settle()` call
/// @param mockPreconditions - controls whether things like ETH wrapping
Expand All @@ -47,8 +47,8 @@ contract Solver {
address payable trader,
address sellToken,
uint256 sellAmount,
address buyToken,
address nativeToken,
address[] calldata tokens,
address payable receiver,
bytes calldata settlementCall,
bool mockPreconditions
Expand Down Expand Up @@ -76,13 +76,14 @@ contract Solver {
// contract.
receiver.call{value: 0}("");

this.storeBalance(sellToken, address(settlementContract), false);
this.storeBalance(buyToken, address(settlementContract), false);
uint256 gasStart = gasleft();
address(settlementContract).doCall(settlementCall);
gasUsed = gasStart - gasleft() - _simulationOverhead;
this.storeBalance(sellToken, address(settlementContract), false);
this.storeBalance(buyToken, address(settlementContract), false);
// Store pre-settlement balances
_storeSettlementBalances(tokens, settlementContract);

gasUsed = _executeSettlement(address(settlementContract), settlementCall);

// Store post-settlement balances
_storeSettlementBalances(tokens, settlementContract);

queriedBalances = _queriedBalances;
}

Expand All @@ -104,4 +105,26 @@ contract Solver {
_simulationOverhead += gasStart - gasleft() + 4460;
}
}

/// @dev Helper function that reads and stores the balances of the `settlementContract` for each token in `tokens`.
/// @param tokens - list of tokens used in the trade
/// @param settlementContract - the settlement contract whose balances are being read
function _storeSettlementBalances(address[] calldata tokens, ISettlement settlementContract) internal {
for (uint256 i = 0; i < tokens.length; i++) {
this.storeBalance(tokens[i], address(settlementContract), false);
}
}

/// @dev Executes the settlement and measures the gas used.
/// @param settlementContract The address of the settlement contract.
/// @param settlementCall The calldata for the settlement function.
/// @return gasUsed The amount of gas used during the settlement execution.
function _executeSettlement(
address settlementContract,
bytes calldata settlementCall
) private returns (uint256 gasUsed) {
uint256 gasStart = gasleft();
address(settlementContract).doCall(settlementCall);
gasUsed = gasStart - gasleft() - _simulationOverhead;
}
}
1 change: 1 addition & 0 deletions crates/driver/src/domain/competition/order/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,7 @@ pub struct Jit {
pub buy: eth::Asset,
pub receiver: eth::Address,
pub valid_to: util::Timestamp,
pub partially_fillable: bool,
pub app_data: AppData,
pub side: Side,
pub sell_token_balance: SellTokenBalance,
Expand Down
11 changes: 11 additions & 0 deletions crates/driver/src/domain/competition/order/signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,17 @@ pub enum Scheme {
PreSign,
}

impl Scheme {
pub fn to_boundary_scheme(&self) -> model::signature::SigningScheme {
match self {
Scheme::Eip712 => model::signature::SigningScheme::Eip712,
Scheme::EthSign => model::signature::SigningScheme::EthSign,
Scheme::Eip1271 => model::signature::SigningScheme::Eip1271,
Scheme::PreSign => model::signature::SigningScheme::PreSign,
}
}
}

pub fn domain_separator(
chain_id: chain::Id,
verifying_contract: eth::ContractAddress,
Expand Down
14 changes: 7 additions & 7 deletions crates/driver/src/domain/competition/solution/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,13 @@ pub fn tx(
let mut native_unwrap = eth::TokenAmount(eth::U256::zero());

// Encode uniform clearing price vector
for asset in solution
for (token, amount) in solution
.clearing_prices()
.iter()
.sorted_by_cached_key(|asset| asset.token)
.into_iter()
.sorted_by_cached_key(|(token, _amount)| *token)
{
tokens.push(asset.token.into());
clearing_prices.push(asset.amount.into());
tokens.push(token.into());
clearing_prices.push(amount);
}

// Encode trades with custom clearing prices
Expand Down Expand Up @@ -312,7 +312,7 @@ struct Flags {
buy_token_balance: order::BuyTokenBalance,
}

mod codec {
pub mod codec {
use crate::domain::{competition::order, eth};

// cf. https://github.com/cowprotocol/contracts/blob/v1.5.0/src/contracts/libraries/GPv2Trade.sol#L16
Expand Down Expand Up @@ -392,7 +392,7 @@ mod codec {
)
}

pub(super) fn signature(signature: &order::Signature) -> super::Bytes<Vec<u8>> {
pub fn signature(signature: &order::Signature) -> super::Bytes<Vec<u8>> {
match signature.scheme {
order::signature::Scheme::Eip712 | order::signature::Scheme::EthSign => {
signature.data.clone()
Expand Down
27 changes: 13 additions & 14 deletions crates/driver/src/domain/competition/solution/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,10 @@ impl Solution {
&self.interactions
}

pub fn pre_interactions(&self) -> &[eth::Interaction] {
&self.pre_interactions
}

/// The solver which generated this solution.
pub fn solver(&self) -> &Solver {
&self.solver
Expand Down Expand Up @@ -401,11 +405,8 @@ impl Solution {
///
/// The rule which relates two prices for tokens X and Y is:
/// amount_x * price_x = amount_y * price_y
pub fn clearing_prices(&self) -> Vec<eth::Asset> {
let prices = self.prices.iter().map(|(&token, &amount)| eth::Asset {
token,
amount: amount.into(),
});
pub fn clearing_prices(&self) -> Prices {
let prices = self.prices.clone();

if self.user_trades().any(|trade| trade.order().buys_eth()) {
// The solution contains an order which buys ETH. Solvers only produce solutions
Expand All @@ -418,28 +419,26 @@ impl Solution {
// If no order trades WETH, the WETH price is not necessary, only the ETH
// price is needed. Remove the unneeded WETH price, which slightly reduces
// gas used by the settlement.
let mut prices = if self.user_trades().all(|trade| {
let mut prices: Prices = if self.user_trades().all(|trade| {
trade.order().sell.token != self.weth.0 && trade.order().buy.token != self.weth.0
}) {
prices
.filter(|price| price.token != self.weth.0)
.collect_vec()
.into_iter()
.filter(|(token, _price)| *token != self.weth.0)
.collect()
} else {
prices.collect_vec()
prices
};

// Add a clearing price for ETH equal to WETH.
prices.push(eth::Asset {
token: eth::ETH_TOKEN,
amount: self.prices[&self.weth.into()].to_owned().into(),
});
prices.insert(eth::ETH_TOKEN, self.prices[&self.weth.into()].to_owned());

return prices;
}

// TODO: We should probably filter out all unused prices to save gas.

prices.collect_vec()
prices
}

/// Clearing price for the given token.
Expand Down
4 changes: 2 additions & 2 deletions crates/driver/src/domain/competition/solution/settlement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,8 +308,8 @@ impl Settlement {
pub fn prices(&self) -> HashMap<eth::TokenAddress, eth::TokenAmount> {
self.solution
.clearing_prices()
.iter()
.map(|asset| (asset.token, asset.amount))
.into_iter()
.map(|(token, amount)| (token, amount.into()))
.collect()
}
}
Expand Down
54 changes: 23 additions & 31 deletions crates/driver/src/domain/quote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,52 +13,37 @@ use {
blockchain::{self, Ethereum},
solver::{self, Solver},
},
util::{self, conv::u256::U256Ext},
util,
},
anyhow::Context,
chrono::Utc,
num::CheckedDiv,
std::{collections::HashSet, iter, ops::Mul},
std::{
collections::{HashMap, HashSet},
iter,
},
};

/// A quote describing the expected outcome of an order.
#[derive(Debug)]
pub struct Quote {
/// The amount that can be bought if this was a sell order, or sold if this
/// was a buy order.
pub amount: eth::U256,
pub clearing_prices: HashMap<eth::H160, eth::U256>,
pub pre_interactions: Vec<eth::Interaction>,
pub interactions: Vec<eth::Interaction>,
pub solver: eth::Address,
pub gas: Option<eth::Gas>,
/// Which `tx.origin` is required to make the quote simulation pass.
pub tx_origin: Option<eth::Address>,
pub jit_orders: Vec<solution::trade::Jit>,
}

impl Quote {
fn new(eth: &Ethereum, order: &Order, solution: competition::Solution) -> Result<Self, Error> {
let sell_price = solution
.clearing_price(order.tokens.sell)
.ok_or(QuotingFailed::ClearingSellMissing)?
.to_big_rational();
let buy_price = solution
.clearing_price(order.tokens.buy)
.ok_or(QuotingFailed::ClearingBuyMissing)?
.to_big_rational();
let order_amount = order.amount.0.to_big_rational();

let amount = match order.side {
order::Side::Sell => order_amount
.mul(sell_price)
.checked_div(&buy_price)
.context("div by zero: buy price")?,
order::Side::Buy => order_amount
.mul(&buy_price)
.checked_div(&sell_price)
.context("div by zero: sell price")?,
};

fn new(eth: &Ethereum, solution: competition::Solution) -> Result<Self, Error> {
Ok(Self {
amount: eth::U256::from_big_rational(&amount)?,
clearing_prices: solution
.clearing_prices()
.into_iter()
.map(|(token, amount)| (token.into(), amount))
.collect(),
pre_interactions: solution.pre_interactions().to_vec(),
interactions: solution
.interactions()
.iter()
Expand All @@ -70,6 +55,14 @@ impl Quote {
solver: solution.solver().address(),
gas: solution.gas(),
tx_origin: *solution.solver().quote_tx_origin(),
jit_orders: solution
.trades()
.iter()
.filter_map(|trade| match trade {
solution::Trade::Jit(jit) => Some(jit.clone()),
_ => None,
})
.collect(),
})
}
}
Expand Down Expand Up @@ -110,7 +103,6 @@ impl Order {
let solutions = solver.solve(&auction, &liquidity).await?;
Quote::new(
eth,
self,
// TODO(#1468): choose the best solution in the future, but for now just pick the
// first solution
solutions
Expand Down
Loading
Loading