Skip to content

Commit

Permalink
Storing pre-interactions and Jit orders in quote metadata (#3162)
Browse files Browse the repository at this point in the history
# Description
Added storing of additional quote metadata: pre-interactions and jit
orders.
Those data can be used for debugging/analysis of the quotes and orders
executed by the winning auction.

# Changes
Extended `QuoteMetadataV1` struct with new fields (vectors):
`pre_interactions` and `jit_orders`.
Extended `QuoteExecution` struct in trade finding quote with same fields
as `QuoteMetadataV1`.
Added filling of `QuoteExecution` new fields from price estimation
results and then moving them to `QuoteMetadataV1` and storing in
database as json.

:warning: This PR needs to be released together with PR #3124, which
introduced `QuoteMetadataV1`. Otherwise this PR needs to be updated to
use new type: `QuoteMetadataV2` for properly handling version 1 data
already stored in database.

## How to test
Existing tests. Also unit tests were updated to verify
serializaion/deserialization of new fields.
  • Loading branch information
mstrug authored Dec 13, 2024
1 parent a520f40 commit ab3ec97
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 9 deletions.
2 changes: 2 additions & 0 deletions crates/e2e/tests/e2e/quote_verification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@ async fn test_bypass_verification_for_rfq_quotes(web3: Web3) {
value: 0.into(),
call_data: hex::decode("aa77476c000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c599000000000000000000000000000000000000000000000000e357b42c3a9d8ccf0000000000000000000000000000000000000000000000000000000004d0e79e000000000000000000000000a69babef1ca67a37ffaf7a485dfff3382056e78c0000000000000000000000009008d19f58aabd9ed0d60971565aa8510560ab41000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000066360af101ffffffffffffffffffffffffffffffffffffff0f3f47f166360a8d0000003f0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000001c66b3383f287dd9c85ad90e7c5a576ea4ba1bdf5a001d794a9afa379e6b2517b47e487a1aef32e75af432cbdbd301ada42754eaeac21ec4ca744afd92732f47540000000000000000000000000000000000000000000000000000000004d0c80f").unwrap()
}],
pre_interactions: vec![],
jit_orders: vec![],
},
};

Expand Down
6 changes: 6 additions & 0 deletions crates/orderbook/src/database/orders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1234,6 +1234,12 @@ mod tests {
call_data: vec![2, 20],
},
],
pre_interactions: vec![InteractionData {
target: H160([3; 20]),
value: U256::from(30),
call_data: vec![3, 20],
}],
jit_orders: vec![],
}
.into(),
..Default::default()
Expand Down
76 changes: 69 additions & 7 deletions crates/shared/src/order_quoting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use {
fee::FeeParameters,
order_validation::PreOrderData,
price_estimation::{Estimate, QuoteVerificationMode, Verification},
trade_finding::external::dto,
},
anyhow::{Context, Result},
chrono::{DateTime, Duration, Utc},
Expand Down Expand Up @@ -244,7 +245,7 @@ pub enum FindQuoteError {
NotFound(Option<QuoteId>),

#[error("quote does not match parameters")]
ParameterMismatch(QuoteData),
ParameterMismatch(Box<QuoteData>),

#[error("quote expired")]
Expired(DateTime<Utc>),
Expand Down Expand Up @@ -447,6 +448,8 @@ impl OrderQuoter {
verified: trade_estimate.verified,
metadata: QuoteMetadataV1 {
interactions: trade_estimate.execution.interactions,
pre_interactions: trade_estimate.execution.pre_interactions,
jit_orders: trade_estimate.execution.jit_orders,
}
.into(),
};
Expand Down Expand Up @@ -576,7 +579,7 @@ impl OrderQuoting for OrderQuoter {
.ok_or(FindQuoteError::NotFound(Some(id)))?;

if !parameters.matches(&data) {
return Err(FindQuoteError::ParameterMismatch(data));
return Err(FindQuoteError::ParameterMismatch(Box::new(data)));
}
if data.expiration < now {
return Err(FindQuoteError::Expired(data.expiration));
Expand Down Expand Up @@ -687,9 +690,15 @@ impl From<QuoteMetadataV1> for QuoteMetadata {
}

#[derive(Clone, Debug, Default, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct QuoteMetadataV1 {
/// Data provided by the solver in response to /quote request.
pub interactions: Vec<InteractionData>,
/// The onchain calls to run before sending user funds to the settlement
/// contract.
pub pre_interactions: Vec<InteractionData>,
/// Orders that were settled outside of the auction.
pub jit_orders: Vec<dto::JitOrder>,
}

#[cfg(test)]
Expand Down Expand Up @@ -1643,6 +1652,34 @@ mod tests {
call_data: vec![2],
},
],
pre_interactions: vec![
InteractionData {
target: H160::from([3; 20]),
value: U256::from(3),
call_data: vec![3],
},
InteractionData {
target: H160::from([4; 20]),
value: U256::from(4),
call_data: vec![4],
},
],
jit_orders: vec![dto::JitOrder {
buy_token: H160([4; 20]),
sell_token: H160([5; 20]),
sell_amount: U256::from(10),
buy_amount: U256::from(20),
executed_amount: U256::from(11),
receiver: H160([6; 20]),
valid_to: 1734084318,
app_data: Default::default(),
side: dto::Side::Sell,
partially_fillable: false,
sell_token_source: model::order::SellTokenSource::External,
buy_token_destination: model::order::BuyTokenDestination::Internal,
signature: vec![1; 16],
signing_scheme: model::signature::SigningScheme::Eip712,
}],
}
.into();
let v = serde_json::to_value(q).unwrap();
Expand All @@ -1652,8 +1689,16 @@ mod tests {
{"version":"1.0",
"interactions":[
{"target":"0x0101010101010101010101010101010101010101","value":"1","callData":"0x01"},
{"target":"0x0202020202020202020202020202020202020202","value":"2","callData":"0x02"}
]}"#,
{"target":"0x0202020202020202020202020202020202020202","value":"2","callData":"0x02"}],
"preInteractions":[
{"target":"0x0303030303030303030303030303030303030303","value":"3","callData":"0x03"},
{"target":"0x0404040404040404040404040404040404040404","value":"4","callData":"0x04"}],
"jitOrders":[{"appData": "0x0000000000000000000000000000000000000000000000000000000000000000",
"buyAmount": "20", "buyToken": "0x0404040404040404040404040404040404040404", "buyTokenDestination": "internal",
"executedAmount": "11", "partiallyFillable": false, "receiver": "0x0606060606060606060606060606060606060606",
"sellAmount": "10", "sellToken": "0x0505050505050505050505050505050505050505", "sellTokenSource": "external",
"side": "sell", "signature": "0x01010101010101010101010101010101", "signingScheme": "eip712", "validTo": 1734084318}]
}"#,
)
.unwrap();

Expand All @@ -1675,14 +1720,31 @@ mod tests {
{"version":"1.0",
"interactions":[
{"target":"0x0101010101010101010101010101010101010101","value":"1","callData":"0x01"},
{"target":"0x0202020202020202020202020202020202020202","value":"2","callData":"0x02"}
]}"#,
{"target":"0x0202020202020202020202020202020202020202","value":"2","callData":"0x02"}],
"preInteractions":[
{"target":"0x0303030303030303030303030303030303030303","value":"3","callData":"0x03"},
{"target":"0x0404040404040404040404040404040404040404","value":"4","callData":"0x04"}],
"jitOrders":[{"appData": "0x0000000000000000000000000000000000000000000000000000000000000000",
"buyAmount": "20", "buyToken": "0x0404040404040404040404040404040404040404", "buyTokenDestination": "internal",
"executedAmount": "11", "partiallyFillable": false, "receiver": "0x0606060606060606060606060606060606060606",
"sellAmount": "10", "sellToken": "0x0505050505050505050505050505050505050505", "sellTokenSource": "external",
"side": "sell", "signature": "0x01010101010101010101010101010101", "signingScheme": "eip712", "validTo": 1734084318},
{"appData": "0x0ddeb6e4a814908832cc25d11311c514e7efe6af3c9bafeb0d241129cf7f4d83",
"buyAmount": "100", "buyToken": "0x0606060606060606060606060606060606060606", "buyTokenDestination": "erc20",
"executedAmount": "99", "partiallyFillable": true, "receiver": "0x0303030303030303030303030303030303030303",
"sellAmount": "10", "sellToken": "0x0101010101010101010101010101010101010101", "sellTokenSource": "erc20",
"side": "buy", "signature": "0x01010101010101010101010101010101", "signingScheme": "eip1271", "validTo": 1734085109}]
}"#,
)
.unwrap();
let metadata: QuoteMetadata = v1.try_into().unwrap();

match metadata {
QuoteMetadata::V1(v1) => assert_eq!(v1.interactions.len(), 2),
QuoteMetadata::V1(v1) => {
assert_eq!(v1.interactions.len(), 2);
assert_eq!(v1.pre_interactions.len(), 2);
assert_eq!(v1.jit_orders.len(), 2);
}
}
}
}
6 changes: 6 additions & 0 deletions crates/shared/src/price_estimation/trade_verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,8 @@ impl TradeVerifier {
verified: true,
execution: QuoteExecution {
interactions: map_interactions_data(&trade.interactions()),
pre_interactions: map_interactions_data(&trade.pre_interactions()),
jit_orders: trade.jit_orders(),
},
};
tracing::warn!(
Expand Down Expand Up @@ -417,6 +419,8 @@ impl TradeVerifying for TradeVerifier {
verified: false,
execution: QuoteExecution {
interactions: map_interactions_data(&trade.interactions()),
pre_interactions: map_interactions_data(&trade.pre_interactions()),
jit_orders: trade.jit_orders(),
},
};
tracing::warn!(
Expand Down Expand Up @@ -809,6 +813,8 @@ fn ensure_quote_accuracy(
verified: true,
execution: QuoteExecution {
interactions: map_interactions_data(&trade.interactions()),
pre_interactions: map_interactions_data(&trade.pre_interactions()),
jit_orders: trade.jit_orders(),
},
})
}
Expand Down
6 changes: 4 additions & 2 deletions crates/shared/src/trade_finding/external.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,8 @@ impl TradeFinding for ExternalTradeFinder {
solver: trade.solver(),
execution: QuoteExecution {
interactions: map_interactions_data(&trade.interactions()),
pre_interactions: map_interactions_data(&trade.pre_interactions()),
jit_orders: trade.jit_orders(),
},
})
}
Expand Down Expand Up @@ -312,7 +314,7 @@ pub(crate) mod dto {
}

#[serde_as]
#[derive(Clone, Debug, Eq, PartialEq, Deserialize)]
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
#[allow(unused)]
pub struct JitOrder {
Expand Down Expand Up @@ -345,7 +347,7 @@ pub(crate) mod dto {
}

#[serde_as]
#[derive(Clone, Debug, Eq, PartialEq, Deserialize)]
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub enum Side {
Buy,
Expand Down
9 changes: 9 additions & 0 deletions crates/shared/src/trade_finding/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ pub struct Quote {
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize)]
pub struct QuoteExecution {
pub interactions: Vec<InteractionData>,
pub pre_interactions: Vec<InteractionData>,
pub jit_orders: Vec<dto::JitOrder>,
}

#[derive(Clone, Debug, Eq, PartialEq)]
Expand Down Expand Up @@ -107,6 +109,13 @@ impl TradeKind {
TradeKind::Regular(trade) => trade.pre_interactions.clone(),
}
}

pub fn jit_orders(&self) -> Vec<dto::JitOrder> {
match self {
TradeKind::Legacy(_) => Vec::new(),
TradeKind::Regular(trade) => trade.jit_orders.clone(),
}
}
}

/// A legacy trade.
Expand Down

0 comments on commit ab3ec97

Please sign in to comment.