From ab8357bd972a153f05b1f4aadf1c7b0d0d91e278 Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Wed, 22 Apr 2026 07:41:13 +0000 Subject: [PATCH 01/44] initial commit --- crates/simulator/src/lib.rs | 2 + crates/simulator/src/simulation_builder.rs | 261 ++++++++++++++++++ .../simulator/src/state_override_helpers.rs | 60 ++++ 3 files changed, 323 insertions(+) create mode 100644 crates/simulator/src/simulation_builder.rs create mode 100644 crates/simulator/src/state_override_helpers.rs diff --git a/crates/simulator/src/lib.rs b/crates/simulator/src/lib.rs index e3f98f0362..83f251e223 100644 --- a/crates/simulator/src/lib.rs +++ b/crates/simulator/src/lib.rs @@ -1,5 +1,7 @@ pub mod encoding; pub mod ethereum; +pub mod simulation_builder; +pub mod state_override_helpers; pub mod swap_simulator; pub mod tenderly; mod utils; diff --git a/crates/simulator/src/simulation_builder.rs b/crates/simulator/src/simulation_builder.rs new file mode 100644 index 0000000000..d7b5126774 --- /dev/null +++ b/crates/simulator/src/simulation_builder.rs @@ -0,0 +1,261 @@ +use { + crate::encoding::{ + EncodedSettlement, + Interaction, + Interactions, + WrapperCall, + encode_interactions, + encode_trade, + encode_wrapper_settlement, + }, + alloy_primitives::{Address, Bytes, U256}, + alloy_rpc_types::{ + TransactionRequest, + state::{AccountOverride, StateOverride}, + }, + model::{ + order::{OrderData, OrderKind}, + signature::{Signature, SigningScheme}, + }, +}; + +/// A simulator-specific order that bundles the data needed to encode a trade. +/// +/// Construct with [`Order::new`] and add optional fields via the builder +/// methods. Defaults to an EIP-1271 signature (pairs with [`FakeUser`] for +/// simulations that need to bypass signature verification). +pub struct Order { + data: OrderData, + owner: Address, + signature: Signature, + pre_interactions: Vec, + post_interactions: Vec, +} + +impl Order { + pub fn new(data: OrderData) -> Self { + Self { + data, + owner: Address::ZERO, + signature: Signature::default_with(SigningScheme::Eip1271), + pre_interactions: vec![], + post_interactions: vec![], + } + } + + pub fn with_signature(mut self, owner: Address, signature: Signature) -> Self { + self.owner = owner; + self.signature = signature; + self + } + + pub fn with_pre_interactions(mut self, interactions: Vec) -> Self { + self.pre_interactions = interactions; + self + } + + pub fn with_post_interactions(mut self, interactions: Vec) -> Self { + self.post_interactions = interactions; + self + } +} + +/// Configuration for wrapping the settlement in a flashloan or custom wrapper +/// contract chain. +pub enum WrapperConfig { + Flashloan { router: Address, data: Bytes }, + Custom(Vec), +} + +/// How clearing prices are determined for the encoded settlement. +pub enum Prices { + /// Derive clearing prices directly from the order's limit price. + /// + /// Sets `price[sell_token] = buy_amount` and `price[buy_token] = + /// sell_amount`, exactly satisfying the order's limit with no surplus. + Limit, + /// Explicit token list and matching clearing prices. + Explicit { + tokens: Vec
, + clearing_prices: Vec, + }, +} + +/// The output of [`SimulationBuilder::build`]: a transaction request and state +/// overrides ready to be passed to an alloy provider for simulation. +pub struct SettlementCall { + pub request: TransactionRequest, + pub state_overrides: StateOverride, +} + +/// Assembles a GPv2 settlement call for simulation purposes. +/// +/// Call [`SimulationBuilder::build`] when done to produce a [`SettlementCall`]. +#[derive(Default)] +pub struct SimulationBuilder { + order: Option, + pre_interactions: Vec, + main_interactions: Vec, + post_interactions: Vec, + wrapper: Option, + prices: Option, + solver: Option
, + state_overrides: StateOverride, +} + +impl SimulationBuilder { + pub fn add_order(mut self, order: Order) -> Self { + self.order = Some(order); + self + } + + /// Mutate the pre-interactions list via a closure. The order's own + /// pre-hooks are prepended automatically in [`build`]; these interactions + /// follow them. + pub fn with_pre_interactions_mut(mut self, f: impl FnOnce(&mut Vec)) -> Self { + f(&mut self.pre_interactions); + self + } + + pub fn with_main_interactions_mut(mut self, f: impl FnOnce(&mut Vec)) -> Self { + f(&mut self.main_interactions); + self + } + + /// Mutate the post-interactions list via a closure. The order's own + /// post-hooks are appended automatically in [`build`]; these interactions + /// precede them. + pub fn with_post_interactions_mut(mut self, f: impl FnOnce(&mut Vec)) -> Self { + f(&mut self.post_interactions); + self + } + + pub fn with_wrapper(mut self, wrapper: WrapperConfig) -> Self { + self.wrapper = Some(wrapper); + self + } + + pub fn with_prices(mut self, prices: Prices) -> Self { + self.prices = Some(prices); + self + } + + pub fn from_solver(mut self, solver: Address) -> Self { + self.solver = Some(solver); + self + } + + pub fn state_override( + mut self, + address: Address, + account_override: impl Into, + ) -> Self { + self.state_overrides + .insert(address, account_override.into()); + self + } + + pub fn build(self, settlement_address: Address) -> Result { + self.build_with_modifications(settlement_address, |_| {}) + } + + pub fn build_with_modifications( + self, + settlement_address: Address, + customize: impl FnOnce(&mut EncodedSettlement), + ) -> Result { + let order = self.order.as_ref().ok_or(BuildError::NoOrder)?; + + let (tokens, clearing_prices) = match &self.prices { + Some(Prices::Explicit { + tokens, + clearing_prices, + }) => (tokens.clone(), clearing_prices.clone()), + // At limit price: price[sell_token] = buy_amount, price[buy_token] = sell_amount. + // This makes sell_amount * price[sell] / price[buy] = buy_amount exactly. + _ => ( + vec![order.data.sell_token, order.data.buy_token], + vec![order.data.buy_amount, order.data.sell_amount], + ), + }; + + let sell_token_index = tokens + .iter() + .position(|t| *t == order.data.sell_token) + .ok_or(BuildError::MissingSellToken)?; + let buy_token_index = tokens + .iter() + .position(|t| *t == order.data.buy_token) + .ok_or(BuildError::MissingBuyToken)?; + + let executed_amount = match order.data.kind { + OrderKind::Sell => order.data.sell_amount, + OrderKind::Buy => order.data.buy_amount, + }; + + let trade = encode_trade( + &order.data, + &order.signature, + order.owner, + sell_token_index, + buy_token_index, + executed_amount, + ); + + let order_pre = &order.pre_interactions; + let order_post = &order.post_interactions; + + let mut settlement = EncodedSettlement { + tokens, + clearing_prices, + trades: vec![trade], + interactions: Interactions { + // order's pre-hooks run before any additional pre-interactions + pre: encode_interactions(order_pre.iter().chain(&self.pre_interactions)), + main: encode_interactions(&self.main_interactions), + // additional post-interactions run before the order's post-hooks + post: encode_interactions(self.post_interactions.iter().chain(order_post)), + }, + }; + + customize(&mut settlement); + + let settle_calldata = settlement.into_settle_call(); + + let (to, input) = match self.wrapper { + Some(WrapperConfig::Custom(wrappers)) if !wrappers.is_empty() => { + encode_wrapper_settlement(&wrappers, settle_calldata) + .expect("wrappers is non-empty") + } + Some(WrapperConfig::Flashloan { router, data }) => encode_wrapper_settlement( + &[WrapperCall { + address: router, + data, + }], + settle_calldata, + ) + .expect("wrappers is non-empty"), + _ => (settlement_address, settle_calldata), + }; + + Ok(SettlementCall { + request: TransactionRequest { + from: self.solver, + to: Some(to.into()), + input: input.into(), + ..Default::default() + }, + state_overrides: self.state_overrides, + }) + } +} + +#[derive(Debug, thiserror::Error)] +pub enum BuildError { + #[error("no order was added")] + NoOrder, + #[error("sell token not found in token list")] + MissingSellToken, + #[error("buy token not found in token list")] + MissingBuyToken, +} diff --git a/crates/simulator/src/state_override_helpers.rs b/crates/simulator/src/state_override_helpers.rs new file mode 100644 index 0000000000..ea835a36a2 --- /dev/null +++ b/crates/simulator/src/state_override_helpers.rs @@ -0,0 +1,60 @@ +use {alloy_primitives::Bytes, alloy_rpc_types::state::AccountOverride}; +pub use { + balance_overrides::{BalanceOverrideRequest, BalanceOverrides, BalanceOverriding}, + configs::balance_overrides::Strategy, +}; + +/// Deploys a fake ERC-1271 contract at a given address so that signature +/// verification succeeds unconditionally. Pass to +/// [`crate::simulation_builder::SimulationBuilder::state_override`] with the +/// order owner's address. +pub struct FakeUser { + pub code: Bytes, +} + +impl Default for FakeUser { + fn default() -> Self { + // Returns bytes4(0x1626ba7e) — the ERC-1271 magic value — for any call. + // Opcodes: PUSH4 1626ba7e | PUSH1 e0 | SHL | PUSH1 00 | MSTORE | PUSH1 20 | + // PUSH1 00 | RETURN + Self { + code: Bytes::from_static(&[ + 0x63, 0x16, 0x26, 0xba, 0x7e, // PUSH4 0x1626ba7e + 0x60, 0xe0, // PUSH1 224 + 0x1b, // SHL → 0x1626ba7e left-aligned in 32-byte word + 0x60, 0x00, // PUSH1 0x00 + 0x52, // MSTORE + 0x60, 0x20, // PUSH1 0x20 (return 32 bytes) + 0x60, 0x00, // PUSH1 0x00 (from offset 0) + 0xf3, // RETURN + ]), + } + } +} + +impl From for AccountOverride { + fn from(fake_user: FakeUser) -> Self { + Self { + code: Some(fake_user.code), + ..Default::default() + } + } +} + +/// Deploys the `AnyoneAuthenticator` contract at a given address, causing it +/// to approve any solver. Pass to +/// [`crate::simulation_builder::SimulationBuilder::state_override`] +/// with the authenticator contract's address. +pub struct AnyoneAuthenticator; + +impl From for AccountOverride { + fn from(_: AnyoneAuthenticator) -> Self { + Self { + code: Some( + contracts::support::AnyoneAuthenticator::AnyoneAuthenticator::DEPLOYED_BYTECODE + .clone(), + ), + ..Default::default() + } + } +} From 76b12c6ef26153a6681a300b5e45fee332d60eee Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Wed, 22 Apr 2026 09:34:37 +0000 Subject: [PATCH 02/44] fixup --- .../simulator/src/state_override_helpers.rs | 38 +++++++------------ 1 file changed, 13 insertions(+), 25 deletions(-) diff --git a/crates/simulator/src/state_override_helpers.rs b/crates/simulator/src/state_override_helpers.rs index ea835a36a2..d30ec72607 100644 --- a/crates/simulator/src/state_override_helpers.rs +++ b/crates/simulator/src/state_override_helpers.rs @@ -8,34 +8,22 @@ pub use { /// verification succeeds unconditionally. Pass to /// [`crate::simulation_builder::SimulationBuilder::state_override`] with the /// order owner's address. -pub struct FakeUser { - pub code: Bytes, -} - -impl Default for FakeUser { - fn default() -> Self { - // Returns bytes4(0x1626ba7e) — the ERC-1271 magic value — for any call. - // Opcodes: PUSH4 1626ba7e | PUSH1 e0 | SHL | PUSH1 00 | MSTORE | PUSH1 20 | - // PUSH1 00 | RETURN - Self { - code: Bytes::from_static(&[ - 0x63, 0x16, 0x26, 0xba, 0x7e, // PUSH4 0x1626ba7e - 0x60, 0xe0, // PUSH1 224 - 0x1b, // SHL → 0x1626ba7e left-aligned in 32-byte word - 0x60, 0x00, // PUSH1 0x00 - 0x52, // MSTORE - 0x60, 0x20, // PUSH1 0x20 (return 32 bytes) - 0x60, 0x00, // PUSH1 0x00 (from offset 0) - 0xf3, // RETURN - ]), - } - } -} +pub struct FakeUser; impl From for AccountOverride { - fn from(fake_user: FakeUser) -> Self { + fn from(_fake_user: FakeUser) -> Self { + let code = Bytes::from_static(&[ + 0x63, 0x16, 0x26, 0xba, 0x7e, // PUSH4 0x1626ba7e + 0x60, 0xe0, // PUSH1 224 + 0x1b, // SHL → 0x1626ba7e left-aligned in 32-byte word + 0x60, 0x00, // PUSH1 0x00 + 0x52, // MSTORE + 0x60, 0x20, // PUSH1 0x20 (return 32 bytes) + 0x60, 0x00, // PUSH1 0x00 (from offset 0) + 0xf3, // RETURN + ]); Self { - code: Some(fake_user.code), + code: Some(code), ..Default::default() } } From d410031f5c262879cdb84cd1e8407388188a07a8 Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Wed, 22 Apr 2026 09:57:47 +0000 Subject: [PATCH 03/44] fixup --- crates/simulator/src/encoding.rs | 2 +- crates/simulator/src/simulation_builder.rs | 73 +++++++++++++++------- 2 files changed, 51 insertions(+), 24 deletions(-) diff --git a/crates/simulator/src/encoding.rs b/crates/simulator/src/encoding.rs index 8c6d188c24..d318450341 100644 --- a/crates/simulator/src/encoding.rs +++ b/crates/simulator/src/encoding.rs @@ -2,7 +2,7 @@ use { alloy_primitives::{Address, B256, Bytes, U256}, alloy_sol_types::SolCall, app_data::AppDataHash, - contracts::GPv2Settlement, + contracts::{FlashLoanRouter::LoanRequest, GPv2Settlement}, derive_more::Debug, model::{ interaction::InteractionData, diff --git a/crates/simulator/src/simulation_builder.rs b/crates/simulator/src/simulation_builder.rs index d7b5126774..f79f0622ed 100644 --- a/crates/simulator/src/simulation_builder.rs +++ b/crates/simulator/src/simulation_builder.rs @@ -8,11 +8,12 @@ use { encode_trade, encode_wrapper_settlement, }, - alloy_primitives::{Address, Bytes, U256}, + alloy_primitives::{Address, U256}, alloy_rpc_types::{ TransactionRequest, state::{AccountOverride, StateOverride}, }, + alloy_sol_types::SolCall, model::{ order::{OrderData, OrderKind}, signature::{Signature, SigningScheme}, @@ -60,10 +61,20 @@ impl Order { } } +pub struct FlashloanRequest { + pub amount: U256, + pub borrower: Address, + pub lender: Address, + pub token: Address, +} + /// Configuration for wrapping the settlement in a flashloan or custom wrapper /// contract chain. pub enum WrapperConfig { - Flashloan { router: Address, data: Bytes }, + Flashloan { + router: Address, + loans: Vec, + }, Custom(Vec), } @@ -100,6 +111,7 @@ pub struct SimulationBuilder { wrapper: Option, prices: Option, solver: Option
, + auction_id: Option, state_overrides: StateOverride, } @@ -109,24 +121,18 @@ impl SimulationBuilder { self } - /// Mutate the pre-interactions list via a closure. The order's own - /// pre-hooks are prepended automatically in [`build`]; these interactions - /// follow them. - pub fn with_pre_interactions_mut(mut self, f: impl FnOnce(&mut Vec)) -> Self { - f(&mut self.pre_interactions); + pub fn with_pre_interactions(mut self, interactions: Vec) -> Self { + self.pre_interactions = interactions; self } - pub fn with_main_interactions_mut(mut self, f: impl FnOnce(&mut Vec)) -> Self { - f(&mut self.main_interactions); + pub fn with_main_interactions(mut self, interactions: Vec) -> Self { + self.main_interactions = interactions; self } - /// Mutate the post-interactions list via a closure. The order's own - /// post-hooks are appended automatically in [`build`]; these interactions - /// precede them. - pub fn with_post_interactions_mut(mut self, f: impl FnOnce(&mut Vec)) -> Self { - f(&mut self.post_interactions); + pub fn with_post_interactions(mut self, interactions: Vec) -> Self { + self.post_interactions = interactions; self } @@ -145,6 +151,11 @@ impl SimulationBuilder { self } + pub fn with_auction_id(mut self, id: i64) -> Self { + self.auction_id = Some(id); + self + } + pub fn state_override( mut self, address: Address, @@ -220,21 +231,37 @@ impl SimulationBuilder { customize(&mut settlement); - let settle_calldata = settlement.into_settle_call(); + let settle_calldata = { + let mut bytes = settlement.into_settle_call().to_vec(); + if let Some(id) = self.auction_id { + bytes.extend_from_slice(&id.to_be_bytes()); + } + bytes.into() + }; let (to, input) = match self.wrapper { Some(WrapperConfig::Custom(wrappers)) if !wrappers.is_empty() => { encode_wrapper_settlement(&wrappers, settle_calldata) .expect("wrappers is non-empty") } - Some(WrapperConfig::Flashloan { router, data }) => encode_wrapper_settlement( - &[WrapperCall { - address: router, - data, - }], - settle_calldata, - ) - .expect("wrappers is non-empty"), + Some(WrapperConfig::Flashloan { router, loans }) => { + let calldata = + contracts::FlashLoanRouter::FlashLoanRouter::flashLoanAndSettleCall { + loans: loans + .into_iter() + .map(|l| contracts::FlashLoanRouter::LoanRequest::Data { + amount: l.amount, + borrower: l.borrower, + lender: l.lender, + token: l.token, + }) + .collect(), + settlement: settle_calldata, + } + .abi_encode() + .into(); + (router, calldata) + } _ => (settlement_address, settle_calldata), }; From 417c9a0d8e8e2954bd269ffd98cec0c6001eddaa Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Wed, 22 Apr 2026 10:22:12 +0000 Subject: [PATCH 04/44] introduce Solver enum --- crates/simulator/src/simulation_builder.rs | 38 +++++++++++++++++++--- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/crates/simulator/src/simulation_builder.rs b/crates/simulator/src/simulation_builder.rs index f79f0622ed..df4f353cfe 100644 --- a/crates/simulator/src/simulation_builder.rs +++ b/crates/simulator/src/simulation_builder.rs @@ -68,6 +68,14 @@ pub struct FlashloanRequest { pub token: Address, } +pub enum Solver { + /// A real allow-listed solver address. Used as-is with no state overrides. + Real(Address), + /// A fake solver for simulation. Uses the provided address or generates a + /// random one, then sets its ETH balance to `U256::MAX / 2`. + Fake(Option
), +} + /// Configuration for wrapping the settlement in a flashloan or custom wrapper /// contract chain. pub enum WrapperConfig { @@ -110,7 +118,7 @@ pub struct SimulationBuilder { post_interactions: Vec, wrapper: Option, prices: Option, - solver: Option
, + solver: Option, auction_id: Option, state_overrides: StateOverride, } @@ -146,7 +154,7 @@ impl SimulationBuilder { self } - pub fn from_solver(mut self, solver: Address) -> Self { + pub fn from_solver(mut self, solver: Solver) -> Self { self.solver = Some(solver); self } @@ -265,14 +273,34 @@ impl SimulationBuilder { _ => (settlement_address, settle_calldata), }; + let mut state_overrides = self.state_overrides; + let from = match self.solver { + Some(Solver::Real(addr)) => addr, + Some(Solver::Fake(opt)) => { + let addr = opt.unwrap_or_else(Address::random); + state_overrides.insert( + addr, + AccountOverride { + balance: Some(U256::MAX / U256::from(2)), + ..Default::default() + }, + ); + // TODO: override the settlement's authenticator with AnyoneAuthenticator + // so this address is accepted as a solver. Requires knowing the + // authenticator contract address, which isn't available here yet. + addr + } + None => return Err(BuildError::NoSolver), + }; + Ok(SettlementCall { request: TransactionRequest { - from: self.solver, + from: Some(from), to: Some(to.into()), input: input.into(), ..Default::default() }, - state_overrides: self.state_overrides, + state_overrides, }) } } @@ -281,6 +309,8 @@ impl SimulationBuilder { pub enum BuildError { #[error("no order was added")] NoOrder, + #[error("no solver was set")] + NoSolver, #[error("sell token not found in token list")] MissingSellToken, #[error("buy token not found in token list")] From 19926d90ac55233b4ba65bb4d485e42d5c957470 Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Wed, 22 Apr 2026 10:46:10 +0000 Subject: [PATCH 05/44] factory --- crates/simulator/src/simulation_builder.rs | 67 +++++++++++++++++----- 1 file changed, 52 insertions(+), 15 deletions(-) diff --git a/crates/simulator/src/simulation_builder.rs b/crates/simulator/src/simulation_builder.rs index df4f353cfe..cbee7c3dc1 100644 --- a/crates/simulator/src/simulation_builder.rs +++ b/crates/simulator/src/simulation_builder.rs @@ -1,12 +1,15 @@ use { - crate::encoding::{ - EncodedSettlement, - Interaction, - Interactions, - WrapperCall, - encode_interactions, - encode_trade, - encode_wrapper_settlement, + crate::{ + encoding::{ + EncodedSettlement, + Interaction, + Interactions, + WrapperCall, + encode_interactions, + encode_trade, + encode_wrapper_settlement, + }, + state_override_helpers::AnyoneAuthenticator, }, alloy_primitives::{Address, U256}, alloy_rpc_types::{ @@ -14,10 +17,12 @@ use { state::{AccountOverride, StateOverride}, }, alloy_sol_types::SolCall, + anyhow::Result, model::{ order::{OrderData, OrderKind}, signature::{Signature, SigningScheme}, }, + std::sync::Arc, }; /// A simulator-specific order that bundles the data needed to encode a trade. @@ -121,6 +126,7 @@ pub struct SimulationBuilder { solver: Option, auction_id: Option, state_overrides: StateOverride, + simulator: Option, } impl SimulationBuilder { @@ -174,15 +180,18 @@ impl SimulationBuilder { self } - pub fn build(self, settlement_address: Address) -> Result { - self.build_with_modifications(settlement_address, |_| {}) + pub fn build(self) -> Result { + self.build_with_modifications(|_| {}) } pub fn build_with_modifications( self, - settlement_address: Address, customize: impl FnOnce(&mut EncodedSettlement), ) -> Result { + let simulator = self + .simulator + .as_ref() + .ok_or(BuildError::NoSettlementAddress)?; let order = self.order.as_ref().ok_or(BuildError::NoOrder)?; let (tokens, clearing_prices) = match &self.prices { @@ -270,7 +279,7 @@ impl SimulationBuilder { .into(); (router, calldata) } - _ => (settlement_address, settle_calldata), + _ => (*simulator.0.settlement.address(), settle_calldata), }; let mut state_overrides = self.state_overrides; @@ -285,9 +294,7 @@ impl SimulationBuilder { ..Default::default() }, ); - // TODO: override the settlement's authenticator with AnyoneAuthenticator - // so this address is accepted as a solver. Requires knowing the - // authenticator contract address, which isn't available here yet. + state_overrides.insert(simulator.0.authenticator, AnyoneAuthenticator.into()); addr } None => return Err(BuildError::NoSolver), @@ -307,6 +314,8 @@ impl SimulationBuilder { #[derive(Debug, thiserror::Error)] pub enum BuildError { + #[error("no settlement address was set")] + NoSettlementAddress, #[error("no order was added")] NoOrder, #[error("no solver was set")] @@ -316,3 +325,31 @@ pub enum BuildError { #[error("buy token not found in token list")] MissingBuyToken, } + +struct Inner { + settlement: contracts::GPv2Settlement::Instance, + authenticator: Address, +} + +/// Holds the settlement contract and its authenticator address, and acts as a +/// factory for [`SimulationBuilder`] instances that are pre-configured with +/// these values. +#[derive(Clone)] +pub struct SettlementSimulator(Arc); + +impl SettlementSimulator { + pub async fn new(settlement: contracts::GPv2Settlement::Instance) -> Result { + let authenticator = Address(settlement.authenticator().call().await?.0); + Ok(Self(Arc::new(Inner { + settlement, + authenticator, + }))) + } + + pub fn new_simulation_builder(&self) -> SimulationBuilder { + SimulationBuilder { + simulator: Some(self.clone()), + ..Default::default() + } + } +} From 9731a30628fc0987421451a53ff9540feb4571fe Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Wed, 22 Apr 2026 11:01:24 +0000 Subject: [PATCH 06/44] Only allow list specific solver address --- crates/simulator/src/simulation_builder.rs | 67 ++++++++++++++-------- 1 file changed, 43 insertions(+), 24 deletions(-) diff --git a/crates/simulator/src/simulation_builder.rs b/crates/simulator/src/simulation_builder.rs index cbee7c3dc1..4d06b7ef09 100644 --- a/crates/simulator/src/simulation_builder.rs +++ b/crates/simulator/src/simulation_builder.rs @@ -1,17 +1,14 @@ use { - crate::{ - encoding::{ - EncodedSettlement, - Interaction, - Interactions, - WrapperCall, - encode_interactions, - encode_trade, - encode_wrapper_settlement, - }, - state_override_helpers::AnyoneAuthenticator, + crate::encoding::{ + EncodedSettlement, + Interaction, + Interactions, + WrapperCall, + encode_interactions, + encode_trade, + encode_wrapper_settlement, }, - alloy_primitives::{Address, U256}, + alloy_primitives::{Address, B256, U256, keccak256}, alloy_rpc_types::{ TransactionRequest, state::{AccountOverride, StateOverride}, @@ -115,7 +112,6 @@ pub struct SettlementCall { /// Assembles a GPv2 settlement call for simulation purposes. /// /// Call [`SimulationBuilder::build`] when done to produce a [`SettlementCall`]. -#[derive(Default)] pub struct SimulationBuilder { order: Option, pre_interactions: Vec, @@ -126,7 +122,7 @@ pub struct SimulationBuilder { solver: Option, auction_id: Option, state_overrides: StateOverride, - simulator: Option, + simulator: SettlementSimulator, } impl SimulationBuilder { @@ -188,10 +184,6 @@ impl SimulationBuilder { self, customize: impl FnOnce(&mut EncodedSettlement), ) -> Result { - let simulator = self - .simulator - .as_ref() - .ok_or(BuildError::NoSettlementAddress)?; let order = self.order.as_ref().ok_or(BuildError::NoOrder)?; let (tokens, clearing_prices) = match &self.prices { @@ -279,7 +271,7 @@ impl SimulationBuilder { .into(); (router, calldata) } - _ => (*simulator.0.settlement.address(), settle_calldata), + _ => (*self.simulator.0.settlement.address(), settle_calldata), }; let mut state_overrides = self.state_overrides; @@ -287,6 +279,7 @@ impl SimulationBuilder { Some(Solver::Real(addr)) => addr, Some(Solver::Fake(opt)) => { let addr = opt.unwrap_or_else(Address::random); + // give solver address enough ETH state_overrides.insert( addr, AccountOverride { @@ -294,7 +287,27 @@ impl SimulationBuilder { ..Default::default() }, ); - state_overrides.insert(simulator.0.authenticator, AnyoneAuthenticator.into()); + + // add address to solver allow-list + let target_slot = { + // authenticator stores a `mapping(address=>bool)` in storage + // slot 1 so we can compute precisely which storage slot we + // have to override + let mut buf = [0; 64]; + buf[12..32].copy_from_slice(addr.as_slice()); + buf[32..64].copy_from_slice(&U256::ONE.to_be_bytes::<32>()); + keccak256(buf) + }; + state_overrides.insert( + self.simulator.0.authenticator, + AccountOverride { + state_diff: Some( + // true is encoded as value with the last bit being 1 + std::iter::once((target_slot, B256::with_last_byte(1))).collect(), + ), + ..Default::default() + }, + ); addr } None => return Err(BuildError::NoSolver), @@ -314,8 +327,6 @@ impl SimulationBuilder { #[derive(Debug, thiserror::Error)] pub enum BuildError { - #[error("no settlement address was set")] - NoSettlementAddress, #[error("no order was added")] NoOrder, #[error("no solver was set")] @@ -348,8 +359,16 @@ impl SettlementSimulator { pub fn new_simulation_builder(&self) -> SimulationBuilder { SimulationBuilder { - simulator: Some(self.clone()), - ..Default::default() + simulator: self.clone(), + order: None, + pre_interactions: vec![], + main_interactions: vec![], + post_interactions: vec![], + wrapper: None, + prices: None, + solver: None, + auction_id: None, + state_overrides: StateOverride::default(), } } } From 0f03c74887bca942b08eb9e424b87d05b2232343 Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Wed, 22 Apr 2026 13:34:16 +0000 Subject: [PATCH 07/44] add function for funding settlement contract --- crates/simulator/src/simulation_builder.rs | 62 +++++++++++++++++++--- 1 file changed, 56 insertions(+), 6 deletions(-) diff --git a/crates/simulator/src/simulation_builder.rs b/crates/simulator/src/simulation_builder.rs index 4d06b7ef09..2abe5c35c3 100644 --- a/crates/simulator/src/simulation_builder.rs +++ b/crates/simulator/src/simulation_builder.rs @@ -15,6 +15,7 @@ use { }, alloy_sol_types::SolCall, anyhow::Result, + balance_overrides::{BalanceOverrideRequest, BalanceOverriding}, model::{ order::{OrderData, OrderKind}, signature::{Signature, SigningScheme}, @@ -71,10 +72,15 @@ pub struct FlashloanRequest { } pub enum Solver { - /// A real allow-listed solver address. Used as-is with no state overrides. + /// Simulation assumes this is an actual solver so no state overrides will + /// be applied to allow list it explicitly. + /// If you need a very specific solver setup for your simulation consider + /// using this and explicitly add the necessary state overrides yourself + /// with `Simulation::build_with_modifications()`. Real(Address), /// A fake solver for simulation. Uses the provided address or generates a - /// random one, then sets its ETH balance to `U256::MAX / 2`. + /// random one. The simulation builder will automatically set the required + /// state overrides to give it enough ETH and allow list it as a solver. Fake(Option
), } @@ -123,6 +129,7 @@ pub struct SimulationBuilder { auction_id: Option, state_overrides: StateOverride, simulator: SettlementSimulator, + fund_settlement_contract: bool, } impl SimulationBuilder { @@ -176,11 +183,19 @@ impl SimulationBuilder { self } - pub fn build(self) -> Result { - self.build_with_modifications(|_| {}) + /// Override the settlement contract's buy token balance so it can pay out + /// the order without any external liquidity. The required amount is derived + /// from the order's executed amount and clearing prices at `build()` time. + pub fn fund_settlement_contract(mut self) -> Self { + self.fund_settlement_contract = true; + self + } + + pub async fn build(self) -> Result { + self.build_with_modifications(|_| {}).await } - pub fn build_with_modifications( + pub async fn build_with_modifications( self, customize: impl FnOnce(&mut EncodedSettlement), ) -> Result { @@ -213,6 +228,17 @@ impl SimulationBuilder { OrderKind::Buy => order.data.buy_amount, }; + // Compute before clearing_prices is moved into EncodedSettlement below. + let fund_amount = self + .fund_settlement_contract + .then(|| match order.data.kind { + OrderKind::Sell => clearing_prices[sell_token_index] + .saturating_mul(executed_amount) + .checked_div(clearing_prices[buy_token_index]) + .unwrap_or(U256::MAX), + OrderKind::Buy => executed_amount, + }); + let trade = encode_trade( &order.data, &order.signature, @@ -248,6 +274,20 @@ impl SimulationBuilder { bytes.into() }; + let fund_override = if let Some(amount) = fund_amount { + self.simulator + .0 + .balance_overrides + .state_override(BalanceOverrideRequest { + token: order.data.buy_token, + holder: *self.simulator.0.settlement.address(), + amount, + }) + .await + } else { + None + }; + let (to, input) = match self.wrapper { Some(WrapperConfig::Custom(wrappers)) if !wrappers.is_empty() => { encode_wrapper_settlement(&wrappers, settle_calldata) @@ -313,6 +353,10 @@ impl SimulationBuilder { None => return Err(BuildError::NoSolver), }; + if let Some((addr, account_override)) = fund_override { + state_overrides.insert(addr, account_override); + } + Ok(SettlementCall { request: TransactionRequest { from: Some(from), @@ -340,6 +384,7 @@ pub enum BuildError { struct Inner { settlement: contracts::GPv2Settlement::Instance, authenticator: Address, + balance_overrides: Arc, } /// Holds the settlement contract and its authenticator address, and acts as a @@ -349,11 +394,15 @@ struct Inner { pub struct SettlementSimulator(Arc); impl SettlementSimulator { - pub async fn new(settlement: contracts::GPv2Settlement::Instance) -> Result { + pub async fn new( + settlement: contracts::GPv2Settlement::Instance, + balance_overrides: Arc, + ) -> Result { let authenticator = Address(settlement.authenticator().call().await?.0); Ok(Self(Arc::new(Inner { settlement, authenticator, + balance_overrides, }))) } @@ -369,6 +418,7 @@ impl SettlementSimulator { solver: None, auction_id: None, state_overrides: StateOverride::default(), + fund_settlement_contract: false, } } } From afedc26ba8aa6a58274c3fa2382146d3761ac6df Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Wed, 22 Apr 2026 14:21:00 +0000 Subject: [PATCH 08/44] add executed amounts --- crates/simulator/src/simulation_builder.rs | 62 +++++++++++++--------- 1 file changed, 38 insertions(+), 24 deletions(-) diff --git a/crates/simulator/src/simulation_builder.rs b/crates/simulator/src/simulation_builder.rs index 2abe5c35c3..504fad8174 100644 --- a/crates/simulator/src/simulation_builder.rs +++ b/crates/simulator/src/simulation_builder.rs @@ -34,6 +34,7 @@ pub struct Order { signature: Signature, pre_interactions: Vec, post_interactions: Vec, + executed_amount: Option, } impl Order { @@ -44,6 +45,7 @@ impl Order { signature: Signature::default_with(SigningScheme::Eip1271), pre_interactions: vec![], post_interactions: vec![], + executed_amount: None, } } @@ -62,6 +64,11 @@ impl Order { self.post_interactions = interactions; self } + + pub fn with_executed_amount(mut self, amount: U256) -> Self { + self.executed_amount = Some(amount); + self + } } pub struct FlashloanRequest { @@ -100,7 +107,10 @@ pub enum Prices { /// /// Sets `price[sell_token] = buy_amount` and `price[buy_token] = /// sell_amount`, exactly satisfying the order's limit with no surplus. + /// This should NOT be used when encoding solutions you actually want + /// to submit. Limit, + // TODO: check how this can be made nicer. /// Explicit token list and matching clearing prices. Explicit { tokens: Vec
, @@ -191,21 +201,25 @@ impl SimulationBuilder { self } + /// Finishes the simulation struct based on the configuration thus far. pub async fn build(self) -> Result { self.build_with_modifications(|_| {}).await } + /// Same as `build()` but allows the caller to alter the simulation + /// before it gets finalized. This should only be used for very speicific + /// setups. pub async fn build_with_modifications( self, customize: impl FnOnce(&mut EncodedSettlement), ) -> Result { let order = self.order.as_ref().ok_or(BuildError::NoOrder)?; - let (tokens, clearing_prices) = match &self.prices { + let (tokens, clearing_prices) = match self.prices { Some(Prices::Explicit { tokens, clearing_prices, - }) => (tokens.clone(), clearing_prices.clone()), + }) => (tokens, clearing_prices), // At limit price: price[sell_token] = buy_amount, price[buy_token] = sell_amount. // This makes sell_amount * price[sell] / price[buy] = buy_amount exactly. _ => ( @@ -223,10 +237,12 @@ impl SimulationBuilder { .position(|t| *t == order.data.buy_token) .ok_or(BuildError::MissingBuyToken)?; - let executed_amount = match order.data.kind { - OrderKind::Sell => order.data.sell_amount, - OrderKind::Buy => order.data.buy_amount, - }; + let executed_amount = order + .executed_amount + .unwrap_or_else(|| match order.data.kind { + OrderKind::Sell => order.data.sell_amount, + OrderKind::Buy => order.data.buy_amount, + }); // Compute before clearing_prices is moved into EncodedSettlement below. let fund_amount = self @@ -274,20 +290,6 @@ impl SimulationBuilder { bytes.into() }; - let fund_override = if let Some(amount) = fund_amount { - self.simulator - .0 - .balance_overrides - .state_override(BalanceOverrideRequest { - token: order.data.buy_token, - holder: *self.simulator.0.settlement.address(), - amount, - }) - .await - } else { - None - }; - let (to, input) = match self.wrapper { Some(WrapperConfig::Custom(wrappers)) if !wrappers.is_empty() => { encode_wrapper_settlement(&wrappers, settle_calldata) @@ -352,10 +354,20 @@ impl SimulationBuilder { } None => return Err(BuildError::NoSolver), }; - - if let Some((addr, account_override)) = fund_override { - state_overrides.insert(addr, account_override); - } + if let Some(amount) = fund_amount { + let (address, state_override) = self + .simulator + .0 + .balance_overrides + .state_override(BalanceOverrideRequest { + token: order.data.buy_token, + holder: *self.simulator.0.settlement.address(), + amount, + }) + .await + .ok_or(BuildError::FailedToOverrideBalances)?; + state_overrides.insert(address, state_override); + }; Ok(SettlementCall { request: TransactionRequest { @@ -379,6 +391,8 @@ pub enum BuildError { MissingSellToken, #[error("buy token not found in token list")] MissingBuyToken, + #[error("could not override token balances to fund settlement contract")] + FailedToOverrideBalances, } struct Inner { From db932ac8426f4d43a90a4ece056e08c8c72feee7 Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Wed, 22 Apr 2026 15:49:57 +0000 Subject: [PATCH 09/44] Move flashloan router address into simulator thingy --- crates/simulator/src/simulation_builder.rs | 60 +++++++------------ .../simulator/src/state_override_helpers.rs | 40 +++++++++---- 2 files changed, 50 insertions(+), 50 deletions(-) diff --git a/crates/simulator/src/simulation_builder.rs b/crates/simulator/src/simulation_builder.rs index 504fad8174..b228fcf98a 100644 --- a/crates/simulator/src/simulation_builder.rs +++ b/crates/simulator/src/simulation_builder.rs @@ -1,14 +1,17 @@ use { - crate::encoding::{ - EncodedSettlement, - Interaction, - Interactions, - WrapperCall, - encode_interactions, - encode_trade, - encode_wrapper_settlement, + crate::{ + encoding::{ + EncodedSettlement, + Interaction, + Interactions, + WrapperCall, + encode_interactions, + encode_trade, + encode_wrapper_settlement, + }, + state_override_helpers::{EthBalanceOverride, SolverAllowlisting}, }, - alloy_primitives::{Address, B256, U256, keccak256}, + alloy_primitives::{Address, U256}, alloy_rpc_types::{ TransactionRequest, state::{AccountOverride, StateOverride}, @@ -94,10 +97,7 @@ pub enum Solver { /// Configuration for wrapping the settlement in a flashloan or custom wrapper /// contract chain. pub enum WrapperConfig { - Flashloan { - router: Address, - loans: Vec, - }, + Flashloan { loans: Vec }, Custom(Vec), } @@ -295,7 +295,7 @@ impl SimulationBuilder { encode_wrapper_settlement(&wrappers, settle_calldata) .expect("wrappers is non-empty") } - Some(WrapperConfig::Flashloan { router, loans }) => { + Some(WrapperConfig::Flashloan { loans }) => { let calldata = contracts::FlashLoanRouter::FlashLoanRouter::flashLoanAndSettleCall { loans: loans @@ -311,7 +311,7 @@ impl SimulationBuilder { } .abi_encode() .into(); - (router, calldata) + (self.simulator.0.flash_loan_router, calldata) } _ => (*self.simulator.0.settlement.address(), settle_calldata), }; @@ -321,34 +321,11 @@ impl SimulationBuilder { Some(Solver::Real(addr)) => addr, Some(Solver::Fake(opt)) => { let addr = opt.unwrap_or_else(Address::random); - // give solver address enough ETH - state_overrides.insert( - addr, - AccountOverride { - balance: Some(U256::MAX / U256::from(2)), - ..Default::default() - }, - ); + state_overrides.insert(addr, EthBalanceOverride(U256::MAX / U256::from(2)).into()); - // add address to solver allow-list - let target_slot = { - // authenticator stores a `mapping(address=>bool)` in storage - // slot 1 so we can compute precisely which storage slot we - // have to override - let mut buf = [0; 64]; - buf[12..32].copy_from_slice(addr.as_slice()); - buf[32..64].copy_from_slice(&U256::ONE.to_be_bytes::<32>()); - keccak256(buf) - }; state_overrides.insert( self.simulator.0.authenticator, - AccountOverride { - state_diff: Some( - // true is encoded as value with the last bit being 1 - std::iter::once((target_slot, B256::with_last_byte(1))).collect(), - ), - ..Default::default() - }, + SolverAllowlisting(addr).into(), ); addr } @@ -398,6 +375,7 @@ pub enum BuildError { struct Inner { settlement: contracts::GPv2Settlement::Instance, authenticator: Address, + flash_loan_router: Address, balance_overrides: Arc, } @@ -410,12 +388,14 @@ pub struct SettlementSimulator(Arc); impl SettlementSimulator { pub async fn new( settlement: contracts::GPv2Settlement::Instance, + flash_loan_router: Address, balance_overrides: Arc, ) -> Result { let authenticator = Address(settlement.authenticator().call().await?.0); Ok(Self(Arc::new(Inner { settlement, authenticator, + flash_loan_router, balance_overrides, }))) } diff --git a/crates/simulator/src/state_override_helpers.rs b/crates/simulator/src/state_override_helpers.rs index d30ec72607..b3aec19451 100644 --- a/crates/simulator/src/state_override_helpers.rs +++ b/crates/simulator/src/state_override_helpers.rs @@ -1,4 +1,8 @@ -use {alloy_primitives::Bytes, alloy_rpc_types::state::AccountOverride}; +use { + alloy_primitives::{Address, B256, Bytes, U256, keccak256}, + alloy_rpc_types::state::AccountOverride, + std::iter, +}; pub use { balance_overrides::{BalanceOverrideRequest, BalanceOverrides, BalanceOverriding}, configs::balance_overrides::Strategy, @@ -29,19 +33,35 @@ impl From for AccountOverride { } } -/// Deploys the `AnyoneAuthenticator` contract at a given address, causing it -/// to approve any solver. Pass to +/// Sets the ETH balance of an address to the given value. +pub struct EthBalanceOverride(pub U256); + +impl From for AccountOverride { + fn from(EthBalanceOverride(balance): EthBalanceOverride) -> Self { + Self { + balance: Some(balance), + ..Default::default() + } + } +} + +/// Overrides the authenticator contract's storage to allowlist a single solver +/// address. Pass to /// [`crate::simulation_builder::SimulationBuilder::state_override`] /// with the authenticator contract's address. -pub struct AnyoneAuthenticator; +pub struct SolverAllowlisting(pub Address); -impl From for AccountOverride { - fn from(_: AnyoneAuthenticator) -> Self { +impl From for AccountOverride { + fn from(SolverAllowlisting(solver): SolverAllowlisting) -> Self { + // GPv2AllowListAuthentication stores `mapping(address => bool) managers` + // at storage slot 1. Solidity mapping key: keccak256(address_padded ++ + // slot_padded). + let mut buf = [0u8; 64]; + buf[12..32].copy_from_slice(solver.as_slice()); + buf[32..64].copy_from_slice(&U256::ONE.to_be_bytes::<32>()); + let slot = keccak256(buf); Self { - code: Some( - contracts::support::AnyoneAuthenticator::AnyoneAuthenticator::DEPLOYED_BYTECODE - .clone(), - ), + state_diff: Some(iter::once((slot, B256::with_last_byte(1))).collect()), ..Default::default() } } From 258539b821b9dd08a53979e3d8bf253bfc6e52f0 Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Wed, 22 Apr 2026 15:52:58 +0000 Subject: [PATCH 10/44] fixup --- crates/simulator/src/simulation_builder.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/crates/simulator/src/simulation_builder.rs b/crates/simulator/src/simulation_builder.rs index b228fcf98a..34e4053552 100644 --- a/crates/simulator/src/simulation_builder.rs +++ b/crates/simulator/src/simulation_builder.rs @@ -97,7 +97,7 @@ pub enum Solver { /// Configuration for wrapping the settlement in a flashloan or custom wrapper /// contract chain. pub enum WrapperConfig { - Flashloan { loans: Vec }, + Flashloan(Vec), Custom(Vec), } @@ -237,12 +237,10 @@ impl SimulationBuilder { .position(|t| *t == order.data.buy_token) .ok_or(BuildError::MissingBuyToken)?; - let executed_amount = order - .executed_amount - .unwrap_or_else(|| match order.data.kind { - OrderKind::Sell => order.data.sell_amount, - OrderKind::Buy => order.data.buy_amount, - }); + let executed_amount = order.executed_amount.unwrap_or(match order.data.kind { + OrderKind::Sell => order.data.sell_amount, + OrderKind::Buy => order.data.buy_amount, + }); // Compute before clearing_prices is moved into EncodedSettlement below. let fund_amount = self @@ -295,7 +293,7 @@ impl SimulationBuilder { encode_wrapper_settlement(&wrappers, settle_calldata) .expect("wrappers is non-empty") } - Some(WrapperConfig::Flashloan { loans }) => { + Some(WrapperConfig::Flashloan(loans)) => { let calldata = contracts::FlashLoanRouter::FlashLoanRouter::flashLoanAndSettleCall { loans: loans From 969ffee59158a5a4ccbec4540e0828ec153e46fc Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Fri, 24 Apr 2026 14:10:09 +0000 Subject: [PATCH 11/44] simulate(), handle fillable amounts --- Cargo.lock | 1 + crates/simulator/Cargo.toml | 1 + crates/simulator/src/encoding.rs | 2 +- crates/simulator/src/simulation_builder.rs | 78 +++++++++++++++++++--- 4 files changed, 71 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9d6a24c147..e7c172064a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7774,6 +7774,7 @@ version = "0.1.0" dependencies = [ "alloy-contract", "alloy-eips", + "alloy-network", "alloy-primitives", "alloy-provider", "alloy-rpc-types", diff --git a/crates/simulator/Cargo.toml b/crates/simulator/Cargo.toml index 1b4777003b..3f5d900acd 100644 --- a/crates/simulator/Cargo.toml +++ b/crates/simulator/Cargo.toml @@ -8,6 +8,7 @@ license = "MIT OR Apache-2.0" [dependencies] alloy-contract = { workspace = true } alloy-eips = { workspace = true } +alloy-network = { workspace = true } alloy-primitives = { workspace = true } alloy-provider = { workspace = true } alloy-rpc-types = { workspace = true } diff --git a/crates/simulator/src/encoding.rs b/crates/simulator/src/encoding.rs index d318450341..8c6d188c24 100644 --- a/crates/simulator/src/encoding.rs +++ b/crates/simulator/src/encoding.rs @@ -2,7 +2,7 @@ use { alloy_primitives::{Address, B256, Bytes, U256}, alloy_sol_types::SolCall, app_data::AppDataHash, - contracts::{FlashLoanRouter::LoanRequest, GPv2Settlement}, + contracts::GPv2Settlement, derive_more::Debug, model::{ interaction::InteractionData, diff --git a/crates/simulator/src/simulation_builder.rs b/crates/simulator/src/simulation_builder.rs index 34e4053552..81ee4889f4 100644 --- a/crates/simulator/src/simulation_builder.rs +++ b/crates/simulator/src/simulation_builder.rs @@ -11,7 +11,9 @@ use { }, state_override_helpers::{EthBalanceOverride, SolverAllowlisting}, }, - alloy_primitives::{Address, U256}, + alloy_network::Ethereum, + alloy_primitives::{Address, Bytes, U256}, + alloy_provider::{DynProvider, EthCall, Provider}, alloy_rpc_types::{ TransactionRequest, state::{AccountOverride, StateOverride}, @@ -20,6 +22,7 @@ use { anyhow::Result, balance_overrides::{BalanceOverrideRequest, BalanceOverriding}, model::{ + DomainSeparator, order::{OrderData, OrderKind}, signature::{Signature, SigningScheme}, }, @@ -120,9 +123,21 @@ pub enum Prices { /// The output of [`SimulationBuilder::build`]: a transaction request and state /// overrides ready to be passed to an alloy provider for simulation. -pub struct SettlementCall { +pub struct EthCallInputs { pub request: TransactionRequest, pub state_overrides: StateOverride, + pub provider: DynProvider, +} + +impl EthCallInputs { + /// Prepares an `eth_call` with the transaction request and state overrides + /// already applied. The call is not sent — callers can chain additional + /// builder methods (e.g. `.block(...)`) before awaiting. + pub fn simulate(self) -> EthCall { + self.provider + .call(self.request) + .overrides(self.state_overrides) + } } /// Assembles a GPv2 settlement call for simulation purposes. @@ -202,7 +217,7 @@ impl SimulationBuilder { } /// Finishes the simulation struct based on the configuration thus far. - pub async fn build(self) -> Result { + pub async fn build(self) -> Result { self.build_with_modifications(|_| {}).await } @@ -212,9 +227,11 @@ impl SimulationBuilder { pub async fn build_with_modifications( self, customize: impl FnOnce(&mut EncodedSettlement), - ) -> Result { + ) -> Result { let order = self.order.as_ref().ok_or(BuildError::NoOrder)?; + let executed_amount = self.executed_amount(order).await; + let (tokens, clearing_prices) = match self.prices { Some(Prices::Explicit { tokens, @@ -237,11 +254,6 @@ impl SimulationBuilder { .position(|t| *t == order.data.buy_token) .ok_or(BuildError::MissingBuyToken)?; - let executed_amount = order.executed_amount.unwrap_or(match order.data.kind { - OrderKind::Sell => order.data.sell_amount, - OrderKind::Buy => order.data.buy_amount, - }); - // Compute before clearing_prices is moved into EncodedSettlement below. let fund_amount = self .fund_settlement_contract @@ -344,7 +356,7 @@ impl SimulationBuilder { state_overrides.insert(address, state_override); }; - Ok(SettlementCall { + Ok(EthCallInputs { request: TransactionRequest { from: Some(from), to: Some(to.into()), @@ -352,8 +364,48 @@ impl SimulationBuilder { ..Default::default() }, state_overrides, + provider: self.simulator.0.provider.clone(), }) } + + /// Determines the amount the order is supposed to be filled with. + /// If user did not configure a value the order's remaining fillable + /// amount will be chosen (determined by call to settlement contract). + async fn executed_amount(&self, order: &Order) -> U256 { + if let Some(executed_amount) = order.executed_amount { + return executed_amount; + } + + let full = match order.data.kind { + OrderKind::Sell => order.data.sell_amount, + OrderKind::Buy => order.data.buy_amount, + }; + + let uid = order + .data + .uid(&self.simulator.0.domain_separator, order.owner); + let filled_res = self + .simulator + .0 + .settlement + .filledAmount(Bytes::from(uid.0)) + .call() + .await; + + match filled_res { + Err(err) => { + // doesn't make sense to pre-emptively fail the simulation + // in this case. Most orders are not filled at all so it's + // reasonable to use the full order amount as a fallback here. + tracing::debug!( + ?err, + "failed to query executed amount - assume full order amount" + ); + full + } + Ok(filled_amount) => full.saturating_sub(filled_amount), + } + } } #[derive(Debug, thiserror::Error)] @@ -375,6 +427,8 @@ struct Inner { authenticator: Address, flash_loan_router: Address, balance_overrides: Arc, + provider: DynProvider, + domain_separator: DomainSeparator, } /// Holds the settlement contract and its authenticator address, and acts as a @@ -390,11 +444,15 @@ impl SettlementSimulator { balance_overrides: Arc, ) -> Result { let authenticator = Address(settlement.authenticator().call().await?.0); + let domain_separator = DomainSeparator(settlement.domainSeparator().call().await?.0); + let provider = settlement.provider().clone(); Ok(Self(Arc::new(Inner { settlement, authenticator, flash_loan_router, balance_overrides, + provider, + domain_separator, }))) } From ae91cf6f7233c27f9a0743818f83805540a63437 Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Mon, 27 Apr 2026 12:11:58 +0000 Subject: [PATCH 12/44] Better handling of block target and executed amount --- crates/simulator/src/simulation_builder.rs | 98 +++++++++++++--------- 1 file changed, 59 insertions(+), 39 deletions(-) diff --git a/crates/simulator/src/simulation_builder.rs b/crates/simulator/src/simulation_builder.rs index 81ee4889f4..139fd502fb 100644 --- a/crates/simulator/src/simulation_builder.rs +++ b/crates/simulator/src/simulation_builder.rs @@ -11,6 +11,7 @@ use { }, state_override_helpers::{EthBalanceOverride, SolverAllowlisting}, }, + alloy_eips::BlockId, alloy_network::Ethereum, alloy_primitives::{Address, Bytes, U256}, alloy_provider::{DynProvider, EthCall, Provider}, @@ -29,6 +30,19 @@ use { std::sync::Arc, }; +/// How much of an order should be filled during simulation. +pub enum ExecutionAmount { + /// Fill the full order amount (sell_amount for sell orders, buy_amount for + /// buy orders), ignoring any on-chain filled state. + Full, + /// Fill whatever is still remaining on-chain (queries the settlement + /// contract for the already-filled amount and subtracts it). Falls back to + /// the full amount if the query fails. + Remaining, + /// Use an explicit fill amount. + Explicit(U256), +} + /// A simulator-specific order that bundles the data needed to encode a trade. /// /// Construct with [`Order::new`] and add optional fields via the builder @@ -40,7 +54,7 @@ pub struct Order { signature: Signature, pre_interactions: Vec, post_interactions: Vec, - executed_amount: Option, + executed_amount: ExecutionAmount, } impl Order { @@ -51,7 +65,7 @@ impl Order { signature: Signature::default_with(SigningScheme::Eip1271), pre_interactions: vec![], post_interactions: vec![], - executed_amount: None, + executed_amount: ExecutionAmount::Remaining, } } @@ -71,8 +85,8 @@ impl Order { self } - pub fn with_executed_amount(mut self, amount: U256) -> Self { - self.executed_amount = Some(amount); + pub fn with_executed_amount(mut self, amount: ExecutionAmount) -> Self { + self.executed_amount = amount; self } } @@ -127,16 +141,18 @@ pub struct EthCallInputs { pub request: TransactionRequest, pub state_overrides: StateOverride, pub provider: DynProvider, + pub block: BlockId, } impl EthCallInputs { - /// Prepares an `eth_call` with the transaction request and state overrides - /// already applied. The call is not sent — callers can chain additional - /// builder methods (e.g. `.block(...)`) before awaiting. + /// Prepares an `eth_call` with the transaction request, state overrides, + /// and block already applied. The call is not sent — callers can chain + /// additional builder methods before awaiting. pub fn simulate(self) -> EthCall { self.provider .call(self.request) .overrides(self.state_overrides) + .block(self.block) } } @@ -155,6 +171,7 @@ pub struct SimulationBuilder { state_overrides: StateOverride, simulator: SettlementSimulator, fund_settlement_contract: bool, + block: BlockId, } impl SimulationBuilder { @@ -208,6 +225,11 @@ impl SimulationBuilder { self } + pub fn at_block(mut self, block: BlockId) -> Self { + self.block = block; + self + } + /// Override the settlement contract's buy token balance so it can pay out /// the order without any external liquidity. The required amount is derived /// from the order's executed amount and clearing prices at `build()` time. @@ -230,7 +252,7 @@ impl SimulationBuilder { ) -> Result { let order = self.order.as_ref().ok_or(BuildError::NoOrder)?; - let executed_amount = self.executed_amount(order).await; + let executed_amount = self.executed_amount(order).await?; let (tokens, clearing_prices) = match self.prices { Some(Prices::Explicit { @@ -239,10 +261,13 @@ impl SimulationBuilder { }) => (tokens, clearing_prices), // At limit price: price[sell_token] = buy_amount, price[buy_token] = sell_amount. // This makes sell_amount * price[sell] / price[buy] = buy_amount exactly. - _ => ( + Some(Prices::Limit) => ( vec![order.data.sell_token, order.data.buy_token], vec![order.data.buy_amount, order.data.sell_amount], ), + None => { + return Err(BuildError::NoPriceEncoding); + } }; let sell_token_index = tokens @@ -365,46 +390,36 @@ impl SimulationBuilder { }, state_overrides, provider: self.simulator.0.provider.clone(), + block: self.block, }) } /// Determines the amount the order is supposed to be filled with. - /// If user did not configure a value the order's remaining fillable - /// amount will be chosen (determined by call to settlement contract). - async fn executed_amount(&self, order: &Order) -> U256 { - if let Some(executed_amount) = order.executed_amount { - return executed_amount; - } - + async fn executed_amount(&self, order: &Order) -> Result { let full = match order.data.kind { OrderKind::Sell => order.data.sell_amount, OrderKind::Buy => order.data.buy_amount, }; - let uid = order - .data - .uid(&self.simulator.0.domain_separator, order.owner); - let filled_res = self - .simulator - .0 - .settlement - .filledAmount(Bytes::from(uid.0)) - .call() - .await; - - match filled_res { - Err(err) => { - // doesn't make sense to pre-emptively fail the simulation - // in this case. Most orders are not filled at all so it's - // reasonable to use the full order amount as a fallback here. - tracing::debug!( - ?err, - "failed to query executed amount - assume full order amount" - ); - full + Ok(match order.executed_amount { + ExecutionAmount::Full => full, + ExecutionAmount::Explicit(amount) => amount, + ExecutionAmount::Remaining => { + let uid = order + .data + .uid(&self.simulator.0.domain_separator, order.owner); + let filled_amount = self + .simulator + .0 + .settlement + .filledAmount(Bytes::from(uid.0)) + .block(self.block) + .call() + .await + .map_err(|err| BuildError::FilledAmountQuery(err.into()))?; + full.saturating_sub(filled_amount) } - Ok(filled_amount) => full.saturating_sub(filled_amount), - } + }) } } @@ -420,6 +435,10 @@ pub enum BuildError { MissingBuyToken, #[error("could not override token balances to fund settlement contract")] FailedToOverrideBalances, + #[error("no strategy to compute the price vector was chosen")] + NoPriceEncoding, + #[error("failed to query filled amount from settlement contract: {0}")] + FilledAmountQuery(#[source] anyhow::Error), } struct Inner { @@ -469,6 +488,7 @@ impl SettlementSimulator { auction_id: None, state_overrides: StateOverride::default(), fund_settlement_contract: false, + block: BlockId::latest(), } } } From 2ccedbb8900d9fbdae1e8c197bc7c2f034536949 Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Mon, 27 Apr 2026 12:24:59 +0000 Subject: [PATCH 13/44] Move encoding logic into separate file --- crates/simulator/src/lib.rs | 1 + crates/simulator/src/simulation_builder.rs | 245 +++----------------- crates/simulator/src/simulation_encoding.rs | 199 ++++++++++++++++ 3 files changed, 233 insertions(+), 212 deletions(-) create mode 100644 crates/simulator/src/simulation_encoding.rs diff --git a/crates/simulator/src/lib.rs b/crates/simulator/src/lib.rs index 83f251e223..a0d66dd65f 100644 --- a/crates/simulator/src/lib.rs +++ b/crates/simulator/src/lib.rs @@ -1,6 +1,7 @@ pub mod encoding; pub mod ethereum; pub mod simulation_builder; +mod simulation_encoding; pub mod state_override_helpers; pub mod swap_simulator; pub mod tenderly; diff --git a/crates/simulator/src/simulation_builder.rs b/crates/simulator/src/simulation_builder.rs index 139fd502fb..84a0b81d91 100644 --- a/crates/simulator/src/simulation_builder.rs +++ b/crates/simulator/src/simulation_builder.rs @@ -1,16 +1,5 @@ use { - crate::{ - encoding::{ - EncodedSettlement, - Interaction, - Interactions, - WrapperCall, - encode_interactions, - encode_trade, - encode_wrapper_settlement, - }, - state_override_helpers::{EthBalanceOverride, SolverAllowlisting}, - }, + crate::encoding::{EncodedSettlement, Interaction, WrapperCall}, alloy_eips::BlockId, alloy_network::Ethereum, alloy_primitives::{Address, Bytes, U256}, @@ -19,12 +8,11 @@ use { TransactionRequest, state::{AccountOverride, StateOverride}, }, - alloy_sol_types::SolCall, anyhow::Result, - balance_overrides::{BalanceOverrideRequest, BalanceOverriding}, + balance_overrides::BalanceOverriding, model::{ DomainSeparator, - order::{OrderData, OrderKind}, + order::OrderData, signature::{Signature, SigningScheme}, }, std::sync::Arc, @@ -49,12 +37,12 @@ pub enum ExecutionAmount { /// methods. Defaults to an EIP-1271 signature (pairs with [`FakeUser`] for /// simulations that need to bypass signature verification). pub struct Order { - data: OrderData, - owner: Address, - signature: Signature, - pre_interactions: Vec, - post_interactions: Vec, - executed_amount: ExecutionAmount, + pub(crate) data: OrderData, + pub(crate) owner: Address, + pub(crate) signature: Signature, + pub(crate) pre_interactions: Vec, + pub(crate) post_interactions: Vec, + pub(crate) executed_amount: ExecutionAmount, } impl Order { @@ -160,18 +148,18 @@ impl EthCallInputs { /// /// Call [`SimulationBuilder::build`] when done to produce a [`SettlementCall`]. pub struct SimulationBuilder { - order: Option, - pre_interactions: Vec, - main_interactions: Vec, - post_interactions: Vec, - wrapper: Option, - prices: Option, - solver: Option, - auction_id: Option, - state_overrides: StateOverride, - simulator: SettlementSimulator, - fund_settlement_contract: bool, - block: BlockId, + pub(crate) order: Option, + pub(crate) pre_interactions: Vec, + pub(crate) main_interactions: Vec, + pub(crate) post_interactions: Vec, + pub(crate) wrapper: Option, + pub(crate) prices: Option, + pub(crate) solver: Option, + pub(crate) auction_id: Option, + pub(crate) state_overrides: StateOverride, + pub(crate) simulator: SettlementSimulator, + pub(crate) fund_settlement_contract: bool, + pub(crate) block: BlockId, } impl SimulationBuilder { @@ -244,182 +232,15 @@ impl SimulationBuilder { } /// Same as `build()` but allows the caller to alter the simulation - /// before it gets finalized. This should only be used for very speicific + /// before it gets finalized. This should only be used for very specific /// setups. pub async fn build_with_modifications( self, customize: impl FnOnce(&mut EncodedSettlement), ) -> Result { - let order = self.order.as_ref().ok_or(BuildError::NoOrder)?; - - let executed_amount = self.executed_amount(order).await?; - - let (tokens, clearing_prices) = match self.prices { - Some(Prices::Explicit { - tokens, - clearing_prices, - }) => (tokens, clearing_prices), - // At limit price: price[sell_token] = buy_amount, price[buy_token] = sell_amount. - // This makes sell_amount * price[sell] / price[buy] = buy_amount exactly. - Some(Prices::Limit) => ( - vec![order.data.sell_token, order.data.buy_token], - vec![order.data.buy_amount, order.data.sell_amount], - ), - None => { - return Err(BuildError::NoPriceEncoding); - } - }; - - let sell_token_index = tokens - .iter() - .position(|t| *t == order.data.sell_token) - .ok_or(BuildError::MissingSellToken)?; - let buy_token_index = tokens - .iter() - .position(|t| *t == order.data.buy_token) - .ok_or(BuildError::MissingBuyToken)?; - - // Compute before clearing_prices is moved into EncodedSettlement below. - let fund_amount = self - .fund_settlement_contract - .then(|| match order.data.kind { - OrderKind::Sell => clearing_prices[sell_token_index] - .saturating_mul(executed_amount) - .checked_div(clearing_prices[buy_token_index]) - .unwrap_or(U256::MAX), - OrderKind::Buy => executed_amount, - }); - - let trade = encode_trade( - &order.data, - &order.signature, - order.owner, - sell_token_index, - buy_token_index, - executed_amount, - ); - - let order_pre = &order.pre_interactions; - let order_post = &order.post_interactions; - - let mut settlement = EncodedSettlement { - tokens, - clearing_prices, - trades: vec![trade], - interactions: Interactions { - // order's pre-hooks run before any additional pre-interactions - pre: encode_interactions(order_pre.iter().chain(&self.pre_interactions)), - main: encode_interactions(&self.main_interactions), - // additional post-interactions run before the order's post-hooks - post: encode_interactions(self.post_interactions.iter().chain(order_post)), - }, - }; - - customize(&mut settlement); - - let settle_calldata = { - let mut bytes = settlement.into_settle_call().to_vec(); - if let Some(id) = self.auction_id { - bytes.extend_from_slice(&id.to_be_bytes()); - } - bytes.into() - }; - - let (to, input) = match self.wrapper { - Some(WrapperConfig::Custom(wrappers)) if !wrappers.is_empty() => { - encode_wrapper_settlement(&wrappers, settle_calldata) - .expect("wrappers is non-empty") - } - Some(WrapperConfig::Flashloan(loans)) => { - let calldata = - contracts::FlashLoanRouter::FlashLoanRouter::flashLoanAndSettleCall { - loans: loans - .into_iter() - .map(|l| contracts::FlashLoanRouter::LoanRequest::Data { - amount: l.amount, - borrower: l.borrower, - lender: l.lender, - token: l.token, - }) - .collect(), - settlement: settle_calldata, - } - .abi_encode() - .into(); - (self.simulator.0.flash_loan_router, calldata) - } - _ => (*self.simulator.0.settlement.address(), settle_calldata), - }; - - let mut state_overrides = self.state_overrides; - let from = match self.solver { - Some(Solver::Real(addr)) => addr, - Some(Solver::Fake(opt)) => { - let addr = opt.unwrap_or_else(Address::random); - state_overrides.insert(addr, EthBalanceOverride(U256::MAX / U256::from(2)).into()); - - state_overrides.insert( - self.simulator.0.authenticator, - SolverAllowlisting(addr).into(), - ); - addr - } - None => return Err(BuildError::NoSolver), - }; - if let Some(amount) = fund_amount { - let (address, state_override) = self - .simulator - .0 - .balance_overrides - .state_override(BalanceOverrideRequest { - token: order.data.buy_token, - holder: *self.simulator.0.settlement.address(), - amount, - }) - .await - .ok_or(BuildError::FailedToOverrideBalances)?; - state_overrides.insert(address, state_override); - }; - - Ok(EthCallInputs { - request: TransactionRequest { - from: Some(from), - to: Some(to.into()), - input: input.into(), - ..Default::default() - }, - state_overrides, - provider: self.simulator.0.provider.clone(), - block: self.block, - }) - } - - /// Determines the amount the order is supposed to be filled with. - async fn executed_amount(&self, order: &Order) -> Result { - let full = match order.data.kind { - OrderKind::Sell => order.data.sell_amount, - OrderKind::Buy => order.data.buy_amount, - }; - - Ok(match order.executed_amount { - ExecutionAmount::Full => full, - ExecutionAmount::Explicit(amount) => amount, - ExecutionAmount::Remaining => { - let uid = order - .data - .uid(&self.simulator.0.domain_separator, order.owner); - let filled_amount = self - .simulator - .0 - .settlement - .filledAmount(Bytes::from(uid.0)) - .block(self.block) - .call() - .await - .map_err(|err| BuildError::FilledAmountQuery(err.into()))?; - full.saturating_sub(filled_amount) - } - }) + // Forward to a helper function to split the boring repetitive builder + // code from the non-trivial code that actually does the encoding. + crate::simulation_encoding::encode(self, customize).await } } @@ -441,20 +262,20 @@ pub enum BuildError { FilledAmountQuery(#[source] anyhow::Error), } -struct Inner { - settlement: contracts::GPv2Settlement::Instance, - authenticator: Address, - flash_loan_router: Address, - balance_overrides: Arc, - provider: DynProvider, - domain_separator: DomainSeparator, +pub(crate) struct Inner { + pub(crate) settlement: contracts::GPv2Settlement::Instance, + pub(crate) authenticator: Address, + pub(crate) flash_loan_router: Address, + pub(crate) balance_overrides: Arc, + pub(crate) provider: DynProvider, + pub(crate) domain_separator: DomainSeparator, } /// Holds the settlement contract and its authenticator address, and acts as a /// factory for [`SimulationBuilder`] instances that are pre-configured with /// these values. #[derive(Clone)] -pub struct SettlementSimulator(Arc); +pub struct SettlementSimulator(pub(crate) Arc); impl SettlementSimulator { pub async fn new( diff --git a/crates/simulator/src/simulation_encoding.rs b/crates/simulator/src/simulation_encoding.rs new file mode 100644 index 0000000000..d3d8c2f916 --- /dev/null +++ b/crates/simulator/src/simulation_encoding.rs @@ -0,0 +1,199 @@ +use { + crate::{ + encoding::{ + EncodedSettlement, + Interactions, + encode_interactions, + encode_trade, + encode_wrapper_settlement, + }, + simulation_builder::{ + BuildError, + EthCallInputs, + ExecutionAmount, + Order, + Prices, + SimulationBuilder, + Solver, + WrapperConfig, + }, + state_override_helpers::{EthBalanceOverride, SolverAllowlisting}, + }, + alloy_primitives::{Address, Bytes, U256}, + alloy_rpc_types::TransactionRequest, + alloy_sol_types::SolCall, + balance_overrides::BalanceOverrideRequest, + model::order::OrderKind, +}; + +pub(crate) async fn encode( + builder: SimulationBuilder, + customize: impl FnOnce(&mut EncodedSettlement), +) -> Result { + let order = builder.order.as_ref().ok_or(BuildError::NoOrder)?; + + let executed_amount = executed_amount(&builder, order).await?; + + let (tokens, clearing_prices) = match builder.prices { + Some(Prices::Explicit { + tokens, + clearing_prices, + }) => (tokens, clearing_prices), + // At limit price: price[sell_token] = buy_amount, price[buy_token] = sell_amount. + // This makes sell_amount * price[sell] / price[buy] = buy_amount exactly. + Some(Prices::Limit) => ( + vec![order.data.sell_token, order.data.buy_token], + vec![order.data.buy_amount, order.data.sell_amount], + ), + None => { + return Err(BuildError::NoPriceEncoding); + } + }; + + let sell_token_index = tokens + .iter() + .position(|t| *t == order.data.sell_token) + .ok_or(BuildError::MissingSellToken)?; + let buy_token_index = tokens + .iter() + .position(|t| *t == order.data.buy_token) + .ok_or(BuildError::MissingBuyToken)?; + + // Compute before clearing_prices is moved into EncodedSettlement below. + let fund_amount = builder + .fund_settlement_contract + .then(|| match order.data.kind { + OrderKind::Sell => clearing_prices[sell_token_index] + .saturating_mul(executed_amount) + .checked_div(clearing_prices[buy_token_index]) + .unwrap_or(U256::MAX), + OrderKind::Buy => executed_amount, + }); + + let trade = encode_trade( + &order.data, + &order.signature, + order.owner, + sell_token_index, + buy_token_index, + executed_amount, + ); + + let order_pre = &order.pre_interactions; + let order_post = &order.post_interactions; + + let mut settlement = EncodedSettlement { + tokens, + clearing_prices, + trades: vec![trade], + interactions: Interactions { + // order's pre-hooks run before any additional pre-interactions + pre: encode_interactions(order_pre.iter().chain(&builder.pre_interactions)), + main: encode_interactions(&builder.main_interactions), + // additional post-interactions run before the order's post-hooks + post: encode_interactions(builder.post_interactions.iter().chain(order_post)), + }, + }; + + customize(&mut settlement); + + let settle_calldata = { + let mut bytes = settlement.into_settle_call().to_vec(); + if let Some(id) = builder.auction_id { + bytes.extend_from_slice(&id.to_be_bytes()); + } + bytes.into() + }; + + let (to, input) = match builder.wrapper { + Some(WrapperConfig::Custom(wrappers)) if !wrappers.is_empty() => { + encode_wrapper_settlement(&wrappers, settle_calldata).expect("wrappers is non-empty") + } + Some(WrapperConfig::Flashloan(loans)) => { + let calldata = contracts::FlashLoanRouter::FlashLoanRouter::flashLoanAndSettleCall { + loans: loans + .into_iter() + .map(|l| contracts::FlashLoanRouter::LoanRequest::Data { + amount: l.amount, + borrower: l.borrower, + lender: l.lender, + token: l.token, + }) + .collect(), + settlement: settle_calldata, + } + .abi_encode() + .into(); + (builder.simulator.0.flash_loan_router, calldata) + } + _ => (*builder.simulator.0.settlement.address(), settle_calldata), + }; + + let mut state_overrides = builder.state_overrides; + let from = match builder.solver { + Some(Solver::Real(addr)) => addr, + Some(Solver::Fake(opt)) => { + let addr = opt.unwrap_or_else(Address::random); + state_overrides.insert(addr, EthBalanceOverride(U256::MAX / U256::from(2)).into()); + state_overrides.insert( + builder.simulator.0.authenticator, + SolverAllowlisting(addr).into(), + ); + addr + } + None => return Err(BuildError::NoSolver), + }; + if let Some(amount) = fund_amount { + let (address, state_override) = builder + .simulator + .0 + .balance_overrides + .state_override(BalanceOverrideRequest { + token: order.data.buy_token, + holder: *builder.simulator.0.settlement.address(), + amount, + }) + .await + .ok_or(BuildError::FailedToOverrideBalances)?; + state_overrides.insert(address, state_override); + } + + Ok(EthCallInputs { + request: TransactionRequest { + from: Some(from), + to: Some(to.into()), + input: input.into(), + ..Default::default() + }, + state_overrides, + provider: builder.simulator.0.provider.clone(), + block: builder.block, + }) +} + +async fn executed_amount(builder: &SimulationBuilder, order: &Order) -> Result { + let full = match order.data.kind { + OrderKind::Sell => order.data.sell_amount, + OrderKind::Buy => order.data.buy_amount, + }; + + Ok(match order.executed_amount { + ExecutionAmount::Full => full, + ExecutionAmount::Explicit(amount) => amount, + ExecutionAmount::Remaining => { + let uid = order + .data + .uid(&builder.simulator.0.domain_separator, order.owner); + let filled_amount = builder + .simulator + .0 + .settlement + .filledAmount(Bytes::from(uid.0)) + .block(builder.block) + .call() + .await + .map_err(|err| BuildError::FilledAmountQuery(err.into()))?; + full.saturating_sub(filled_amount) + } + }) +} From c1a0fe2e24edf6747c54d4a8115926af9dc6cf43 Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Mon, 27 Apr 2026 13:38:15 +0000 Subject: [PATCH 14/44] move code around --- crates/simulator/src/simulation_builder.rs | 340 +++++++++++---------- 1 file changed, 171 insertions(+), 169 deletions(-) diff --git a/crates/simulator/src/simulation_builder.rs b/crates/simulator/src/simulation_builder.rs index 84a0b81d91..b5b941ac5f 100644 --- a/crates/simulator/src/simulation_builder.rs +++ b/crates/simulator/src/simulation_builder.rs @@ -18,130 +18,56 @@ use { std::sync::Arc, }; -/// How much of an order should be filled during simulation. -pub enum ExecutionAmount { - /// Fill the full order amount (sell_amount for sell orders, buy_amount for - /// buy orders), ignoring any on-chain filled state. - Full, - /// Fill whatever is still remaining on-chain (queries the settlement - /// contract for the already-filled amount and subtracts it). Falls back to - /// the full amount if the query fails. - Remaining, - /// Use an explicit fill amount. - Explicit(U256), -} +/// Holds the settlement contract and its authenticator address, and acts as a +/// factory for [`SimulationBuilder`] instances that are pre-configured with +/// these values. +#[derive(Clone)] +pub struct SettlementSimulator(pub(crate) Arc); -/// A simulator-specific order that bundles the data needed to encode a trade. -/// -/// Construct with [`Order::new`] and add optional fields via the builder -/// methods. Defaults to an EIP-1271 signature (pairs with [`FakeUser`] for -/// simulations that need to bypass signature verification). -pub struct Order { - pub(crate) data: OrderData, - pub(crate) owner: Address, - pub(crate) signature: Signature, - pub(crate) pre_interactions: Vec, - pub(crate) post_interactions: Vec, - pub(crate) executed_amount: ExecutionAmount, +pub(crate) struct Inner { + pub(crate) settlement: contracts::GPv2Settlement::Instance, + pub(crate) authenticator: Address, + pub(crate) flash_loan_router: Address, + pub(crate) balance_overrides: Arc, + pub(crate) provider: DynProvider, + pub(crate) domain_separator: DomainSeparator, } -impl Order { - pub fn new(data: OrderData) -> Self { - Self { - data, - owner: Address::ZERO, - signature: Signature::default_with(SigningScheme::Eip1271), +impl SettlementSimulator { + pub async fn new( + settlement: contracts::GPv2Settlement::Instance, + flash_loan_router: Address, + balance_overrides: Arc, + ) -> Result { + let authenticator = Address(settlement.authenticator().call().await?.0); + let domain_separator = DomainSeparator(settlement.domainSeparator().call().await?.0); + let provider = settlement.provider().clone(); + Ok(Self(Arc::new(Inner { + settlement, + authenticator, + flash_loan_router, + balance_overrides, + provider, + domain_separator, + }))) + } + + pub fn new_simulation_builder(&self) -> SimulationBuilder { + SimulationBuilder { + simulator: self.clone(), + order: None, pre_interactions: vec![], + main_interactions: vec![], post_interactions: vec![], - executed_amount: ExecutionAmount::Remaining, + wrapper: None, + prices: None, + solver: None, + auction_id: None, + state_overrides: StateOverride::default(), + fund_settlement_contract: false, + block: BlockId::latest(), } } - - pub fn with_signature(mut self, owner: Address, signature: Signature) -> Self { - self.owner = owner; - self.signature = signature; - self - } - - pub fn with_pre_interactions(mut self, interactions: Vec) -> Self { - self.pre_interactions = interactions; - self - } - - pub fn with_post_interactions(mut self, interactions: Vec) -> Self { - self.post_interactions = interactions; - self - } - - pub fn with_executed_amount(mut self, amount: ExecutionAmount) -> Self { - self.executed_amount = amount; - self - } -} - -pub struct FlashloanRequest { - pub amount: U256, - pub borrower: Address, - pub lender: Address, - pub token: Address, -} - -pub enum Solver { - /// Simulation assumes this is an actual solver so no state overrides will - /// be applied to allow list it explicitly. - /// If you need a very specific solver setup for your simulation consider - /// using this and explicitly add the necessary state overrides yourself - /// with `Simulation::build_with_modifications()`. - Real(Address), - /// A fake solver for simulation. Uses the provided address or generates a - /// random one. The simulation builder will automatically set the required - /// state overrides to give it enough ETH and allow list it as a solver. - Fake(Option
), -} - -/// Configuration for wrapping the settlement in a flashloan or custom wrapper -/// contract chain. -pub enum WrapperConfig { - Flashloan(Vec), - Custom(Vec), -} - -/// How clearing prices are determined for the encoded settlement. -pub enum Prices { - /// Derive clearing prices directly from the order's limit price. - /// - /// Sets `price[sell_token] = buy_amount` and `price[buy_token] = - /// sell_amount`, exactly satisfying the order's limit with no surplus. - /// This should NOT be used when encoding solutions you actually want - /// to submit. - Limit, - // TODO: check how this can be made nicer. - /// Explicit token list and matching clearing prices. - Explicit { - tokens: Vec
, - clearing_prices: Vec, - }, -} - -/// The output of [`SimulationBuilder::build`]: a transaction request and state -/// overrides ready to be passed to an alloy provider for simulation. -pub struct EthCallInputs { - pub request: TransactionRequest, - pub state_overrides: StateOverride, - pub provider: DynProvider, - pub block: BlockId, -} - -impl EthCallInputs { - /// Prepares an `eth_call` with the transaction request, state overrides, - /// and block already applied. The call is not sent — callers can chain - /// additional builder methods before awaiting. - pub fn simulate(self) -> EthCall { - self.provider - .call(self.request) - .overrides(self.state_overrides) - .block(self.block) - } } /// Assembles a GPv2 settlement call for simulation purposes. @@ -163,6 +89,8 @@ pub struct SimulationBuilder { } impl SimulationBuilder { + // TODO: support multiple orders to support use case of encoding solutions + // in the driver and the trade verification (requires JIT orders) pub fn add_order(mut self, order: Order) -> Self { self.order = Some(order); self @@ -244,6 +172,132 @@ impl SimulationBuilder { } } +pub enum Solver { + /// Simulation assumes this is an actual solver so no state overrides will + /// be applied to allow list it explicitly. + /// If you need a very specific solver setup for your simulation consider + /// using this and explicitly add the necessary state overrides yourself + /// with `Simulation::build_with_modifications()`. + Real(Address), + /// A fake solver for simulation. Uses the provided address or generates a + /// random one. The simulation builder will automatically set the required + /// state overrides to give it enough ETH and allow list it as a solver. + Fake(Option
), +} + +/// How clearing prices are determined for the encoded settlement. +pub enum Prices { + /// Derive clearing prices directly from the order's limit price. + /// + /// Sets `price[sell_token] = buy_amount` and `price[buy_token] = + /// sell_amount`, exactly satisfying the order's limit with no surplus. + /// This should NOT be used when encoding solutions you actually want + /// to submit. + Limit, + // TODO: check how this can be made nicer. + /// Explicit token list and matching clearing prices. + Explicit { + tokens: Vec
, + clearing_prices: Vec, + }, +} + +/// How much of an order should be filled during simulation. +pub enum ExecutionAmount { + /// Fill the full order amount (sell_amount for sell orders, buy_amount for + /// buy orders), ignoring any on-chain filled state. + Full, + /// Fill whatever is still remaining on-chain (queries the settlement + /// contract for the already-filled amount and subtracts it). Falls back to + /// the full amount if the query fails. + Remaining, + /// Use an explicit fill amount. + Explicit(U256), +} + +/// A simulator-specific order that bundles the data needed to encode a trade. +/// +/// Construct with [`Order::new`] and add optional fields via the builder +/// methods. Defaults to an EIP-1271 signature (pairs with [`FakeUser`] for +/// simulations that need to bypass signature verification). +pub struct Order { + pub(crate) data: OrderData, + pub(crate) owner: Address, + pub(crate) signature: Signature, + pub(crate) pre_interactions: Vec, + pub(crate) post_interactions: Vec, + pub(crate) executed_amount: ExecutionAmount, +} + +/// Configuration for wrapping the settlement in a flashloan or custom wrapper +/// contract chain. +pub enum WrapperConfig { + Flashloan(Vec), + Custom(Vec), +} + +pub struct FlashloanRequest { + pub amount: U256, + pub borrower: Address, + pub lender: Address, + pub token: Address, +} + +impl Order { + pub fn new(data: OrderData) -> Self { + Self { + data, + owner: Address::ZERO, + signature: Signature::default_with(SigningScheme::Eip1271), + pre_interactions: vec![], + post_interactions: vec![], + executed_amount: ExecutionAmount::Remaining, + } + } + + pub fn with_signature(mut self, owner: Address, signature: Signature) -> Self { + self.owner = owner; + self.signature = signature; + self + } + + pub fn with_pre_interactions(mut self, interactions: Vec) -> Self { + self.pre_interactions = interactions; + self + } + + pub fn with_post_interactions(mut self, interactions: Vec) -> Self { + self.post_interactions = interactions; + self + } + + pub fn with_executed_amount(mut self, amount: ExecutionAmount) -> Self { + self.executed_amount = amount; + self + } +} + +/// The output of [`SimulationBuilder::build`]: a transaction request and state +/// overrides ready to be passed to an alloy provider for simulation. +pub struct EthCallInputs { + pub request: TransactionRequest, + pub state_overrides: StateOverride, + pub provider: DynProvider, + pub block: BlockId, +} + +impl EthCallInputs { + /// Prepares an `eth_call` with the transaction request, state overrides, + /// and block already applied. The call is not sent — callers can chain + /// additional builder methods before awaiting. + pub fn simulate(self) -> EthCall { + self.provider + .call(self.request) + .overrides(self.state_overrides) + .block(self.block) + } +} + #[derive(Debug, thiserror::Error)] pub enum BuildError { #[error("no order was added")] @@ -261,55 +315,3 @@ pub enum BuildError { #[error("failed to query filled amount from settlement contract: {0}")] FilledAmountQuery(#[source] anyhow::Error), } - -pub(crate) struct Inner { - pub(crate) settlement: contracts::GPv2Settlement::Instance, - pub(crate) authenticator: Address, - pub(crate) flash_loan_router: Address, - pub(crate) balance_overrides: Arc, - pub(crate) provider: DynProvider, - pub(crate) domain_separator: DomainSeparator, -} - -/// Holds the settlement contract and its authenticator address, and acts as a -/// factory for [`SimulationBuilder`] instances that are pre-configured with -/// these values. -#[derive(Clone)] -pub struct SettlementSimulator(pub(crate) Arc); - -impl SettlementSimulator { - pub async fn new( - settlement: contracts::GPv2Settlement::Instance, - flash_loan_router: Address, - balance_overrides: Arc, - ) -> Result { - let authenticator = Address(settlement.authenticator().call().await?.0); - let domain_separator = DomainSeparator(settlement.domainSeparator().call().await?.0); - let provider = settlement.provider().clone(); - Ok(Self(Arc::new(Inner { - settlement, - authenticator, - flash_loan_router, - balance_overrides, - provider, - domain_separator, - }))) - } - - pub fn new_simulation_builder(&self) -> SimulationBuilder { - SimulationBuilder { - simulator: self.clone(), - order: None, - pre_interactions: vec![], - main_interactions: vec![], - post_interactions: vec![], - wrapper: None, - prices: None, - solver: None, - auction_id: None, - state_overrides: StateOverride::default(), - fund_settlement_contract: false, - block: BlockId::latest(), - } - } -} From 36d46aa75067a8b6f9bd3c7c312f0e865929c739 Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Mon, 27 Apr 2026 14:41:27 +0000 Subject: [PATCH 15/44] store chain_id and convert to tenderly request --- crates/simulator/src/simulation_builder.rs | 10 ++++++++-- crates/simulator/src/simulation_encoding.rs | 2 +- crates/simulator/src/tenderly/mod.rs | 16 +++++++++++++++- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/crates/simulator/src/simulation_builder.rs b/crates/simulator/src/simulation_builder.rs index b5b941ac5f..2502426746 100644 --- a/crates/simulator/src/simulation_builder.rs +++ b/crates/simulator/src/simulation_builder.rs @@ -31,6 +31,7 @@ pub(crate) struct Inner { pub(crate) balance_overrides: Arc, pub(crate) provider: DynProvider, pub(crate) domain_separator: DomainSeparator, + pub(crate) chain_id: u64, } impl SettlementSimulator { @@ -42,6 +43,7 @@ impl SettlementSimulator { let authenticator = Address(settlement.authenticator().call().await?.0); let domain_separator = DomainSeparator(settlement.domainSeparator().call().await?.0); let provider = settlement.provider().clone(); + let chain_id = provider.get_chain_id().await?; Ok(Self(Arc::new(Inner { settlement, authenticator, @@ -49,6 +51,7 @@ impl SettlementSimulator { balance_overrides, provider, domain_separator, + chain_id, }))) } @@ -282,7 +285,7 @@ impl Order { pub struct EthCallInputs { pub request: TransactionRequest, pub state_overrides: StateOverride, - pub provider: DynProvider, + pub simulator: SettlementSimulator, pub block: BlockId, } @@ -291,7 +294,10 @@ impl EthCallInputs { /// and block already applied. The call is not sent — callers can chain /// additional builder methods before awaiting. pub fn simulate(self) -> EthCall { - self.provider + self.simulator + .0 + .provider + .clone() .call(self.request) .overrides(self.state_overrides) .block(self.block) diff --git a/crates/simulator/src/simulation_encoding.rs b/crates/simulator/src/simulation_encoding.rs index d3d8c2f916..e176f603d4 100644 --- a/crates/simulator/src/simulation_encoding.rs +++ b/crates/simulator/src/simulation_encoding.rs @@ -166,7 +166,7 @@ pub(crate) async fn encode( ..Default::default() }, state_overrides, - provider: builder.simulator.0.provider.clone(), + simulator: builder.simulator, block: builder.block, }) } diff --git a/crates/simulator/src/tenderly/mod.rs b/crates/simulator/src/tenderly/mod.rs index fe6c55d66d..9c544479cd 100644 --- a/crates/simulator/src/tenderly/mod.rs +++ b/crates/simulator/src/tenderly/mod.rs @@ -1,5 +1,6 @@ use { - crate::ethereum::Ethereum, + crate::{ethereum::Ethereum, simulation_builder::EthCallInputs}, + alloy_eips::{BlockId, BlockNumberOrTag}, alloy_primitives::TxKind, alloy_rpc_types::{TransactionRequest, state::StateOverride}, anyhow::{Result, anyhow}, @@ -245,6 +246,19 @@ pub fn prepare_request( }) } +pub fn request_from_eth_call(inputs: &EthCallInputs) -> Result { + let block = match inputs.block { + BlockId::Number(BlockNumberOrTag::Number(n)) => Some(BlockNo(n)), + _ => None, + }; + prepare_request( + inputs.simulator.0.chain_id.to_string(), + &inputs.request, + inputs.state_overrides.clone(), + block, + ) +} + pub fn log_simulation_request( simulation_endpoint: &Url, dashboard: &Url, From 9259b06e5c6c781bb25ec9e2888622be1b43d05a Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Mon, 27 Apr 2026 15:20:50 +0000 Subject: [PATCH 16/44] current block watcher in simulator --- crates/simulator/src/simulation_builder.rs | 23 +++++++++++++++------ crates/simulator/src/simulation_encoding.rs | 18 ++++++++++++---- crates/simulator/src/tenderly/mod.rs | 7 +------ 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/crates/simulator/src/simulation_builder.rs b/crates/simulator/src/simulation_builder.rs index 2502426746..5cdfa209aa 100644 --- a/crates/simulator/src/simulation_builder.rs +++ b/crates/simulator/src/simulation_builder.rs @@ -1,6 +1,5 @@ use { crate::encoding::{EncodedSettlement, Interaction, WrapperCall}, - alloy_eips::BlockId, alloy_network::Ethereum, alloy_primitives::{Address, Bytes, U256}, alloy_provider::{DynProvider, EthCall, Provider}, @@ -10,6 +9,7 @@ use { }, anyhow::Result, balance_overrides::BalanceOverriding, + ethrpc::block_stream::CurrentBlockWatcher, model::{ DomainSeparator, order::OrderData, @@ -32,6 +32,7 @@ pub(crate) struct Inner { pub(crate) provider: DynProvider, pub(crate) domain_separator: DomainSeparator, pub(crate) chain_id: u64, + pub(crate) current_block: CurrentBlockWatcher, } impl SettlementSimulator { @@ -39,6 +40,7 @@ impl SettlementSimulator { settlement: contracts::GPv2Settlement::Instance, flash_loan_router: Address, balance_overrides: Arc, + current_block: CurrentBlockWatcher, ) -> Result { let authenticator = Address(settlement.authenticator().call().await?.0); let domain_separator = DomainSeparator(settlement.domainSeparator().call().await?.0); @@ -52,6 +54,7 @@ impl SettlementSimulator { provider, domain_separator, chain_id, + current_block, }))) } @@ -68,11 +71,19 @@ impl SettlementSimulator { auction_id: None, state_overrides: StateOverride::default(), fund_settlement_contract: false, - block: BlockId::latest(), + block: Block::Latest, } } } +/// Which block to simulate against. +pub enum Block { + /// Use the current head block from the block stream, pinning the + /// simulation to a concrete number at build time. + Latest, + Number(u64), +} + /// Assembles a GPv2 settlement call for simulation purposes. /// /// Call [`SimulationBuilder::build`] when done to produce a [`SettlementCall`]. @@ -88,7 +99,7 @@ pub struct SimulationBuilder { pub(crate) state_overrides: StateOverride, pub(crate) simulator: SettlementSimulator, pub(crate) fund_settlement_contract: bool, - pub(crate) block: BlockId, + pub(crate) block: Block, } impl SimulationBuilder { @@ -144,7 +155,7 @@ impl SimulationBuilder { self } - pub fn at_block(mut self, block: BlockId) -> Self { + pub fn at_block(mut self, block: Block) -> Self { self.block = block; self } @@ -286,7 +297,7 @@ pub struct EthCallInputs { pub request: TransactionRequest, pub state_overrides: StateOverride, pub simulator: SettlementSimulator, - pub block: BlockId, + pub block: u64, } impl EthCallInputs { @@ -300,7 +311,7 @@ impl EthCallInputs { .clone() .call(self.request) .overrides(self.state_overrides) - .block(self.block) + .block(self.block.into()) } } diff --git a/crates/simulator/src/simulation_encoding.rs b/crates/simulator/src/simulation_encoding.rs index e176f603d4..1d0dc19082 100644 --- a/crates/simulator/src/simulation_encoding.rs +++ b/crates/simulator/src/simulation_encoding.rs @@ -8,6 +8,7 @@ use { encode_wrapper_settlement, }, simulation_builder::{ + Block, BuildError, EthCallInputs, ExecutionAmount, @@ -32,7 +33,12 @@ pub(crate) async fn encode( ) -> Result { let order = builder.order.as_ref().ok_or(BuildError::NoOrder)?; - let executed_amount = executed_amount(&builder, order).await?; + let block = match builder.block { + Block::Latest => builder.simulator.0.current_block.borrow().number, + Block::Number(n) => n, + }; + + let executed_amount = executed_amount(&builder, order, block).await?; let (tokens, clearing_prices) = match builder.prices { Some(Prices::Explicit { @@ -166,12 +172,16 @@ pub(crate) async fn encode( ..Default::default() }, state_overrides, + block, simulator: builder.simulator, - block: builder.block, }) } -async fn executed_amount(builder: &SimulationBuilder, order: &Order) -> Result { +async fn executed_amount( + builder: &SimulationBuilder, + order: &Order, + block: u64, +) -> Result { let full = match order.data.kind { OrderKind::Sell => order.data.sell_amount, OrderKind::Buy => order.data.buy_amount, @@ -189,7 +199,7 @@ async fn executed_amount(builder: &SimulationBuilder, order: &Order) -> Result Result { - let block = match inputs.block { - BlockId::Number(BlockNumberOrTag::Number(n)) => Some(BlockNo(n)), - _ => None, - }; prepare_request( inputs.simulator.0.chain_id.to_string(), &inputs.request, inputs.state_overrides.clone(), - block, + Some(BlockNo(inputs.block)), ) } From fb58a25d77126e25b44b4f25f1739499adb049b4 Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Tue, 28 Apr 2026 07:41:54 +0000 Subject: [PATCH 17/44] more fixes --- crates/orderbook/src/orderbook.rs | 46 +++++--- crates/orderbook/src/run.rs | 27 ++++- crates/simulator/src/simulation_builder.rs | 112 ++++++++++++++++++-- crates/simulator/src/simulation_encoding.rs | 10 +- crates/simulator/src/tenderly/mod.rs | 11 +- 5 files changed, 170 insertions(+), 36 deletions(-) diff --git a/crates/orderbook/src/orderbook.rs b/crates/orderbook/src/orderbook.rs index 2058f8a91c..ba594d3202 100644 --- a/crates/orderbook/src/orderbook.rs +++ b/crates/orderbook/src/orderbook.rs @@ -42,6 +42,7 @@ use { is_order_outside_market_price, }, }, + simulator::simulation_builder::SettlementSimulator, std::{borrow::Cow, sync::Arc}, strum::Display, thiserror::Error, @@ -240,6 +241,7 @@ pub struct Orderbook { app_data: Arc, active_order_competition_threshold: u32, order_simulator: Option>, + order_simulator2: Option>, } impl Orderbook { @@ -253,6 +255,7 @@ impl Orderbook { app_data: Arc, active_order_competition_threshold: u32, order_simulator: Option>, + order_simulator2: Option>, ) -> Self { Metrics::initialize(); Self { @@ -264,6 +267,7 @@ impl Orderbook { app_data, active_order_competition_threshold, order_simulator, + order_simulator2, } } @@ -642,7 +646,7 @@ impl Orderbook { uid: &OrderUid, block_number: Option, ) -> Result, OrderSimulationError> { - let Some(order_simulator) = &self.order_simulator else { + let Some(order_simulator) = &self.order_simulator2 else { return Err(OrderSimulationError::NotEnabled); }; let Some(order) = self @@ -653,18 +657,35 @@ impl Orderbook { return Ok(None); }; - let (_, wrappers) = self - .parse_interactions_and_wrappers( - order.metadata.full_app_data.as_deref().unwrap_or_default(), - ) - .map_err(OrderSimulationError::Other)?; + // let app_data = self + // .parse_app_data(order.metadata.full_app_data.as_deref(). + // unwrap_or_default()) .map_err(OrderSimulationError::Other)?; + + let sim = order_simulator.new_simulation_builder().add_order( + simulator::simulation_builder::Order::new(order.data) + .with_signature(order.metadata.owner, order.signature) + // properly populate those values + .with_pre_interactions(vec![]) + .with_post_interactions(vec![]) + .with_executed_amount(simulator::simulation_builder::ExecutionAmount::Remaining), + ) + .at_block(block_number.map(simulator::simulation_builder::Block::Number).unwrap_or(simulator::simulation_builder::Block::Latest)) + .fund_settlement_contract() + .from_solver(simulator::simulation_builder::Solver::Fake(None)) + // TODO: add wrapper + .build() + .await + // TODO: proper error handling + .unwrap(); - let swap = order_simulator - .encode_order(&order, wrappers, block_number) - .await?; - Ok(Some( - order_simulator.simulate_swap(swap, block_number).await?, - )) + // TODO: error handling + let sim_res = sim.simulate_with_tenderly_report().await.unwrap(); + + Ok(Some(OrderSimulationResult { + tenderly_url: sim_res.tenderly_url, + tenderly_request: sim_res.tenderly_request, + error: sim_res.error, + })) } /// Simulates an arbitrary order without requiring it to exist in the @@ -840,6 +861,7 @@ mod tests { app_data, active_order_competition_threshold: Default::default(), order_simulator: None, + order_simulator2: None, }; // Different owner diff --git a/crates/orderbook/src/run.rs b/crates/orderbook/src/run.rs index 912680642c..6b5b8b6971 100644 --- a/crates/orderbook/src/run.rs +++ b/crates/orderbook/src/run.rs @@ -411,7 +411,7 @@ pub async fn run(config: Configuration) { ipfs, )); - let order_simulator = if let Some(config) = config.order_simulation { + let order_simulator = if let Some(config) = &config.order_simulation { let tenderly: Option> = config.tenderly.as_ref().map(|tenderly_config| { Box::new(simulator::tenderly::TenderlyApi::new( @@ -441,6 +441,30 @@ pub async fn run(config: Configuration) { None }; + let order_simulator2 = if let Some(config) = config.order_simulation { + let tenderly: Option> = + config.tenderly.as_ref().map(|tenderly_config| { + Arc::new(simulator::tenderly::TenderlyApi::new( + tenderly_config, + &http_factory, + chain.id().to_string(), + )) as _ + }); + Some(Arc::new( + simulator::simulation_builder::SettlementSimulator::new( + settlement_contract.clone(), + Default::default(), + balance_overrider.clone(), + current_block_stream.clone(), + tenderly, + ) + .await + .unwrap(), + )) + } else { + None + }; + let orderbook = Arc::new(Orderbook::new( domain_separator, *settlement_contract.address(), @@ -450,6 +474,7 @@ pub async fn run(config: Configuration) { app_data.clone(), config.active_order_competition_threshold, order_simulator, + order_simulator2, )); check_database_connection(orderbook.as_ref()).await; diff --git a/crates/simulator/src/simulation_builder.rs b/crates/simulator/src/simulation_builder.rs index 5cdfa209aa..929753825c 100644 --- a/crates/simulator/src/simulation_builder.rs +++ b/crates/simulator/src/simulation_builder.rs @@ -1,13 +1,16 @@ use { - crate::encoding::{EncodedSettlement, Interaction, WrapperCall}, - alloy_network::Ethereum, - alloy_primitives::{Address, Bytes, U256}, - alloy_provider::{DynProvider, EthCall, Provider}, + crate::{ + encoding::{EncodedSettlement, Interaction, WrapperCall}, + tenderly::dto::StateObject, + }, + alloy_primitives::{Address, Bytes, TxKind, U256}, + alloy_provider::{DynProvider, Provider}, alloy_rpc_types::{ TransactionRequest, state::{AccountOverride, StateOverride}, }, - anyhow::Result, + alloy_transport::RpcError, + anyhow::{Context, Result}, balance_overrides::BalanceOverriding, ethrpc::block_stream::CurrentBlockWatcher, model::{ @@ -33,6 +36,7 @@ pub(crate) struct Inner { pub(crate) domain_separator: DomainSeparator, pub(crate) chain_id: u64, pub(crate) current_block: CurrentBlockWatcher, + pub(crate) tenderly: Option>, } impl SettlementSimulator { @@ -41,6 +45,7 @@ impl SettlementSimulator { flash_loan_router: Address, balance_overrides: Arc, current_block: CurrentBlockWatcher, + tenderly: Option>, ) -> Result { let authenticator = Address(settlement.authenticator().call().await?.0); let domain_separator = DomainSeparator(settlement.domainSeparator().call().await?.0); @@ -55,6 +60,7 @@ impl SettlementSimulator { domain_separator, chain_id, current_block, + tenderly, }))) } @@ -300,11 +306,21 @@ pub struct EthCallInputs { pub block: u64, } +/// The result of Order simulation, contains the error (if any) +/// and full Tenderly API request that can be used to resimulate +/// and debug using Tenderly +#[derive(Clone, Debug)] +pub struct TenderlyReport { + /// Full request object that can be used directly with the Tenderly API + pub tenderly_request: crate::tenderly::dto::Request, + /// Shared Tenderly simulation URL for debugging in the dashboard + pub tenderly_url: Option, + /// Any error that might have been reported during order simulation + pub error: Option, +} + impl EthCallInputs { - /// Prepares an `eth_call` with the transaction request, state overrides, - /// and block already applied. The call is not sent — callers can chain - /// additional builder methods before awaiting. - pub fn simulate(self) -> EthCall { + pub async fn simulate(self) -> Result> { self.simulator .0 .provider @@ -312,7 +328,85 @@ impl EthCallInputs { .call(self.request) .overrides(self.state_overrides) .block(self.block.into()) + .await + } + + pub async fn simulate_with_tenderly_report(self) -> Result { + // TODO: error handling + let tenderly_request = self.to_tenderly_request().unwrap(); + let tenderly_url = match &self.simulator.0.tenderly { + Some(api) => Some( + api.simulate_and_share(tenderly_request.clone()) + .await + .context("tenderly failed")?, + ), + None => None, + }; + let simulation_result = self.simulate().await; + + Ok(TenderlyReport { + tenderly_request, + tenderly_url, + error: match simulation_result { + Ok(_) => None, + Err(err) => Some(err.to_string()), + }, + }) } + + /// Converts the simulation into a request that can be simulated with + /// tenderly. + pub fn to_tenderly_request(&self) -> Result { + Ok(crate::tenderly::dto::Request { + block_number: Some(self.block), + // By default, tenderly simulates on the top of the specified block, whereas regular + // nodes simulate at the end of the specified block. This is to make + // simulation results match in case critical state changed within the block. + transaction_index: Some(-1), + network_id: self.simulator.0.chain_id.to_string(), + from: self.request.from.unwrap_or_default(), + // TODO: error handling + to: match &self.request.to.ok_or(ConversionError::MissingTo)? { + TxKind::Create => Default::default(), + TxKind::Call(to) => *to, + }, + input: self + .request + .input + .input + .as_ref() + .map(|bytes| bytes.to_vec()) + .unwrap_or_default(), + gas: self.request.gas, + gas_price: None, // use tenderly default for now + value: self.request.value, + simulation_type: Some(crate::tenderly::dto::SimulationType::Full), + state_objects: Some( + self.state_overrides + .iter() + .map(|(key, value)| { + Ok(( + *key, + StateObject::try_from(value.clone()) + .map_err(|_| ConversionError::StateOverrides)?, + )) + }) + .collect::>()?, + ), + access_list: self.request.access_list.as_ref().map(Into::into), + save: Some(true), + save_if_fails: Some(true), + ..Default::default() + }) + } +} + +#[derive(Debug, thiserror::Error)] +pub enum ConversionError { + #[error("simulation does not have a target")] + MissingTo, + #[error("could not convert state overrides")] + StateOverrides, } #[derive(Debug, thiserror::Error)] diff --git a/crates/simulator/src/simulation_encoding.rs b/crates/simulator/src/simulation_encoding.rs index 1d0dc19082..4d0de3f49d 100644 --- a/crates/simulator/src/simulation_encoding.rs +++ b/crates/simulator/src/simulation_encoding.rs @@ -66,15 +66,17 @@ pub(crate) async fn encode( .ok_or(BuildError::MissingBuyToken)?; // Compute before clearing_prices is moved into EncodedSettlement below. - let fund_amount = builder - .fund_settlement_contract - .then(|| match order.data.kind { + let fund_amount = builder.fund_settlement_contract.then(|| { + let base_amount = match order.data.kind { OrderKind::Sell => clearing_prices[sell_token_index] .saturating_mul(executed_amount) .checked_div(clearing_prices[buy_token_index]) .unwrap_or(U256::MAX), OrderKind::Buy => executed_amount, - }); + }; + // give 1 wei extra to avoid issues with rounding divisions + base_amount.saturating_add(U256::ONE) + }); let trade = encode_trade( &order.data, diff --git a/crates/simulator/src/tenderly/mod.rs b/crates/simulator/src/tenderly/mod.rs index 3abf8c3a7e..fe6c55d66d 100644 --- a/crates/simulator/src/tenderly/mod.rs +++ b/crates/simulator/src/tenderly/mod.rs @@ -1,5 +1,5 @@ use { - crate::{ethereum::Ethereum, simulation_builder::EthCallInputs}, + crate::ethereum::Ethereum, alloy_primitives::TxKind, alloy_rpc_types::{TransactionRequest, state::StateOverride}, anyhow::{Result, anyhow}, @@ -245,15 +245,6 @@ pub fn prepare_request( }) } -pub fn request_from_eth_call(inputs: &EthCallInputs) -> Result { - prepare_request( - inputs.simulator.0.chain_id.to_string(), - &inputs.request, - inputs.state_overrides.clone(), - Some(BlockNo(inputs.block)), - ) -} - pub fn log_simulation_request( simulation_endpoint: &Url, dashboard: &Url, From 8a0cd7826ba3419d9ea6bd3bdb78908c2c358523 Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Tue, 28 Apr 2026 09:29:10 +0000 Subject: [PATCH 18/44] fmt --- crates/orderbook/src/orderbook.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/crates/orderbook/src/orderbook.rs b/crates/orderbook/src/orderbook.rs index ba594d3202..0cafad84d9 100644 --- a/crates/orderbook/src/orderbook.rs +++ b/crates/orderbook/src/orderbook.rs @@ -662,14 +662,17 @@ impl Orderbook { // unwrap_or_default()) .map_err(OrderSimulationError::Other)?; let sim = order_simulator.new_simulation_builder().add_order( - simulator::simulation_builder::Order::new(order.data) - .with_signature(order.metadata.owner, order.signature) - // properly populate those values - .with_pre_interactions(vec![]) - .with_post_interactions(vec![]) - .with_executed_amount(simulator::simulation_builder::ExecutionAmount::Remaining), - ) - .at_block(block_number.map(simulator::simulation_builder::Block::Number).unwrap_or(simulator::simulation_builder::Block::Latest)) + simulator::simulation_builder::Order::new(order.data) + .with_signature(order.metadata.owner, order.signature) + // properly populate those values + .with_pre_interactions(vec![]) + .with_post_interactions(vec![]) + .with_executed_amount(simulator::simulation_builder::ExecutionAmount::Remaining), + ) + .at_block( + block_number.map(simulator::simulation_builder::Block::Number) + .unwrap_or(simulator::simulation_builder::Block::Latest) + ) .fund_settlement_contract() .from_solver(simulator::simulation_builder::Solver::Fake(None)) // TODO: add wrapper From dcdd98654984396320acaa33b2379ee1c11bc4c7 Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Tue, 28 Apr 2026 11:50:22 +0000 Subject: [PATCH 19/44] more fixes --- crates/orderbook/src/orderbook.rs | 80 +++++++++++++++------ crates/simulator/src/encoding.rs | 9 ++- crates/simulator/src/simulation_builder.rs | 32 +++++---- crates/simulator/src/simulation_encoding.rs | 4 +- 4 files changed, 82 insertions(+), 43 deletions(-) diff --git a/crates/orderbook/src/orderbook.rs b/crates/orderbook/src/orderbook.rs index 0cafad84d9..a5e1fdb0fa 100644 --- a/crates/orderbook/src/orderbook.rs +++ b/crates/orderbook/src/orderbook.rs @@ -42,7 +42,7 @@ use { is_order_outside_market_price, }, }, - simulator::simulation_builder::SettlementSimulator, + simulator::simulation_builder::{self, SettlementSimulator}, std::{borrow::Cow, sync::Arc}, strum::Display, thiserror::Error, @@ -657,37 +657,71 @@ impl Orderbook { return Ok(None); }; - // let app_data = self - // .parse_app_data(order.metadata.full_app_data.as_deref(). - // unwrap_or_default()) .map_err(OrderSimulationError::Other)?; + let full_app_data = order + .metadata + .full_app_data + .as_ref() + .ok_or_else(|| OrderSimulationError::Other(anyhow!("can't find appdata")))?; - let sim = order_simulator.new_simulation_builder().add_order( - simulator::simulation_builder::Order::new(order.data) + let parsed_app_data = self + .order_validator + .validate_app_data( + &OrderCreationAppData::Full { + full: full_app_data.clone(), + }, + &None, + ) + .map_err(|err| OrderSimulationError::Other(anyhow::anyhow!("{:?}", err)))? + .inner + .protocol; + + let wrapper = match (parsed_app_data.flashloan, parsed_app_data.wrappers) { + (None, wrappers) if wrappers.is_empty() => simulation_builder::WrapperConfig::NoWrapper, + (None, wrappers) if !wrappers.is_empty() => { + // TODO: convert wrappers + simulation_builder::WrapperConfig::Custom(vec![]) + } + (Some(flashloan), wrappers) if wrappers.is_empty() => { + // TODO: convert flashloan + simulation_builder::WrapperConfig::Flashloan(vec![]) + } + _ => { + return Err(OrderSimulationError::Other(anyhow!( + "can't configure custom wrappers and flashloans at the same time" + ))); + } + }; + + let sim = order_simulator + .new_simulation_builder() + .add_order( + simulation_builder::Order::new(order.data) .with_signature(order.metadata.owner, order.signature) - // properly populate those values - .with_pre_interactions(vec![]) - .with_post_interactions(vec![]) - .with_executed_amount(simulator::simulation_builder::ExecutionAmount::Remaining), + .with_pre_interactions(order.interactions.pre) + .with_post_interactions(order.interactions.post) + .with_executed_amount(simulation_builder::ExecutionAmount::Remaining), ) .at_block( - block_number.map(simulator::simulation_builder::Block::Number) - .unwrap_or(simulator::simulation_builder::Block::Latest) + block_number + .map(simulation_builder::Block::Number) + .unwrap_or(simulation_builder::Block::Latest), ) - .fund_settlement_contract() - .from_solver(simulator::simulation_builder::Solver::Fake(None)) - // TODO: add wrapper + .fund_settlement_contract_with_buy_tokens() + .from_solver(simulation_builder::Solver::Fake(None)) + .with_wrapper(wrapper) .build() .await - // TODO: proper error handling - .unwrap(); + .context("failed to finalize simulation")?; - // TODO: error handling - let sim_res = sim.simulate_with_tenderly_report().await.unwrap(); + let simulation_result = sim + .simulate_with_tenderly_report() + .await + .context("failed to execute simulation")?; Ok(Some(OrderSimulationResult { - tenderly_url: sim_res.tenderly_url, - tenderly_request: sim_res.tenderly_request, - error: sim_res.error, + tenderly_url: simulation_result.tenderly_url, + tenderly_request: simulation_result.tenderly_request, + error: simulation_result.error, })) } @@ -764,7 +798,7 @@ pub enum OrderSimulationError { #[error("order simulation is not enabled")] NotEnabled, #[error("malformed input")] - MalformedInput(anyhow::Error), + MalformedInput(#[from] anyhow::Error), #[error("simulation could not be created for order")] Other(anyhow::Error), } diff --git a/crates/simulator/src/encoding.rs b/crates/simulator/src/encoding.rs index 8c6d188c24..61b3db0105 100644 --- a/crates/simulator/src/encoding.rs +++ b/crates/simulator/src/encoding.rs @@ -241,9 +241,12 @@ impl From for Interaction { } } -pub fn encode_interactions<'a>( - interactions: impl IntoIterator, -) -> Vec { +pub fn encode_interactions<'a, I>( + interactions: impl IntoIterator, +) -> Vec +where + I: InteractionEncoding + 'a, +{ interactions.into_iter().map(|i| i.encode()).collect() } diff --git a/crates/simulator/src/simulation_builder.rs b/crates/simulator/src/simulation_builder.rs index 929753825c..8799783121 100644 --- a/crates/simulator/src/simulation_builder.rs +++ b/crates/simulator/src/simulation_builder.rs @@ -1,6 +1,6 @@ use { crate::{ - encoding::{EncodedSettlement, Interaction, WrapperCall}, + encoding::{EncodedSettlement, WrapperCall}, tenderly::dto::StateObject, }, alloy_primitives::{Address, Bytes, TxKind, U256}, @@ -15,6 +15,7 @@ use { ethrpc::block_stream::CurrentBlockWatcher, model::{ DomainSeparator, + interaction::InteractionData, order::OrderData, signature::{Signature, SigningScheme}, }, @@ -71,7 +72,7 @@ impl SettlementSimulator { pre_interactions: vec![], main_interactions: vec![], post_interactions: vec![], - wrapper: None, + wrapper: WrapperConfig::NoWrapper, prices: None, solver: None, auction_id: None, @@ -95,10 +96,10 @@ pub enum Block { /// Call [`SimulationBuilder::build`] when done to produce a [`SettlementCall`]. pub struct SimulationBuilder { pub(crate) order: Option, - pub(crate) pre_interactions: Vec, - pub(crate) main_interactions: Vec, - pub(crate) post_interactions: Vec, - pub(crate) wrapper: Option, + pub(crate) pre_interactions: Vec, + pub(crate) main_interactions: Vec, + pub(crate) post_interactions: Vec, + pub(crate) wrapper: WrapperConfig, pub(crate) prices: Option, pub(crate) solver: Option, pub(crate) auction_id: Option, @@ -116,23 +117,23 @@ impl SimulationBuilder { self } - pub fn with_pre_interactions(mut self, interactions: Vec) -> Self { + pub fn with_pre_interactions(mut self, interactions: Vec) -> Self { self.pre_interactions = interactions; self } - pub fn with_main_interactions(mut self, interactions: Vec) -> Self { + pub fn with_main_interactions(mut self, interactions: Vec) -> Self { self.main_interactions = interactions; self } - pub fn with_post_interactions(mut self, interactions: Vec) -> Self { + pub fn with_post_interactions(mut self, interactions: Vec) -> Self { self.post_interactions = interactions; self } pub fn with_wrapper(mut self, wrapper: WrapperConfig) -> Self { - self.wrapper = Some(wrapper); + self.wrapper = wrapper; self } @@ -169,7 +170,7 @@ impl SimulationBuilder { /// Override the settlement contract's buy token balance so it can pay out /// the order without any external liquidity. The required amount is derived /// from the order's executed amount and clearing prices at `build()` time. - pub fn fund_settlement_contract(mut self) -> Self { + pub fn fund_settlement_contract_with_buy_tokens(mut self) -> Self { self.fund_settlement_contract = true; self } @@ -244,8 +245,8 @@ pub struct Order { pub(crate) data: OrderData, pub(crate) owner: Address, pub(crate) signature: Signature, - pub(crate) pre_interactions: Vec, - pub(crate) post_interactions: Vec, + pub(crate) pre_interactions: Vec, + pub(crate) post_interactions: Vec, pub(crate) executed_amount: ExecutionAmount, } @@ -254,6 +255,7 @@ pub struct Order { pub enum WrapperConfig { Flashloan(Vec), Custom(Vec), + NoWrapper, } pub struct FlashloanRequest { @@ -281,12 +283,12 @@ impl Order { self } - pub fn with_pre_interactions(mut self, interactions: Vec) -> Self { + pub fn with_pre_interactions(mut self, interactions: Vec) -> Self { self.pre_interactions = interactions; self } - pub fn with_post_interactions(mut self, interactions: Vec) -> Self { + pub fn with_post_interactions(mut self, interactions: Vec) -> Self { self.post_interactions = interactions; self } diff --git a/crates/simulator/src/simulation_encoding.rs b/crates/simulator/src/simulation_encoding.rs index 4d0de3f49d..e2723d25c2 100644 --- a/crates/simulator/src/simulation_encoding.rs +++ b/crates/simulator/src/simulation_encoding.rs @@ -114,10 +114,10 @@ pub(crate) async fn encode( }; let (to, input) = match builder.wrapper { - Some(WrapperConfig::Custom(wrappers)) if !wrappers.is_empty() => { + WrapperConfig::Custom(wrappers) if !wrappers.is_empty() => { encode_wrapper_settlement(&wrappers, settle_calldata).expect("wrappers is non-empty") } - Some(WrapperConfig::Flashloan(loans)) => { + WrapperConfig::Flashloan(loans) => { let calldata = contracts::FlashLoanRouter::FlashLoanRouter::flashLoanAndSettleCall { loans: loans .into_iter() From 6ecd60048eac26278cebd164be754b981393a35f Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Wed, 29 Apr 2026 07:07:36 +0000 Subject: [PATCH 20/44] correct tenderly block --- crates/simulator/src/simulation_builder.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/crates/simulator/src/simulation_builder.rs b/crates/simulator/src/simulation_builder.rs index 8799783121..e10ed6f5c6 100644 --- a/crates/simulator/src/simulation_builder.rs +++ b/crates/simulator/src/simulation_builder.rs @@ -360,11 +360,10 @@ impl EthCallInputs { /// tenderly. pub fn to_tenderly_request(&self) -> Result { Ok(crate::tenderly::dto::Request { - block_number: Some(self.block), - // By default, tenderly simulates on the top of the specified block, whereas regular - // nodes simulate at the end of the specified block. This is to make - // simulation results match in case critical state changed within the block. - transaction_index: Some(-1), + // By default tenderly simulates calls at the start of the block. So if we simulate + // something when the latest block is `n` we need to tell tenderly to simulate at + // block `n+1` to still have all of block n's txs happen before our simulation runs. + block_number: Some(self.block + 1), network_id: self.simulator.0.chain_id.to_string(), from: self.request.from.unwrap_or_default(), // TODO: error handling From 93a8dda7d07b236da96f0503326e52936aff7cfe Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Wed, 29 Apr 2026 08:50:05 +0000 Subject: [PATCH 21/44] comment for idea --- crates/orderbook/src/orderbook.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/orderbook/src/orderbook.rs b/crates/orderbook/src/orderbook.rs index a5e1fdb0fa..520bcb0e0c 100644 --- a/crates/orderbook/src/orderbook.rs +++ b/crates/orderbook/src/orderbook.rs @@ -663,6 +663,7 @@ impl Orderbook { .as_ref() .ok_or_else(|| OrderSimulationError::Other(anyhow!("can't find appdata")))?; + // TODO: move this logic into builder by introducing `WrapperConfig::FromAppData(String)`? let parsed_app_data = self .order_validator .validate_app_data( From 5c1f7c3e262148f692a5e6933f00c1f03baa7eb8 Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Wed, 29 Apr 2026 11:52:13 +0000 Subject: [PATCH 22/44] extract wrappers from appdata --- crates/orderbook/src/orderbook.rs | 36 ++--------------- crates/simulator/src/simulation_builder.rs | 7 ++++ crates/simulator/src/simulation_encoding.rs | 44 ++++++++++++++++++++- 3 files changed, 54 insertions(+), 33 deletions(-) diff --git a/crates/orderbook/src/orderbook.rs b/crates/orderbook/src/orderbook.rs index 520bcb0e0c..69b4f8f754 100644 --- a/crates/orderbook/src/orderbook.rs +++ b/crates/orderbook/src/orderbook.rs @@ -660,39 +660,9 @@ impl Orderbook { let full_app_data = order .metadata .full_app_data - .as_ref() + .clone() .ok_or_else(|| OrderSimulationError::Other(anyhow!("can't find appdata")))?; - // TODO: move this logic into builder by introducing `WrapperConfig::FromAppData(String)`? - let parsed_app_data = self - .order_validator - .validate_app_data( - &OrderCreationAppData::Full { - full: full_app_data.clone(), - }, - &None, - ) - .map_err(|err| OrderSimulationError::Other(anyhow::anyhow!("{:?}", err)))? - .inner - .protocol; - - let wrapper = match (parsed_app_data.flashloan, parsed_app_data.wrappers) { - (None, wrappers) if wrappers.is_empty() => simulation_builder::WrapperConfig::NoWrapper, - (None, wrappers) if !wrappers.is_empty() => { - // TODO: convert wrappers - simulation_builder::WrapperConfig::Custom(vec![]) - } - (Some(flashloan), wrappers) if wrappers.is_empty() => { - // TODO: convert flashloan - simulation_builder::WrapperConfig::Flashloan(vec![]) - } - _ => { - return Err(OrderSimulationError::Other(anyhow!( - "can't configure custom wrappers and flashloans at the same time" - ))); - } - }; - let sim = order_simulator .new_simulation_builder() .add_order( @@ -709,7 +679,9 @@ impl Orderbook { ) .fund_settlement_contract_with_buy_tokens() .from_solver(simulation_builder::Solver::Fake(None)) - .with_wrapper(wrapper) + .with_wrapper(simulation_builder::WrapperConfig::FromAppData( + full_app_data, + )) .build() .await .context("failed to finalize simulation")?; diff --git a/crates/simulator/src/simulation_builder.rs b/crates/simulator/src/simulation_builder.rs index e10ed6f5c6..d5ac64b19a 100644 --- a/crates/simulator/src/simulation_builder.rs +++ b/crates/simulator/src/simulation_builder.rs @@ -256,6 +256,9 @@ pub enum WrapperConfig { Flashloan(Vec), Custom(Vec), NoWrapper, + /// Parse the app data JSON string and extract flashloan/wrapper + /// configuration. + FromAppData(String), } pub struct FlashloanRequest { @@ -426,4 +429,8 @@ pub enum BuildError { NoPriceEncoding, #[error("failed to query filled amount from settlement contract: {0}")] FilledAmountQuery(#[source] anyhow::Error), + #[error("failed to parse app data: {0}")] + AppDataParse(#[source] serde_json::Error), + #[error("both wrappers and flashloans cannot be encoded in the same settlement")] + FlashloanWrappersIncompatible, } diff --git a/crates/simulator/src/simulation_encoding.rs b/crates/simulator/src/simulation_encoding.rs index e2723d25c2..71e57aa3e9 100644 --- a/crates/simulator/src/simulation_encoding.rs +++ b/crates/simulator/src/simulation_encoding.rs @@ -3,6 +3,7 @@ use { encoding::{ EncodedSettlement, Interactions, + WrapperCall, encode_interactions, encode_trade, encode_wrapper_settlement, @@ -12,6 +13,7 @@ use { BuildError, EthCallInputs, ExecutionAmount, + FlashloanRequest, Order, Prices, SimulationBuilder, @@ -113,7 +115,8 @@ pub(crate) async fn encode( bytes.into() }; - let (to, input) = match builder.wrapper { + let wrapper = resolve_wrapper_config(builder.wrapper)?; + let (to, input) = match wrapper { WrapperConfig::Custom(wrappers) if !wrappers.is_empty() => { encode_wrapper_settlement(&wrappers, settle_calldata).expect("wrappers is non-empty") } @@ -179,6 +182,45 @@ pub(crate) async fn encode( }) } +fn resolve_wrapper_config(wrapper: WrapperConfig) -> Result { + let WrapperConfig::FromAppData(app_data_str) = wrapper else { + return Ok(wrapper); + }; + + let protocol = app_data::parse(app_data_str.as_bytes()).map_err(BuildError::AppDataParse)?; + + let has_wrappers = !protocol.wrappers.is_empty(); + let has_flashloan = protocol.flashloan.is_some(); + + if has_wrappers && has_flashloan { + return Err(BuildError::FlashloanWrappersIncompatible); + } + + if has_wrappers { + return Ok(WrapperConfig::Custom( + protocol + .wrappers + .into_iter() + .map(|w| WrapperCall { + address: w.address, + data: w.data.into(), + }) + .collect(), + )); + } + + if let Some(flashloan) = protocol.flashloan { + return Ok(WrapperConfig::Flashloan(vec![FlashloanRequest { + amount: flashloan.amount, + borrower: flashloan.protocol_adapter, + lender: flashloan.liquidity_provider, + token: flashloan.token, + }])); + } + + Ok(WrapperConfig::NoWrapper) +} + async fn executed_amount( builder: &SimulationBuilder, order: &Order, From 3f33170ef33d8c2ead010acbf0eb0b17d6599022 Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Wed, 29 Apr 2026 12:57:08 +0000 Subject: [PATCH 23/44] Populate properties from appdata --- crates/orderbook/openapi.yml | 25 +++- crates/orderbook/src/dto/mod.rs | 23 +++- crates/orderbook/src/orderbook.rs | 138 ++++++++------------ crates/orderbook/src/run.rs | 39 +----- crates/simulator/src/simulation_builder.rs | 64 ++++++++- crates/simulator/src/simulation_encoding.rs | 43 +----- 6 files changed, 159 insertions(+), 173 deletions(-) diff --git a/crates/orderbook/openapi.yml b/crates/orderbook/openapi.yml index b2191161ea..aa4d136b22 100644 --- a/crates/orderbook/openapi.yml +++ b/crates/orderbook/openapi.yml @@ -2731,24 +2731,37 @@ components: if omitted. allOf: - $ref: "#/components/schemas/Address" - nullable: true sellTokenBalance: description: Where the sell token should be drawn from. allOf: - $ref: "#/components/schemas/SellTokenSource" - default: erc20 buyTokenBalance: description: Where the buy token should be transferred to. allOf: - $ref: "#/components/schemas/BuyTokenDestination" - default: erc20 appData: description: > - Full app data JSON string. Defaults to `"{}"` if omitted. + Full app data JSON string. type: string - nullable: true blockNumber: type: integer + nullable: true + signingScheme: + $ref: "#/components/schemas/SigningScheme" + signature: + $ref: "#/components/schemas/Signature" + feeAmount: + description: > + The fee amount in sell token atoms. Expected to be 0; only present + because it must be part of the signed order data. + allOf: + - $ref: "#/components/schemas/TokenAmount" + validTo: + description: Unix timestamp (`uint32`) until which the order is valid. + type: integer + partiallyFillable: + description: Whether the order can be partially filled or must be filled all at once. + type: boolean required: - sellToken - buyToken @@ -2756,6 +2769,8 @@ components: - buyAmount - kind - owner + - signingScheme + - signature OrderSimulation: description: > The Tenderly simulation request for an order, along with any diff --git a/crates/orderbook/src/dto/mod.rs b/crates/orderbook/src/dto/mod.rs index 75b868d17f..cb5cf0b26a 100644 --- a/crates/orderbook/src/dto/mod.rs +++ b/crates/orderbook/src/dto/mod.rs @@ -4,7 +4,10 @@ pub mod order; use { alloy::primitives::U256, eth_domain_types::{Address, NonZeroU256}, - model::order::{BuyTokenDestination, OrderKind, SellTokenSource}, + model::{ + order::{BuyTokenDestination, OrderKind, SellTokenSource}, + signature::Signature, + }, number::serialization::HexOrDecimalU256, serde::{Deserialize, Serialize}, serde_with::serde_as, @@ -45,12 +48,22 @@ pub struct OrderSimulationRequest { /// token transfer or internal Balancer Vault transfer. #[serde(default)] pub buy_token_balance: BuyTokenDestination, - /// Full app data JSON. Defaults to `"{}"` if omitted. - #[serde(default)] - pub app_data: Option, + /// Full app data JSON. + pub app_data: String, /// The block number at which the simulation should happen - #[serde(default)] pub block_number: Option, + /// The order signature (signing scheme + bytes). Required to pass + /// signature verification in the settlement contract during simulation. + #[serde(flatten)] + pub signature: Signature, + /// The fee amount signed by the user. This field is expected to be 0 and + /// only still there because it needs to be signed by the user. + pub fee_amount: U256, + /// UNIX timestamp when the order will expire. + pub valid_to: u32, + /// Whether the order needs to be filled all at once or allows to be filled + /// over multiple partial executions. + pub partially_fillable: bool, } /// The result of Order simulation, contains the error (if any) diff --git a/crates/orderbook/src/orderbook.rs b/crates/orderbook/src/orderbook.rs index 69b4f8f754..d7fcad8bb8 100644 --- a/crates/orderbook/src/orderbook.rs +++ b/crates/orderbook/src/orderbook.rs @@ -5,25 +5,22 @@ use { trades::{TradeFilter, TradeRetrieving}, }, dto::{self, OrderSimulationRequest, OrderSimulationResult}, - order_simulator::{self, OrderSimulator}, solver_competition::{Identifier, LoadSolverCompetitionError, SolverCompetitionStoring}, }, - alloy::primitives::{Address, B256}, + alloy::primitives::{Address, B256, keccak256}, anyhow::{Context, Result, anyhow}, - app_data::{AppDataHash, Validator, WrapperCall}, + app_data::{AppDataHash, Validator}, bigdecimal::ToPrimitive, chrono::Utc, database::order_events::OrderEventLabel, model::{ DomainSeparator, order::{ - Interactions, Order, OrderCancellation, OrderCreation, OrderCreationAppData, OrderData, - OrderMetadata, OrderStatus, OrderUid, SignedOrderCancellations, @@ -240,8 +237,7 @@ pub struct Orderbook { order_validator: Arc, app_data: Arc, active_order_competition_threshold: u32, - order_simulator: Option>, - order_simulator2: Option>, + order_simulator: Option, } impl Orderbook { @@ -254,8 +250,7 @@ impl Orderbook { order_validator: Arc, app_data: Arc, active_order_competition_threshold: u32, - order_simulator: Option>, - order_simulator2: Option>, + order_simulator: Option, ) -> Self { Metrics::initialize(); Self { @@ -267,7 +262,6 @@ impl Orderbook { app_data, active_order_competition_threshold, order_simulator, - order_simulator2, } } @@ -617,26 +611,6 @@ impl Orderbook { Ok(status) } - fn parse_interactions_and_wrappers( - &self, - full_app_data: &str, - ) -> Result<(Interactions, Vec)> { - if !full_app_data.is_empty() { - let app_data = self - .order_validator - .validate_app_data( - &OrderCreationAppData::Full { - full: full_app_data.to_string(), - }, - &None, - ) - .map_err(|err| anyhow::anyhow!("{:?}", err))?; - Ok((app_data.interactions, app_data.inner.protocol.wrappers)) - } else { - Ok((Interactions::default(), Vec::default())) - } - } - /// Simulates an order based on its Uid using the OrderSimulator. /// /// The returned value contains the simulation result and tenderly API @@ -646,7 +620,7 @@ impl Orderbook { uid: &OrderUid, block_number: Option, ) -> Result, OrderSimulationError> { - let Some(order_simulator) = &self.order_simulator2 else { + let Some(order_simulator) = &self.order_simulator else { return Err(OrderSimulationError::NotEnabled); }; let Some(order) = self @@ -668,20 +642,18 @@ impl Orderbook { .add_order( simulation_builder::Order::new(order.data) .with_signature(order.metadata.owner, order.signature) - .with_pre_interactions(order.interactions.pre) - .with_post_interactions(order.interactions.post) .with_executed_amount(simulation_builder::ExecutionAmount::Remaining), ) + .parameters_from_app_data(&full_app_data) + .context("failed to parse app data")? .at_block( block_number .map(simulation_builder::Block::Number) .unwrap_or(simulation_builder::Block::Latest), ) + .with_prices(simulation_builder::Prices::Limit) .fund_settlement_contract_with_buy_tokens() .from_solver(simulation_builder::Solver::Fake(None)) - .with_wrapper(simulation_builder::WrapperConfig::FromAppData( - full_app_data, - )) .build() .await .context("failed to finalize simulation")?; @@ -707,45 +679,55 @@ impl Orderbook { let Some(order_simulator) = &self.order_simulator else { return Err(OrderSimulationError::NotEnabled); }; - let full_app_data = request.app_data.unwrap_or_default(); - let (interactions, wrappers) = self - .parse_interactions_and_wrappers(&full_app_data) - .map_err(|err| { - OrderSimulationError::MalformedInput(anyhow!("app_data `{}`: {err}", full_app_data)) - })?; - let order = Order { - metadata: OrderMetadata { - owner: request.owner, - full_app_data: Some(full_app_data), - ..Default::default() - }, - data: OrderData { - sell_token: request.sell_token, - buy_token: request.buy_token, - sell_amount: request.sell_amount.into(), - buy_amount: request.buy_amount, - kind: request.kind, - receiver: request.receiver, - sell_token_balance: request.sell_token_balance, - buy_token_balance: request.buy_token_balance, - ..Default::default() - }, - interactions, - ..Default::default() - }; + let app_data_hash = keccak256(&request.app_data); - let swap = order_simulator - .encode_order(&order, wrappers, request.block_number) + let sim = order_simulator + .new_simulation_builder() + .add_order( + simulation_builder::Order::new(OrderData { + sell_token: request.sell_token, + buy_token: request.buy_token, + sell_amount: request.sell_amount.into(), + buy_amount: request.buy_amount, + kind: request.kind, + receiver: request.receiver, + sell_token_balance: request.sell_token_balance, + buy_token_balance: request.buy_token_balance, + fee_amount: request.fee_amount, + valid_to: request.valid_to, + app_data: AppDataHash(app_data_hash.into()), + partially_fillable: request.partially_fillable, + }) + .with_signature(request.owner, request.signature) + .with_executed_amount(simulation_builder::ExecutionAmount::Full), + ) + .parameters_from_app_data(&request.app_data) + .context("failed to parse app data")? + .at_block( + request + .block_number + .map(simulation_builder::Block::Number) + .unwrap_or(simulation_builder::Block::Latest), + ) + .with_prices(simulation_builder::Prices::Limit) + .fund_settlement_contract_with_buy_tokens() + .from_solver(simulation_builder::Solver::Fake(None)) + .build() + .await + .context("failed to finalize simulation") + .map_err(OrderSimulationError::Other)?; + + let simulation_result = sim + .simulate_with_tenderly_report() .await - .map_err(|err| match err { - order_simulator::Error::Other(err) => OrderSimulationError::Other(err), - order_simulator::Error::MalformedInput(err) => { - OrderSimulationError::MalformedInput(err) - } - })?; - Ok(order_simulator - .simulate_swap(swap, request.block_number) - .await?) + .context("failed to execute simulation") + .map_err(OrderSimulationError::Other)?; + + Ok(OrderSimulationResult { + tenderly_url: simulation_result.tenderly_url, + tenderly_request: simulation_result.tenderly_request, + error: simulation_result.error, + }) } } @@ -776,15 +758,6 @@ pub enum OrderSimulationError { Other(anyhow::Error), } -impl From for OrderSimulationError { - fn from(value: order_simulator::Error) -> Self { - match value { - order_simulator::Error::Other(err) => Self::Other(err), - order_simulator::Error::MalformedInput(err) => Self::MalformedInput(err), - } - } -} - #[async_trait::async_trait] impl LivenessChecking for Orderbook { async fn is_alive(&self) -> bool { @@ -871,7 +844,6 @@ mod tests { app_data, active_order_competition_threshold: Default::default(), order_simulator: None, - order_simulator2: None, }; // Different owner diff --git a/crates/orderbook/src/run.rs b/crates/orderbook/src/run.rs index 6b5b8b6971..ae9cdee469 100644 --- a/crates/orderbook/src/run.rs +++ b/crates/orderbook/src/run.rs @@ -5,7 +5,6 @@ use { database::Postgres, ipfs::Ipfs, ipfs_app_data::IpfsAppData, - order_simulator::OrderSimulator, orderbook::Orderbook, quoter::QuoteHandler, }, @@ -43,7 +42,6 @@ use { order_quoting::{self, OrderQuoter}, order_validation::{OrderValidPeriodConfiguration, OrderValidator}, }, - simulator::swap_simulator::SwapSimulator, std::{future::Future, net::SocketAddr, sync::Arc, time::Duration}, token_info::{CachedTokenInfoFetcher, TokenInfoFetcher}, tokio::task::{self, JoinHandle}, @@ -171,6 +169,7 @@ pub async fn run(config: Configuration) { .await .expect("load hooks trampoline contract"), }; + let hooks_trampoline_address = *hooks_contract.address(); verify_deployed_contract_constants(&settlement_contract, chain_id) .await @@ -411,36 +410,6 @@ pub async fn run(config: Configuration) { ipfs, )); - let order_simulator = if let Some(config) = &config.order_simulation { - let tenderly: Option> = - config.tenderly.as_ref().map(|tenderly_config| { - Box::new(simulator::tenderly::TenderlyApi::new( - tenderly_config, - &http_factory, - chain.id().to_string(), - )) as _ - }); - Some(Arc::new(OrderSimulator::new( - SwapSimulator::new( - balance_overrider.clone(), - *settlement_contract.address(), - *native_token.address(), - current_block_stream.clone(), - web3, - config - .gas_limit - .try_into() - .expect("gas_limit must fit in u64"), - ) - .await - .expect("failed to create SwapSimulator"), - chain.id().to_string(), - tenderly, - ))) - } else { - None - }; - let order_simulator2 = if let Some(config) = config.order_simulation { let tenderly: Option> = config.tenderly.as_ref().map(|tenderly_config| { @@ -450,17 +419,18 @@ pub async fn run(config: Configuration) { chain.id().to_string(), )) as _ }); - Some(Arc::new( + Some( simulator::simulation_builder::SettlementSimulator::new( settlement_contract.clone(), Default::default(), + hooks_trampoline_address, balance_overrider.clone(), current_block_stream.clone(), tenderly, ) .await .unwrap(), - )) + ) } else { None }; @@ -473,7 +443,6 @@ pub async fn run(config: Configuration) { order_validator.clone(), app_data.clone(), config.active_order_competition_threshold, - order_simulator, order_simulator2, )); diff --git a/crates/simulator/src/simulation_builder.rs b/crates/simulator/src/simulation_builder.rs index d5ac64b19a..f8202bcbe4 100644 --- a/crates/simulator/src/simulation_builder.rs +++ b/crates/simulator/src/simulation_builder.rs @@ -9,6 +9,7 @@ use { TransactionRequest, state::{AccountOverride, StateOverride}, }, + alloy_sol_types::SolCall, alloy_transport::RpcError, anyhow::{Context, Result}, balance_overrides::BalanceOverriding, @@ -32,6 +33,7 @@ pub(crate) struct Inner { pub(crate) settlement: contracts::GPv2Settlement::Instance, pub(crate) authenticator: Address, pub(crate) flash_loan_router: Address, + pub(crate) hooks_trampoline: Address, pub(crate) balance_overrides: Arc, pub(crate) provider: DynProvider, pub(crate) domain_separator: DomainSeparator, @@ -44,6 +46,7 @@ impl SettlementSimulator { pub async fn new( settlement: contracts::GPv2Settlement::Instance, flash_loan_router: Address, + hooks_trampoline: Address, balance_overrides: Arc, current_block: CurrentBlockWatcher, tenderly: Option>, @@ -56,6 +59,7 @@ impl SettlementSimulator { settlement, authenticator, flash_loan_router, + hooks_trampoline, balance_overrides, provider, domain_separator, @@ -167,6 +171,63 @@ impl SimulationBuilder { self } + /// Parses the app data JSON and configures the builder accordingly: + /// - Pre/post hooks are encoded as interactions via the [`HooksTrampoline`] + /// - Flashloan/wrapper fields set the [`WrapperConfig`]. + pub fn parameters_from_app_data(mut self, app_data: &str) -> Result { + let protocol = app_data::parse(app_data.as_bytes()).map_err(BuildError::AppDataParse)?; + + let encode_hooks = |hooks: &[app_data::Hook]| -> Vec { + if hooks.is_empty() { + return vec![]; + } + vec![InteractionData { + target: self.simulator.0.hooks_trampoline, + value: U256::ZERO, + call_data: contracts::HooksTrampoline::HooksTrampoline::executeCall { + hooks: hooks + .iter() + .map(|h| contracts::HooksTrampoline::HooksTrampoline::Hook { + target: h.target, + callData: Bytes::copy_from_slice(&h.call_data), + gasLimit: U256::from(h.gas_limit), + }) + .collect(), + } + .abi_encode(), + }] + }; + self.pre_interactions = encode_hooks(&protocol.hooks.pre); + self.post_interactions = encode_hooks(&protocol.hooks.post); + + let has_wrappers = !protocol.wrappers.is_empty(); + let has_flashloan = protocol.flashloan.is_some(); + if has_wrappers && has_flashloan { + return Err(BuildError::FlashloanWrappersIncompatible); + } + if has_wrappers { + self.wrapper = WrapperConfig::Custom( + protocol + .wrappers + .into_iter() + .map(|w| WrapperCall { + address: w.address, + data: w.data.into(), + }) + .collect(), + ); + } else if let Some(flashloan) = protocol.flashloan { + self.wrapper = WrapperConfig::Flashloan(vec![FlashloanRequest { + amount: flashloan.amount, + borrower: flashloan.protocol_adapter, + lender: flashloan.liquidity_provider, + token: flashloan.token, + }]); + } + + Ok(self) + } + /// Override the settlement contract's buy token balance so it can pay out /// the order without any external liquidity. The required amount is derived /// from the order's executed amount and clearing prices at `build()` time. @@ -256,9 +317,6 @@ pub enum WrapperConfig { Flashloan(Vec), Custom(Vec), NoWrapper, - /// Parse the app data JSON string and extract flashloan/wrapper - /// configuration. - FromAppData(String), } pub struct FlashloanRequest { diff --git a/crates/simulator/src/simulation_encoding.rs b/crates/simulator/src/simulation_encoding.rs index 71e57aa3e9..a8edecbfab 100644 --- a/crates/simulator/src/simulation_encoding.rs +++ b/crates/simulator/src/simulation_encoding.rs @@ -3,7 +3,6 @@ use { encoding::{ EncodedSettlement, Interactions, - WrapperCall, encode_interactions, encode_trade, encode_wrapper_settlement, @@ -13,7 +12,6 @@ use { BuildError, EthCallInputs, ExecutionAmount, - FlashloanRequest, Order, Prices, SimulationBuilder, @@ -115,7 +113,7 @@ pub(crate) async fn encode( bytes.into() }; - let wrapper = resolve_wrapper_config(builder.wrapper)?; + let wrapper = builder.wrapper; let (to, input) = match wrapper { WrapperConfig::Custom(wrappers) if !wrappers.is_empty() => { encode_wrapper_settlement(&wrappers, settle_calldata).expect("wrappers is non-empty") @@ -182,45 +180,6 @@ pub(crate) async fn encode( }) } -fn resolve_wrapper_config(wrapper: WrapperConfig) -> Result { - let WrapperConfig::FromAppData(app_data_str) = wrapper else { - return Ok(wrapper); - }; - - let protocol = app_data::parse(app_data_str.as_bytes()).map_err(BuildError::AppDataParse)?; - - let has_wrappers = !protocol.wrappers.is_empty(); - let has_flashloan = protocol.flashloan.is_some(); - - if has_wrappers && has_flashloan { - return Err(BuildError::FlashloanWrappersIncompatible); - } - - if has_wrappers { - return Ok(WrapperConfig::Custom( - protocol - .wrappers - .into_iter() - .map(|w| WrapperCall { - address: w.address, - data: w.data.into(), - }) - .collect(), - )); - } - - if let Some(flashloan) = protocol.flashloan { - return Ok(WrapperConfig::Flashloan(vec![FlashloanRequest { - amount: flashloan.amount, - borrower: flashloan.protocol_adapter, - lender: flashloan.liquidity_provider, - token: flashloan.token, - }])); - } - - Ok(WrapperConfig::NoWrapper) -} - async fn executed_amount( builder: &SimulationBuilder, order: &Order, From 7d0253a94da95e928df9ec38e962d5a756d9be23 Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Wed, 29 Apr 2026 13:00:54 +0000 Subject: [PATCH 24/44] .context() instead of unwrap() --- crates/simulator/src/simulation_builder.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/simulator/src/simulation_builder.rs b/crates/simulator/src/simulation_builder.rs index f8202bcbe4..b7a20307e9 100644 --- a/crates/simulator/src/simulation_builder.rs +++ b/crates/simulator/src/simulation_builder.rs @@ -395,8 +395,9 @@ impl EthCallInputs { } pub async fn simulate_with_tenderly_report(self) -> Result { - // TODO: error handling - let tenderly_request = self.to_tenderly_request().unwrap(); + let tenderly_request = self + .to_tenderly_request() + .context("failed to convert to tenderly request")?; let tenderly_url = match &self.simulator.0.tenderly { Some(api) => Some( api.simulate_and_share(tenderly_request.clone()) From 764d3e732491063bf42062df2d2412fc428b4b37 Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Wed, 29 Apr 2026 13:45:54 +0000 Subject: [PATCH 25/44] fix tests --- crates/e2e/tests/e2e/order_simulation.rs | 58 ++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/crates/e2e/tests/e2e/order_simulation.rs b/crates/e2e/tests/e2e/order_simulation.rs index e7c63dc762..d5a61f75a3 100644 --- a/crates/e2e/tests/e2e/order_simulation.rs +++ b/crates/e2e/tests/e2e/order_simulation.rs @@ -1,10 +1,19 @@ use { - alloy::{primitives::Address, providers::Provider}, + alloy::{ + primitives::{Address, ruint::aliases::U256}, + providers::Provider, + }, configs::test_util::TestDefault, e2e::setup::{API_HOST, OnchainComponents, Services, TIMEOUT, run_test, wait_for_condition}, ethrpc::{Web3, alloy::CallBuilderExt}, model::{ - order::{OrderCreation, OrderKind}, + order::{ + BuyTokenDestination, + OrderCreation, + OrderCreationAppData, + OrderKind, + SellTokenSource, + }, signature::EcdsaSigningScheme, }, number::units::EthUnit, @@ -50,15 +59,53 @@ async fn custom_order_simulation(web3: Web3) { .await; let client = services.client(); + let valid_to = model::time::now_in_epoch_seconds() + 100; + let sell_amount = 1u64.eth(); - let request = orderbook::dto::OrderSimulationRequest { + let app_data = "{}"; + + // Sign an OrderCreation with the same fields the simulation will encode. + // simulate_custom_order hashes `app_data` with keccak256; OrderCreation::Full + // does the same, so the signature covers exactly the same OrderData hash. + let signed = OrderCreation { sell_token: *token.address(), buy_token: *onchain.contracts().weth.address(), - sell_amount: sell_amount.try_into().expect("Sell amount is non zero"), + sell_amount, buy_amount: 1u64.eth(), kind: OrderKind::Sell, - owner: trader.address(), + receiver: Some(Address::default()), + sell_token_balance: SellTokenSource::Erc20, + buy_token_balance: BuyTokenDestination::Erc20, + fee_amount: U256::ZERO, + valid_to, + app_data: OrderCreationAppData::Full { + full: app_data.to_string(), + }, + partially_fillable: false, ..Default::default() + } + .sign( + EcdsaSigningScheme::Eip712, + &onchain.contracts().domain_separator, + &trader.signer, + ); + + let request = orderbook::dto::OrderSimulationRequest { + sell_token: signed.sell_token, + buy_token: signed.buy_token, + sell_amount: signed.sell_amount.try_into().unwrap(), + buy_amount: signed.buy_amount, + kind: signed.kind, + owner: trader.address(), + receiver: signed.receiver, + sell_token_balance: signed.sell_token_balance, + buy_token_balance: signed.buy_token_balance, + app_data: app_data.to_string(), + block_number: None, + signature: signed.signature, + fee_amount: signed.fee_amount, + valid_to: signed.valid_to, + partially_fillable: signed.partially_fillable, }; // Trader has no sell tokens — simulation should revert. @@ -368,6 +415,7 @@ async fn order_simulation_partial_fill(web3: Web3) { .unwrap(); assert_eq!(response.status(), StatusCode::OK); let result = response.json::().await.unwrap(); + tracing::error!(?result); assert_eq!( result.error, None, From 934da3977a3b4cdef63f4fd8eb81bf38ae8de3bd Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Wed, 29 Apr 2026 13:52:37 +0000 Subject: [PATCH 26/44] remove test log --- crates/e2e/tests/e2e/order_simulation.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/e2e/tests/e2e/order_simulation.rs b/crates/e2e/tests/e2e/order_simulation.rs index d5a61f75a3..1217ed2d69 100644 --- a/crates/e2e/tests/e2e/order_simulation.rs +++ b/crates/e2e/tests/e2e/order_simulation.rs @@ -415,7 +415,6 @@ async fn order_simulation_partial_fill(web3: Web3) { .unwrap(); assert_eq!(response.status(), StatusCode::OK); let result = response.json::().await.unwrap(); - tracing::error!(?result); assert_eq!( result.error, None, From c1aabeb4625b7bb2b8267ff33e7f459713faa963 Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Thu, 30 Apr 2026 06:55:11 +0000 Subject: [PATCH 27/44] more functionality needed for trade verification --- crates/simulator/src/simulation_builder.rs | 25 ++++++++++++++++++++- crates/simulator/src/simulation_encoding.rs | 11 +++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/crates/simulator/src/simulation_builder.rs b/crates/simulator/src/simulation_builder.rs index b7a20307e9..e4f1d21563 100644 --- a/crates/simulator/src/simulation_builder.rs +++ b/crates/simulator/src/simulation_builder.rs @@ -12,7 +12,7 @@ use { alloy_sol_types::SolCall, alloy_transport::RpcError, anyhow::{Context, Result}, - balance_overrides::BalanceOverriding, + balance_overrides::{BalanceOverrideRequest, BalanceOverriding}, ethrpc::block_stream::CurrentBlockWatcher, model::{ DomainSeparator, @@ -82,9 +82,14 @@ impl SettlementSimulator { auction_id: None, state_overrides: StateOverride::default(), fund_settlement_contract: false, + fund_requests: vec![], block: Block::Latest, } } + + pub fn domain_separator(&self) -> DomainSeparator { + self.0.domain_separator + } } /// Which block to simulate against. @@ -110,6 +115,7 @@ pub struct SimulationBuilder { pub(crate) state_overrides: StateOverride, pub(crate) simulator: SettlementSimulator, pub(crate) fund_settlement_contract: bool, + pub(crate) fund_requests: Vec, pub(crate) block: Block, } @@ -236,6 +242,23 @@ impl SimulationBuilder { self } + /// Override the token balance of an arbitrary address. Useful for seeding + /// an intermediate funding contract (e.g. Spardose) with sell tokens before + /// the simulation runs. + pub fn fund_address_with_tokens( + mut self, + holder: Address, + token: Address, + amount: U256, + ) -> Self { + self.fund_requests.push(BalanceOverrideRequest { + token, + holder, + amount, + }); + self + } + /// Finishes the simulation struct based on the configuration thus far. pub async fn build(self) -> Result { self.build_with_modifications(|_| {}).await diff --git a/crates/simulator/src/simulation_encoding.rs b/crates/simulator/src/simulation_encoding.rs index a8edecbfab..0fd48b8a9d 100644 --- a/crates/simulator/src/simulation_encoding.rs +++ b/crates/simulator/src/simulation_encoding.rs @@ -167,6 +167,17 @@ pub(crate) async fn encode( state_overrides.insert(address, state_override); } + for request in builder.fund_requests { + let (address, account_override) = builder + .simulator + .0 + .balance_overrides + .state_override(request) + .await + .ok_or(BuildError::FailedToOverrideBalances)?; + state_overrides.insert(address, account_override); + } + Ok(EthCallInputs { request: TransactionRequest { from: Some(from), From 29c225466cf14f326851bff343c6345482ddb3f4 Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Thu, 30 Apr 2026 06:58:02 +0000 Subject: [PATCH 28/44] remove need for native token address in trade verification --- contracts/artifacts/Solver.json | 9 +--- contracts/artifacts/Trader.json | 9 +--- .../contracts-generated/solver/src/lib.rs | 41 ++++++------------- .../contracts-generated/trader/src/lib.rs | 41 ++++++------------- contracts/solidity/Solver.sol | 4 +- contracts/solidity/Trader.sol | 19 --------- .../src/trade_verifier/mod.rs | 1 - 7 files changed, 29 insertions(+), 95 deletions(-) diff --git a/contracts/artifacts/Solver.json b/contracts/artifacts/Solver.json index 94ba3bd7da..1a0068b09a 100644 --- a/contracts/artifacts/Solver.json +++ b/contracts/artifacts/Solver.json @@ -22,11 +22,6 @@ "name": "sellAmount", "type": "uint256" }, - { - "internalType": "address", - "name": "nativeToken", - "type": "address" - }, { "internalType": "address", "name": "spardose", @@ -101,8 +96,8 @@ "type": "function" } ], - "bytecode": "0x6080604052348015600e575f5ffd5b506109c58061001c5f395ff3fe608060405234801561000f575f5ffd5b506004361061003f575f3560e01c80631d47e7f4146100435780632582edb41461006d5780633bbb2e1d14610082575b5f5ffd5b610056610051366004610702565b610095565b6040516100649291906107c6565b60405180910390f35b61008061007b366004610813565b610229565b005b610080610090366004610888565b61031e565b5f606033301461012b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f6f6e6c792073696d756c6174696f6e206c6f67696320697320616c6c6f77656460448201527f20746f2063616c6c202773776170272066756e6374696f6e0000000000000000606482015260840160405180910390fd5b5f8573ffffffffffffffffffffffffffffffffffffffff165f6040515f6040518083038185875af1925050503d805f8114610181576040519150601f19603f3d011682016040523d82523d5f602084013e610186565b606091505b505090505061019687878a61048d565b6101a1888585610557565b91506101ae87878a61048d565b7f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722c80548060200260200160405190810160405280929190818152602001828054801561021757602002820191905f5260205f20905b815481526020019060010190808311610203575b50505050509050965096945050505050565b5f5a6040517f542eb77d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8881166004830152878116602483015260448201879052858116606483015284811660848301529192509088169063542eb77d9060a4015f604051808303815f87803b1580156102b4575f5ffd5b505af11580156102c6573d5f5f3e3d5ffd5b505050505a6102d59082610901565b6102e19061116c61091a565b7f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722b5f828254610310919061091a565b909155505050505050505050565b5f5a90507f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722c73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff861614610407576040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85811660048301528616906370a0823190602401602060405180830381865afa1580156103de573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610402919061092d565b610420565b8373ffffffffffffffffffffffffffffffffffffffff16315b81546001810183555f9283526020909220909101558115610487575a6104469082610901565b6104529061116c61091a565b7f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722b5f828254610481919061091a565b90915550505b50505050565b5f5b828110156104875730633bbb2e1d8585848181106104af576104af610944565b90506020020160208101906104c49190610971565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff918216600482015290851660248201525f60448201526064015f604051808303815f87803b158015610535575f5ffd5b505af1158015610547573d5f5f3e3d5ffd5b50506001909201915061048f9050565b5f5f5a90506105b284848080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152505073ffffffffffffffffffffffffffffffffffffffff8916929150506105f3565b507f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722b545a6105e09083610901565b6105ea9190610901565b95945050505050565b6060610600835f84610607565b9392505050565b60605f8473ffffffffffffffffffffffffffffffffffffffff168484604051610630919061098c565b5f6040518083038185875af1925050503d805f811461066a576040519150601f19603f3d011682016040523d82523d5f602084013e61066f565b606091505b50925090508061068157815160208301fd5b509392505050565b73ffffffffffffffffffffffffffffffffffffffff811681146106aa575f5ffd5b50565b80356106b881610689565b919050565b5f5f83601f8401126106cd575f5ffd5b50813567ffffffffffffffff8111156106e4575f5ffd5b6020830191508360208285010111156106fb575f5ffd5b9250929050565b5f5f5f5f5f5f60808789031215610717575f5ffd5b863561072281610689565b9550602087013567ffffffffffffffff81111561073d575f5ffd5b8701601f8101891361074d575f5ffd5b803567ffffffffffffffff811115610763575f5ffd5b8960208260051b8401011115610777575f5ffd5b6020919091019550935061078d604088016106ad565b9250606087013567ffffffffffffffff8111156107a8575f5ffd5b6107b489828a016106bd565b979a9699509497509295939492505050565b5f60408201848352604060208401528084518083526060850191506020860192505f5b818110156108075783518352602093840193909201916001016107e9565b50909695505050505050565b5f5f5f5f5f5f60c08789031215610828575f5ffd5b863561083381610689565b9550602087013561084381610689565b9450604087013561085381610689565b935060608701359250608087013561086a81610689565b915060a087013561087a81610689565b809150509295509295509295565b5f5f5f6060848603121561089a575f5ffd5b83356108a581610689565b925060208401356108b581610689565b9150604084013580151581146108c9575f5ffd5b809150509250925092565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b81810381811115610914576109146108d4565b92915050565b80820180821115610914576109146108d4565b5f6020828403121561093d575f5ffd5b5051919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f60208284031215610981575f5ffd5b813561060081610689565b5f82515f5b818110156109ab5760208186018101518583015201610991565b505f92019182525091905056fea164736f6c634300081e000a", - "deployedBytecode": "0x608060405234801561000f575f5ffd5b506004361061003f575f3560e01c80631d47e7f4146100435780632582edb41461006d5780633bbb2e1d14610082575b5f5ffd5b610056610051366004610702565b610095565b6040516100649291906107c6565b60405180910390f35b61008061007b366004610813565b610229565b005b610080610090366004610888565b61031e565b5f606033301461012b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f6f6e6c792073696d756c6174696f6e206c6f67696320697320616c6c6f77656460448201527f20746f2063616c6c202773776170272066756e6374696f6e0000000000000000606482015260840160405180910390fd5b5f8573ffffffffffffffffffffffffffffffffffffffff165f6040515f6040518083038185875af1925050503d805f8114610181576040519150601f19603f3d011682016040523d82523d5f602084013e610186565b606091505b505090505061019687878a61048d565b6101a1888585610557565b91506101ae87878a61048d565b7f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722c80548060200260200160405190810160405280929190818152602001828054801561021757602002820191905f5260205f20905b815481526020019060010190808311610203575b50505050509050965096945050505050565b5f5a6040517f542eb77d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8881166004830152878116602483015260448201879052858116606483015284811660848301529192509088169063542eb77d9060a4015f604051808303815f87803b1580156102b4575f5ffd5b505af11580156102c6573d5f5f3e3d5ffd5b505050505a6102d59082610901565b6102e19061116c61091a565b7f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722b5f828254610310919061091a565b909155505050505050505050565b5f5a90507f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722c73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff861614610407576040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85811660048301528616906370a0823190602401602060405180830381865afa1580156103de573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610402919061092d565b610420565b8373ffffffffffffffffffffffffffffffffffffffff16315b81546001810183555f9283526020909220909101558115610487575a6104469082610901565b6104529061116c61091a565b7f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722b5f828254610481919061091a565b90915550505b50505050565b5f5b828110156104875730633bbb2e1d8585848181106104af576104af610944565b90506020020160208101906104c49190610971565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff918216600482015290851660248201525f60448201526064015f604051808303815f87803b158015610535575f5ffd5b505af1158015610547573d5f5f3e3d5ffd5b50506001909201915061048f9050565b5f5f5a90506105b284848080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152505073ffffffffffffffffffffffffffffffffffffffff8916929150506105f3565b507f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722b545a6105e09083610901565b6105ea9190610901565b95945050505050565b6060610600835f84610607565b9392505050565b60605f8473ffffffffffffffffffffffffffffffffffffffff168484604051610630919061098c565b5f6040518083038185875af1925050503d805f811461066a576040519150601f19603f3d011682016040523d82523d5f602084013e61066f565b606091505b50925090508061068157815160208301fd5b509392505050565b73ffffffffffffffffffffffffffffffffffffffff811681146106aa575f5ffd5b50565b80356106b881610689565b919050565b5f5f83601f8401126106cd575f5ffd5b50813567ffffffffffffffff8111156106e4575f5ffd5b6020830191508360208285010111156106fb575f5ffd5b9250929050565b5f5f5f5f5f5f60808789031215610717575f5ffd5b863561072281610689565b9550602087013567ffffffffffffffff81111561073d575f5ffd5b8701601f8101891361074d575f5ffd5b803567ffffffffffffffff811115610763575f5ffd5b8960208260051b8401011115610777575f5ffd5b6020919091019550935061078d604088016106ad565b9250606087013567ffffffffffffffff8111156107a8575f5ffd5b6107b489828a016106bd565b979a9699509497509295939492505050565b5f60408201848352604060208401528084518083526060850191506020860192505f5b818110156108075783518352602093840193909201916001016107e9565b50909695505050505050565b5f5f5f5f5f5f60c08789031215610828575f5ffd5b863561083381610689565b9550602087013561084381610689565b9450604087013561085381610689565b935060608701359250608087013561086a81610689565b915060a087013561087a81610689565b809150509295509295509295565b5f5f5f6060848603121561089a575f5ffd5b83356108a581610689565b925060208401356108b581610689565b9150604084013580151581146108c9575f5ffd5b809150509250925092565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b81810381811115610914576109146108d4565b92915050565b80820180821115610914576109146108d4565b5f6020828403121561093d575f5ffd5b5051919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f60208284031215610981575f5ffd5b813561060081610689565b5f82515f5b818110156109ab5760208186018101518583015201610991565b505f92019182525091905056fea164736f6c634300081e000a", + "bytecode": "0x6080604052348015600e575f5ffd5b506109ab8061001c5f395ff3fe608060405234801561000f575f5ffd5b506004361061003f575f3560e01c80631d47e7f4146100435780632ff111f51461006d5780633bbb2e1d14610082575b5f5ffd5b6100566100513660046106f9565b610095565b6040516100649291906107bd565b60405180910390f35b61008061007b36600461080a565b610229565b005b61008061009036600461086e565b610315565b5f606033301461012b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f6f6e6c792073696d756c6174696f6e206c6f67696320697320616c6c6f77656460448201527f20746f2063616c6c202773776170272066756e6374696f6e0000000000000000606482015260840160405180910390fd5b5f8573ffffffffffffffffffffffffffffffffffffffff165f6040515f6040518083038185875af1925050503d805f8114610181576040519150601f19603f3d011682016040523d82523d5f602084013e610186565b606091505b505090505061019687878a610484565b6101a188858561054e565b91506101ae87878a610484565b7f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722c80548060200260200160405190810160405280929190818152602001828054801561021757602002820191905f5260205f20905b815481526020019060010190808311610203575b50505050509050965096945050505050565b5f5a6040517f5bbe8b3f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8781166004830152868116602483015260448201869052848116606483015291925090871690635bbe8b3f906084015f604051808303815f87803b1580156102ac575f5ffd5b505af11580156102be573d5f5f3e3d5ffd5b505050505a6102cd90826108e7565b6102d99061116c610900565b7f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722b5f8282546103089190610900565b9091555050505050505050565b5f5a90507f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722c73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff8616146103fe576040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85811660048301528616906370a0823190602401602060405180830381865afa1580156103d5573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103f99190610913565b610417565b8373ffffffffffffffffffffffffffffffffffffffff16315b81546001810183555f928352602090922090910155811561047e575a61043d90826108e7565b6104499061116c610900565b7f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722b5f8282546104789190610900565b90915550505b50505050565b5f5b8281101561047e5730633bbb2e1d8585848181106104a6576104a661092a565b90506020020160208101906104bb9190610957565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff918216600482015290851660248201525f60448201526064015f604051808303815f87803b15801561052c575f5ffd5b505af115801561053e573d5f5f3e3d5ffd5b5050600190920191506104869050565b5f5f5a90506105a984848080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152505073ffffffffffffffffffffffffffffffffffffffff8916929150506105ea565b507f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722b545a6105d790836108e7565b6105e191906108e7565b95945050505050565b60606105f7835f846105fe565b9392505050565b60605f8473ffffffffffffffffffffffffffffffffffffffff1684846040516106279190610972565b5f6040518083038185875af1925050503d805f8114610661576040519150601f19603f3d011682016040523d82523d5f602084013e610666565b606091505b50925090508061067857815160208301fd5b509392505050565b73ffffffffffffffffffffffffffffffffffffffff811681146106a1575f5ffd5b50565b80356106af81610680565b919050565b5f5f83601f8401126106c4575f5ffd5b50813567ffffffffffffffff8111156106db575f5ffd5b6020830191508360208285010111156106f2575f5ffd5b9250929050565b5f5f5f5f5f5f6080878903121561070e575f5ffd5b863561071981610680565b9550602087013567ffffffffffffffff811115610734575f5ffd5b8701601f81018913610744575f5ffd5b803567ffffffffffffffff81111561075a575f5ffd5b8960208260051b840101111561076e575f5ffd5b60209190910195509350610784604088016106a4565b9250606087013567ffffffffffffffff81111561079f575f5ffd5b6107ab89828a016106b4565b979a9699509497509295939492505050565b5f60408201848352604060208401528084518083526060850191506020860192505f5b818110156107fe5783518352602093840193909201916001016107e0565b50909695505050505050565b5f5f5f5f5f60a0868803121561081e575f5ffd5b853561082981610680565b9450602086013561083981610680565b9350604086013561084981610680565b925060608601359150608086013561086081610680565b809150509295509295909350565b5f5f5f60608486031215610880575f5ffd5b833561088b81610680565b9250602084013561089b81610680565b9150604084013580151581146108af575f5ffd5b809150509250925092565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b818103818111156108fa576108fa6108ba565b92915050565b808201808211156108fa576108fa6108ba565b5f60208284031215610923575f5ffd5b5051919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f60208284031215610967575f5ffd5b81356105f781610680565b5f82515f5b818110156109915760208186018101518583015201610977565b505f92019182525091905056fea164736f6c634300081e000a", + "deployedBytecode": "0x608060405234801561000f575f5ffd5b506004361061003f575f3560e01c80631d47e7f4146100435780632ff111f51461006d5780633bbb2e1d14610082575b5f5ffd5b6100566100513660046106f9565b610095565b6040516100649291906107bd565b60405180910390f35b61008061007b36600461080a565b610229565b005b61008061009036600461086e565b610315565b5f606033301461012b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f6f6e6c792073696d756c6174696f6e206c6f67696320697320616c6c6f77656460448201527f20746f2063616c6c202773776170272066756e6374696f6e0000000000000000606482015260840160405180910390fd5b5f8573ffffffffffffffffffffffffffffffffffffffff165f6040515f6040518083038185875af1925050503d805f8114610181576040519150601f19603f3d011682016040523d82523d5f602084013e610186565b606091505b505090505061019687878a610484565b6101a188858561054e565b91506101ae87878a610484565b7f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722c80548060200260200160405190810160405280929190818152602001828054801561021757602002820191905f5260205f20905b815481526020019060010190808311610203575b50505050509050965096945050505050565b5f5a6040517f5bbe8b3f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8781166004830152868116602483015260448201869052848116606483015291925090871690635bbe8b3f906084015f604051808303815f87803b1580156102ac575f5ffd5b505af11580156102be573d5f5f3e3d5ffd5b505050505a6102cd90826108e7565b6102d99061116c610900565b7f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722b5f8282546103089190610900565b9091555050505050505050565b5f5a90507f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722c73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff8616146103fe576040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85811660048301528616906370a0823190602401602060405180830381865afa1580156103d5573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103f99190610913565b610417565b8373ffffffffffffffffffffffffffffffffffffffff16315b81546001810183555f928352602090922090910155811561047e575a61043d90826108e7565b6104499061116c610900565b7f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722b5f8282546104789190610900565b90915550505b50505050565b5f5b8281101561047e5730633bbb2e1d8585848181106104a6576104a661092a565b90506020020160208101906104bb9190610957565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff918216600482015290851660248201525f60448201526064015f604051808303815f87803b15801561052c575f5ffd5b505af115801561053e573d5f5f3e3d5ffd5b5050600190920191506104869050565b5f5f5a90506105a984848080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152505073ffffffffffffffffffffffffffffffffffffffff8916929150506105ea565b507f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722b545a6105d790836108e7565b6105e191906108e7565b95945050505050565b60606105f7835f846105fe565b9392505050565b60605f8473ffffffffffffffffffffffffffffffffffffffff1684846040516106279190610972565b5f6040518083038185875af1925050503d805f8114610661576040519150601f19603f3d011682016040523d82523d5f602084013e610666565b606091505b50925090508061067857815160208301fd5b509392505050565b73ffffffffffffffffffffffffffffffffffffffff811681146106a1575f5ffd5b50565b80356106af81610680565b919050565b5f5f83601f8401126106c4575f5ffd5b50813567ffffffffffffffff8111156106db575f5ffd5b6020830191508360208285010111156106f2575f5ffd5b9250929050565b5f5f5f5f5f5f6080878903121561070e575f5ffd5b863561071981610680565b9550602087013567ffffffffffffffff811115610734575f5ffd5b8701601f81018913610744575f5ffd5b803567ffffffffffffffff81111561075a575f5ffd5b8960208260051b840101111561076e575f5ffd5b60209190910195509350610784604088016106a4565b9250606087013567ffffffffffffffff81111561079f575f5ffd5b6107ab89828a016106b4565b979a9699509497509295939492505050565b5f60408201848352604060208401528084518083526060850191506020860192505f5b818110156107fe5783518352602093840193909201916001016107e0565b50909695505050505050565b5f5f5f5f5f60a0868803121561081e575f5ffd5b853561082981610680565b9450602086013561083981610680565b9350604086013561084981610680565b925060608601359150608086013561086081610680565b809150509295509295909350565b5f5f5f60608486031215610880575f5ffd5b833561088b81610680565b9250602084013561089b81610680565b9150604084013580151581146108af575f5ffd5b809150509250925092565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b818103818111156108fa576108fa6108ba565b92915050565b808201808211156108fa576108fa6108ba565b5f60208284031215610923575f5ffd5b5051919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f60208284031215610967575f5ffd5b81356105f781610680565b5f82515f5b818110156109915760208186018101518583015201610977565b505f92019182525091905056fea164736f6c634300081e000a", "devdoc": { "methods": {} }, diff --git a/contracts/artifacts/Trader.json b/contracts/artifacts/Trader.json index 2bb2245e43..7a9cb937ef 100644 --- a/contracts/artifacts/Trader.json +++ b/contracts/artifacts/Trader.json @@ -21,11 +21,6 @@ "name": "sellAmount", "type": "uint256" }, - { - "internalType": "address", - "name": "nativeToken", - "type": "address" - }, { "internalType": "address", "name": "spardose", @@ -89,8 +84,8 @@ "type": "receive" } ], - "bytecode": "0x6080604052348015600e575f5ffd5b50610d008061001c5f395ff3fe608060405260043610610037575f3560e01c80631626ba7e1461008d578063542eb77d14610104578063eb5625d9146101255761003e565b3661003e57005b5f6100835f368080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525062010000939250506101449050565b9050805160208201f35b348015610098575f5ffd5b506100cf6100a7366004610b01565b7f1626ba7e000000000000000000000000000000000000000000000000000000009392505050565b6040517fffffffff00000000000000000000000000000000000000000000000000000000909116815260200160405180910390f35b34801561010f575f5ffd5b5061012361011e366004610b9c565b6101c2565b005b348015610130575f5ffd5b5061012361013f366004610c00565b610916565b60605f8373ffffffffffffffffffffffffffffffffffffffff168360405161016c9190610c3e565b5f60405180830381855af49150503d805f81146101a4576040519150601f19603f3d011682016040523d82523d5f602084013e6101a9565b606091505b5092509050806101bb57815160208301fd5b5092915050565b7f02565dba7d68dcbed629110024b7b5e785bfc1a484602045eea513de8a2dcf99805460017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082161790915560ff16156102a3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f70726570617265537761702063616e206f6e6c792062652063616c6c6564206f60448201527f6e6365000000000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b8173ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16036103e4576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201525f9073ffffffffffffffffffffffffffffffffffffffff8616906370a0823190602401602060405180830381865afa158015610340573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103649190610c6a565b9050838110156103e2575f6103798286610c81565b90508047106103e0578373ffffffffffffffffffffffffffffffffffffffff1663d0e30db0826040518263ffffffff1660e01b81526004015f604051808303818588803b1580156103c8575f5ffd5b505af11580156103da573d5f5f3e3d5ffd5b50505050505b505b505b5f8573ffffffffffffffffffffffffffffffffffffffff16639b552cc26040518163ffffffff1660e01b8152600401602060405180830381865afa15801561042e573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104529190610cb9565b6040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff80831660248301529192505f9187169063dd62ed3e90604401602060405180830381865afa1580156104c7573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104eb9190610c6a565b905084811015610748576040517feb5625d900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8088166004830152831660248201525f6044820152309063eb5625d9906064015f604051808303815f87803b158015610567575f5ffd5b505af1925050508015610578575060015b506040517feb5625d900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8088166004830152831660248201527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6044820152309063eb5625d9906064015f604051808303815f87803b15801561060b575f5ffd5b505af192505050801561061c575060015b506040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff83811660248301525f919088169063dd62ed3e90604401602060405180830381865afa158015610690573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106b49190610c6a565b905085811015610746576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f74726164657220646964206e6f7420676976652074686520726571756972656460448201527f20617070726f76616c7300000000000000000000000000000000000000000000606482015260840161029a565b505b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201525f9073ffffffffffffffffffffffffffffffffffffffff8816906370a0823190602401602060405180830381865afa1580156107b2573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107d69190610c6a565b90508581101561090c5773ffffffffffffffffffffffffffffffffffffffff841663494666b688610807848a610c81565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff909216600483015260248201526044015f604051808303815f87803b15801561086f575f5ffd5b505af1925050508015610880575060015b61090c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f74726164657220646f6573206e6f74206861766520656e6f7567682073656c6c60448201527f20746f6b656e0000000000000000000000000000000000000000000000000000606482015260840161029a565b5050505050505050565b61093773ffffffffffffffffffffffffffffffffffffffff8416838361093c565b505050565b6040805173ffffffffffffffffffffffffffffffffffffffff848116602483015260448083018590528351808403909101815260649092019092526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f095ea7b300000000000000000000000000000000000000000000000000000000179052905f906109ce90861683610a46565b90506109d981610a5a565b610a3f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f5361666545524332303a20617070726f76616c206661696c6564000000000000604482015260640161029a565b5050505050565b6060610a53835f84610a7f565b9392505050565b5f81515f1480610a79575081806020019051810190610a799190610cd4565b92915050565b60605f8473ffffffffffffffffffffffffffffffffffffffff168484604051610aa89190610c3e565b5f6040518083038185875af1925050503d805f8114610ae2576040519150601f19603f3d011682016040523d82523d5f602084013e610ae7565b606091505b509250905080610af957815160208301fd5b509392505050565b5f5f5f60408486031215610b13575f5ffd5b83359250602084013567ffffffffffffffff811115610b30575f5ffd5b8401601f81018613610b40575f5ffd5b803567ffffffffffffffff811115610b56575f5ffd5b866020828401011115610b67575f5ffd5b939660209190910195509293505050565b73ffffffffffffffffffffffffffffffffffffffff81168114610b99575f5ffd5b50565b5f5f5f5f5f60a08688031215610bb0575f5ffd5b8535610bbb81610b78565b94506020860135610bcb81610b78565b9350604086013592506060860135610be281610b78565b91506080860135610bf281610b78565b809150509295509295909350565b5f5f5f60608486031215610c12575f5ffd5b8335610c1d81610b78565b92506020840135610c2d81610b78565b929592945050506040919091013590565b5f82515f5b81811015610c5d5760208186018101518583015201610c43565b505f920191825250919050565b5f60208284031215610c7a575f5ffd5b5051919050565b81810381811115610a79577f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f60208284031215610cc9575f5ffd5b8151610a5381610b78565b5f60208284031215610ce4575f5ffd5b81518015158114610a53575f5ffdfea164736f6c634300081e000a", - "deployedBytecode": "0x608060405260043610610037575f3560e01c80631626ba7e1461008d578063542eb77d14610104578063eb5625d9146101255761003e565b3661003e57005b5f6100835f368080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525062010000939250506101449050565b9050805160208201f35b348015610098575f5ffd5b506100cf6100a7366004610b01565b7f1626ba7e000000000000000000000000000000000000000000000000000000009392505050565b6040517fffffffff00000000000000000000000000000000000000000000000000000000909116815260200160405180910390f35b34801561010f575f5ffd5b5061012361011e366004610b9c565b6101c2565b005b348015610130575f5ffd5b5061012361013f366004610c00565b610916565b60605f8373ffffffffffffffffffffffffffffffffffffffff168360405161016c9190610c3e565b5f60405180830381855af49150503d805f81146101a4576040519150601f19603f3d011682016040523d82523d5f602084013e6101a9565b606091505b5092509050806101bb57815160208301fd5b5092915050565b7f02565dba7d68dcbed629110024b7b5e785bfc1a484602045eea513de8a2dcf99805460017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082161790915560ff16156102a3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f70726570617265537761702063616e206f6e6c792062652063616c6c6564206f60448201527f6e6365000000000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b8173ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16036103e4576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201525f9073ffffffffffffffffffffffffffffffffffffffff8616906370a0823190602401602060405180830381865afa158015610340573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103649190610c6a565b9050838110156103e2575f6103798286610c81565b90508047106103e0578373ffffffffffffffffffffffffffffffffffffffff1663d0e30db0826040518263ffffffff1660e01b81526004015f604051808303818588803b1580156103c8575f5ffd5b505af11580156103da573d5f5f3e3d5ffd5b50505050505b505b505b5f8573ffffffffffffffffffffffffffffffffffffffff16639b552cc26040518163ffffffff1660e01b8152600401602060405180830381865afa15801561042e573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104529190610cb9565b6040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff80831660248301529192505f9187169063dd62ed3e90604401602060405180830381865afa1580156104c7573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104eb9190610c6a565b905084811015610748576040517feb5625d900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8088166004830152831660248201525f6044820152309063eb5625d9906064015f604051808303815f87803b158015610567575f5ffd5b505af1925050508015610578575060015b506040517feb5625d900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8088166004830152831660248201527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6044820152309063eb5625d9906064015f604051808303815f87803b15801561060b575f5ffd5b505af192505050801561061c575060015b506040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff83811660248301525f919088169063dd62ed3e90604401602060405180830381865afa158015610690573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106b49190610c6a565b905085811015610746576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f74726164657220646964206e6f7420676976652074686520726571756972656460448201527f20617070726f76616c7300000000000000000000000000000000000000000000606482015260840161029a565b505b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201525f9073ffffffffffffffffffffffffffffffffffffffff8816906370a0823190602401602060405180830381865afa1580156107b2573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107d69190610c6a565b90508581101561090c5773ffffffffffffffffffffffffffffffffffffffff841663494666b688610807848a610c81565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff909216600483015260248201526044015f604051808303815f87803b15801561086f575f5ffd5b505af1925050508015610880575060015b61090c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f74726164657220646f6573206e6f74206861766520656e6f7567682073656c6c60448201527f20746f6b656e0000000000000000000000000000000000000000000000000000606482015260840161029a565b5050505050505050565b61093773ffffffffffffffffffffffffffffffffffffffff8416838361093c565b505050565b6040805173ffffffffffffffffffffffffffffffffffffffff848116602483015260448083018590528351808403909101815260649092019092526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f095ea7b300000000000000000000000000000000000000000000000000000000179052905f906109ce90861683610a46565b90506109d981610a5a565b610a3f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f5361666545524332303a20617070726f76616c206661696c6564000000000000604482015260640161029a565b5050505050565b6060610a53835f84610a7f565b9392505050565b5f81515f1480610a79575081806020019051810190610a799190610cd4565b92915050565b60605f8473ffffffffffffffffffffffffffffffffffffffff168484604051610aa89190610c3e565b5f6040518083038185875af1925050503d805f8114610ae2576040519150601f19603f3d011682016040523d82523d5f602084013e610ae7565b606091505b509250905080610af957815160208301fd5b509392505050565b5f5f5f60408486031215610b13575f5ffd5b83359250602084013567ffffffffffffffff811115610b30575f5ffd5b8401601f81018613610b40575f5ffd5b803567ffffffffffffffff811115610b56575f5ffd5b866020828401011115610b67575f5ffd5b939660209190910195509293505050565b73ffffffffffffffffffffffffffffffffffffffff81168114610b99575f5ffd5b50565b5f5f5f5f5f60a08688031215610bb0575f5ffd5b8535610bbb81610b78565b94506020860135610bcb81610b78565b9350604086013592506060860135610be281610b78565b91506080860135610bf281610b78565b809150509295509295909350565b5f5f5f60608486031215610c12575f5ffd5b8335610c1d81610b78565b92506020840135610c2d81610b78565b929592945050506040919091013590565b5f82515f5b81811015610c5d5760208186018101518583015201610c43565b505f920191825250919050565b5f60208284031215610c7a575f5ffd5b5051919050565b81810381811115610a79577f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f60208284031215610cc9575f5ffd5b8151610a5381610b78565b5f60208284031215610ce4575f5ffd5b81518015158114610a53575f5ffdfea164736f6c634300081e000a", + "bytecode": "0x6080604052348015600e575f5ffd5b50610baa8061001c5f395ff3fe608060405260043610610037575f3560e01c80631626ba7e1461008d5780635bbe8b3f14610104578063eb5625d9146101255761003e565b3661003e57005b5f6100835f368080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525062010000939250506101449050565b9050805160208201f35b348015610098575f5ffd5b506100cf6100a73660046109bf565b7f1626ba7e000000000000000000000000000000000000000000000000000000009392505050565b6040517fffffffff00000000000000000000000000000000000000000000000000000000909116815260200160405180910390f35b34801561010f575f5ffd5b5061012361011e366004610a5a565b6101c2565b005b348015610130575f5ffd5b5061012361013f366004610aaa565b6107d4565b60605f8373ffffffffffffffffffffffffffffffffffffffff168360405161016c9190610ae8565b5f60405180830381855af49150503d805f81146101a4576040519150601f19603f3d011682016040523d82523d5f602084013e6101a9565b606091505b5092509050806101bb57815160208301fd5b5092915050565b7f02565dba7d68dcbed629110024b7b5e785bfc1a484602045eea513de8a2dcf99805460017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082161790915560ff16156102a3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f70726570617265537761702063616e206f6e6c792062652063616c6c6564206f60448201527f6e6365000000000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b5f8473ffffffffffffffffffffffffffffffffffffffff16639b552cc26040518163ffffffff1660e01b8152600401602060405180830381865afa1580156102ed573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103119190610b14565b6040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff80831660248301529192505f9186169063dd62ed3e90604401602060405180830381865afa158015610386573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103aa9190610b2f565b905083811015610607576040517feb5625d900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8087166004830152831660248201525f6044820152309063eb5625d9906064015f604051808303815f87803b158015610426575f5ffd5b505af1925050508015610437575060015b506040517feb5625d900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8087166004830152831660248201527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6044820152309063eb5625d9906064015f604051808303815f87803b1580156104ca575f5ffd5b505af19250505080156104db575060015b506040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff83811660248301525f919087169063dd62ed3e90604401602060405180830381865afa15801561054f573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105739190610b2f565b905084811015610605576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f74726164657220646964206e6f7420676976652074686520726571756972656460448201527f20617070726f76616c7300000000000000000000000000000000000000000000606482015260840161029a565b505b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201525f9073ffffffffffffffffffffffffffffffffffffffff8716906370a0823190602401602060405180830381865afa158015610671573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106959190610b2f565b9050848110156107cb5773ffffffffffffffffffffffffffffffffffffffff841663494666b6876106c68489610b46565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff909216600483015260248201526044015f604051808303815f87803b15801561072e575f5ffd5b505af192505050801561073f575060015b6107cb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f74726164657220646f6573206e6f74206861766520656e6f7567682073656c6c60448201527f20746f6b656e0000000000000000000000000000000000000000000000000000606482015260840161029a565b50505050505050565b6107f573ffffffffffffffffffffffffffffffffffffffff841683836107fa565b505050565b6040805173ffffffffffffffffffffffffffffffffffffffff848116602483015260448083018590528351808403909101815260649092019092526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f095ea7b300000000000000000000000000000000000000000000000000000000179052905f9061088c90861683610904565b905061089781610918565b6108fd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f5361666545524332303a20617070726f76616c206661696c6564000000000000604482015260640161029a565b5050505050565b6060610911835f8461093d565b9392505050565b5f81515f14806109375750818060200190518101906109379190610b7e565b92915050565b60605f8473ffffffffffffffffffffffffffffffffffffffff1684846040516109669190610ae8565b5f6040518083038185875af1925050503d805f81146109a0576040519150601f19603f3d011682016040523d82523d5f602084013e6109a5565b606091505b5092509050806109b757815160208301fd5b509392505050565b5f5f5f604084860312156109d1575f5ffd5b83359250602084013567ffffffffffffffff8111156109ee575f5ffd5b8401601f810186136109fe575f5ffd5b803567ffffffffffffffff811115610a14575f5ffd5b866020828401011115610a25575f5ffd5b939660209190910195509293505050565b73ffffffffffffffffffffffffffffffffffffffff81168114610a57575f5ffd5b50565b5f5f5f5f60808587031215610a6d575f5ffd5b8435610a7881610a36565b93506020850135610a8881610a36565b9250604085013591506060850135610a9f81610a36565b939692955090935050565b5f5f5f60608486031215610abc575f5ffd5b8335610ac781610a36565b92506020840135610ad781610a36565b929592945050506040919091013590565b5f82515f5b81811015610b075760208186018101518583015201610aed565b505f920191825250919050565b5f60208284031215610b24575f5ffd5b815161091181610a36565b5f60208284031215610b3f575f5ffd5b5051919050565b81810381811115610937577f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f60208284031215610b8e575f5ffd5b81518015158114610911575f5ffdfea164736f6c634300081e000a", + "deployedBytecode": "0x608060405260043610610037575f3560e01c80631626ba7e1461008d5780635bbe8b3f14610104578063eb5625d9146101255761003e565b3661003e57005b5f6100835f368080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525062010000939250506101449050565b9050805160208201f35b348015610098575f5ffd5b506100cf6100a73660046109bf565b7f1626ba7e000000000000000000000000000000000000000000000000000000009392505050565b6040517fffffffff00000000000000000000000000000000000000000000000000000000909116815260200160405180910390f35b34801561010f575f5ffd5b5061012361011e366004610a5a565b6101c2565b005b348015610130575f5ffd5b5061012361013f366004610aaa565b6107d4565b60605f8373ffffffffffffffffffffffffffffffffffffffff168360405161016c9190610ae8565b5f60405180830381855af49150503d805f81146101a4576040519150601f19603f3d011682016040523d82523d5f602084013e6101a9565b606091505b5092509050806101bb57815160208301fd5b5092915050565b7f02565dba7d68dcbed629110024b7b5e785bfc1a484602045eea513de8a2dcf99805460017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082161790915560ff16156102a3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f70726570617265537761702063616e206f6e6c792062652063616c6c6564206f60448201527f6e6365000000000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b5f8473ffffffffffffffffffffffffffffffffffffffff16639b552cc26040518163ffffffff1660e01b8152600401602060405180830381865afa1580156102ed573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103119190610b14565b6040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff80831660248301529192505f9186169063dd62ed3e90604401602060405180830381865afa158015610386573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103aa9190610b2f565b905083811015610607576040517feb5625d900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8087166004830152831660248201525f6044820152309063eb5625d9906064015f604051808303815f87803b158015610426575f5ffd5b505af1925050508015610437575060015b506040517feb5625d900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8087166004830152831660248201527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6044820152309063eb5625d9906064015f604051808303815f87803b1580156104ca575f5ffd5b505af19250505080156104db575060015b506040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff83811660248301525f919087169063dd62ed3e90604401602060405180830381865afa15801561054f573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105739190610b2f565b905084811015610605576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f74726164657220646964206e6f7420676976652074686520726571756972656460448201527f20617070726f76616c7300000000000000000000000000000000000000000000606482015260840161029a565b505b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201525f9073ffffffffffffffffffffffffffffffffffffffff8716906370a0823190602401602060405180830381865afa158015610671573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106959190610b2f565b9050848110156107cb5773ffffffffffffffffffffffffffffffffffffffff841663494666b6876106c68489610b46565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff909216600483015260248201526044015f604051808303815f87803b15801561072e575f5ffd5b505af192505050801561073f575060015b6107cb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f74726164657220646f6573206e6f74206861766520656e6f7567682073656c6c60448201527f20746f6b656e0000000000000000000000000000000000000000000000000000606482015260840161029a565b50505050505050565b6107f573ffffffffffffffffffffffffffffffffffffffff841683836107fa565b505050565b6040805173ffffffffffffffffffffffffffffffffffffffff848116602483015260448083018590528351808403909101815260649092019092526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f095ea7b300000000000000000000000000000000000000000000000000000000179052905f9061088c90861683610904565b905061089781610918565b6108fd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f5361666545524332303a20617070726f76616c206661696c6564000000000000604482015260640161029a565b5050505050565b6060610911835f8461093d565b9392505050565b5f81515f14806109375750818060200190518101906109379190610b7e565b92915050565b60605f8473ffffffffffffffffffffffffffffffffffffffff1684846040516109669190610ae8565b5f6040518083038185875af1925050503d805f81146109a0576040519150601f19603f3d011682016040523d82523d5f602084013e6109a5565b606091505b5092509050806109b757815160208301fd5b509392505050565b5f5f5f604084860312156109d1575f5ffd5b83359250602084013567ffffffffffffffff8111156109ee575f5ffd5b8401601f810186136109fe575f5ffd5b803567ffffffffffffffff811115610a14575f5ffd5b866020828401011115610a25575f5ffd5b939660209190910195509293505050565b73ffffffffffffffffffffffffffffffffffffffff81168114610a57575f5ffd5b50565b5f5f5f5f60808587031215610a6d575f5ffd5b8435610a7881610a36565b93506020850135610a8881610a36565b9250604085013591506060850135610a9f81610a36565b939692955090935050565b5f5f5f60608486031215610abc575f5ffd5b8335610ac781610a36565b92506020840135610ad781610a36565b929592945050506040919091013590565b5f82515f5b81811015610b075760208186018101518583015201610aed565b505f920191825250919050565b5f60208284031215610b24575f5ffd5b815161091181610a36565b5f60208284031215610b3f575f5ffd5b5051919050565b81810381811115610937577f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f60208284031215610b8e575f5ffd5b81518015158114610911575f5ffdfea164736f6c634300081e000a", "devdoc": { "methods": {} }, diff --git a/contracts/generated/contracts-generated/solver/src/lib.rs b/contracts/generated/contracts-generated/solver/src/lib.rs index cd27b738be..fbfe9aad21 100644 --- a/contracts/generated/contracts-generated/solver/src/lib.rs +++ b/contracts/generated/contracts-generated/solver/src/lib.rs @@ -11,7 +11,7 @@ Generated by the following Solidity interface... ```solidity interface Solver { - function ensureTradePreconditions(address trader, address settlementContract, address sellToken, uint256 sellAmount, address nativeToken, address spardose) external; + function ensureTradePreconditions(address trader, address settlementContract, address sellToken, uint256 sellAmount, address spardose) external; function storeBalance(address token, address owner, bool countGas) external; function swap(address settlementContract, address[] memory tokens, address payable receiver, bytes memory settlementCall) external returns (uint256 gasUsed, uint256[] memory queriedBalances); } @@ -44,11 +44,6 @@ interface Solver { "type": "uint256", "internalType": "uint256" }, - { - "name": "nativeToken", - "type": "address", - "internalType": "address" - }, { "name": "spardose", "type": "address", @@ -134,27 +129,27 @@ pub mod Solver { /// The creation / init bytecode of the contract. /// /// ```text - ///0x6080604052348015600e575f5ffd5b506109c58061001c5f395ff3fe608060405234801561000f575f5ffd5b506004361061003f575f3560e01c80631d47e7f4146100435780632582edb41461006d5780633bbb2e1d14610082575b5f5ffd5b610056610051366004610702565b610095565b6040516100649291906107c6565b60405180910390f35b61008061007b366004610813565b610229565b005b610080610090366004610888565b61031e565b5f606033301461012b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f6f6e6c792073696d756c6174696f6e206c6f67696320697320616c6c6f77656460448201527f20746f2063616c6c202773776170272066756e6374696f6e0000000000000000606482015260840160405180910390fd5b5f8573ffffffffffffffffffffffffffffffffffffffff165f6040515f6040518083038185875af1925050503d805f8114610181576040519150601f19603f3d011682016040523d82523d5f602084013e610186565b606091505b505090505061019687878a61048d565b6101a1888585610557565b91506101ae87878a61048d565b7f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722c80548060200260200160405190810160405280929190818152602001828054801561021757602002820191905f5260205f20905b815481526020019060010190808311610203575b50505050509050965096945050505050565b5f5a6040517f542eb77d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8881166004830152878116602483015260448201879052858116606483015284811660848301529192509088169063542eb77d9060a4015f604051808303815f87803b1580156102b4575f5ffd5b505af11580156102c6573d5f5f3e3d5ffd5b505050505a6102d59082610901565b6102e19061116c61091a565b7f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722b5f828254610310919061091a565b909155505050505050505050565b5f5a90507f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722c73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff861614610407576040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85811660048301528616906370a0823190602401602060405180830381865afa1580156103de573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610402919061092d565b610420565b8373ffffffffffffffffffffffffffffffffffffffff16315b81546001810183555f9283526020909220909101558115610487575a6104469082610901565b6104529061116c61091a565b7f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722b5f828254610481919061091a565b90915550505b50505050565b5f5b828110156104875730633bbb2e1d8585848181106104af576104af610944565b90506020020160208101906104c49190610971565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff918216600482015290851660248201525f60448201526064015f604051808303815f87803b158015610535575f5ffd5b505af1158015610547573d5f5f3e3d5ffd5b50506001909201915061048f9050565b5f5f5a90506105b284848080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152505073ffffffffffffffffffffffffffffffffffffffff8916929150506105f3565b507f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722b545a6105e09083610901565b6105ea9190610901565b95945050505050565b6060610600835f84610607565b9392505050565b60605f8473ffffffffffffffffffffffffffffffffffffffff168484604051610630919061098c565b5f6040518083038185875af1925050503d805f811461066a576040519150601f19603f3d011682016040523d82523d5f602084013e61066f565b606091505b50925090508061068157815160208301fd5b509392505050565b73ffffffffffffffffffffffffffffffffffffffff811681146106aa575f5ffd5b50565b80356106b881610689565b919050565b5f5f83601f8401126106cd575f5ffd5b50813567ffffffffffffffff8111156106e4575f5ffd5b6020830191508360208285010111156106fb575f5ffd5b9250929050565b5f5f5f5f5f5f60808789031215610717575f5ffd5b863561072281610689565b9550602087013567ffffffffffffffff81111561073d575f5ffd5b8701601f8101891361074d575f5ffd5b803567ffffffffffffffff811115610763575f5ffd5b8960208260051b8401011115610777575f5ffd5b6020919091019550935061078d604088016106ad565b9250606087013567ffffffffffffffff8111156107a8575f5ffd5b6107b489828a016106bd565b979a9699509497509295939492505050565b5f60408201848352604060208401528084518083526060850191506020860192505f5b818110156108075783518352602093840193909201916001016107e9565b50909695505050505050565b5f5f5f5f5f5f60c08789031215610828575f5ffd5b863561083381610689565b9550602087013561084381610689565b9450604087013561085381610689565b935060608701359250608087013561086a81610689565b915060a087013561087a81610689565b809150509295509295509295565b5f5f5f6060848603121561089a575f5ffd5b83356108a581610689565b925060208401356108b581610689565b9150604084013580151581146108c9575f5ffd5b809150509250925092565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b81810381811115610914576109146108d4565b92915050565b80820180821115610914576109146108d4565b5f6020828403121561093d575f5ffd5b5051919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f60208284031215610981575f5ffd5b813561060081610689565b5f82515f5b818110156109ab5760208186018101518583015201610991565b505f92019182525091905056fea164736f6c634300081e000a + ///0x6080604052348015600e575f5ffd5b506109ab8061001c5f395ff3fe608060405234801561000f575f5ffd5b506004361061003f575f3560e01c80631d47e7f4146100435780632ff111f51461006d5780633bbb2e1d14610082575b5f5ffd5b6100566100513660046106f9565b610095565b6040516100649291906107bd565b60405180910390f35b61008061007b36600461080a565b610229565b005b61008061009036600461086e565b610315565b5f606033301461012b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f6f6e6c792073696d756c6174696f6e206c6f67696320697320616c6c6f77656460448201527f20746f2063616c6c202773776170272066756e6374696f6e0000000000000000606482015260840160405180910390fd5b5f8573ffffffffffffffffffffffffffffffffffffffff165f6040515f6040518083038185875af1925050503d805f8114610181576040519150601f19603f3d011682016040523d82523d5f602084013e610186565b606091505b505090505061019687878a610484565b6101a188858561054e565b91506101ae87878a610484565b7f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722c80548060200260200160405190810160405280929190818152602001828054801561021757602002820191905f5260205f20905b815481526020019060010190808311610203575b50505050509050965096945050505050565b5f5a6040517f5bbe8b3f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8781166004830152868116602483015260448201869052848116606483015291925090871690635bbe8b3f906084015f604051808303815f87803b1580156102ac575f5ffd5b505af11580156102be573d5f5f3e3d5ffd5b505050505a6102cd90826108e7565b6102d99061116c610900565b7f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722b5f8282546103089190610900565b9091555050505050505050565b5f5a90507f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722c73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff8616146103fe576040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85811660048301528616906370a0823190602401602060405180830381865afa1580156103d5573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103f99190610913565b610417565b8373ffffffffffffffffffffffffffffffffffffffff16315b81546001810183555f928352602090922090910155811561047e575a61043d90826108e7565b6104499061116c610900565b7f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722b5f8282546104789190610900565b90915550505b50505050565b5f5b8281101561047e5730633bbb2e1d8585848181106104a6576104a661092a565b90506020020160208101906104bb9190610957565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff918216600482015290851660248201525f60448201526064015f604051808303815f87803b15801561052c575f5ffd5b505af115801561053e573d5f5f3e3d5ffd5b5050600190920191506104869050565b5f5f5a90506105a984848080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152505073ffffffffffffffffffffffffffffffffffffffff8916929150506105ea565b507f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722b545a6105d790836108e7565b6105e191906108e7565b95945050505050565b60606105f7835f846105fe565b9392505050565b60605f8473ffffffffffffffffffffffffffffffffffffffff1684846040516106279190610972565b5f6040518083038185875af1925050503d805f8114610661576040519150601f19603f3d011682016040523d82523d5f602084013e610666565b606091505b50925090508061067857815160208301fd5b509392505050565b73ffffffffffffffffffffffffffffffffffffffff811681146106a1575f5ffd5b50565b80356106af81610680565b919050565b5f5f83601f8401126106c4575f5ffd5b50813567ffffffffffffffff8111156106db575f5ffd5b6020830191508360208285010111156106f2575f5ffd5b9250929050565b5f5f5f5f5f5f6080878903121561070e575f5ffd5b863561071981610680565b9550602087013567ffffffffffffffff811115610734575f5ffd5b8701601f81018913610744575f5ffd5b803567ffffffffffffffff81111561075a575f5ffd5b8960208260051b840101111561076e575f5ffd5b60209190910195509350610784604088016106a4565b9250606087013567ffffffffffffffff81111561079f575f5ffd5b6107ab89828a016106b4565b979a9699509497509295939492505050565b5f60408201848352604060208401528084518083526060850191506020860192505f5b818110156107fe5783518352602093840193909201916001016107e0565b50909695505050505050565b5f5f5f5f5f60a0868803121561081e575f5ffd5b853561082981610680565b9450602086013561083981610680565b9350604086013561084981610680565b925060608601359150608086013561086081610680565b809150509295509295909350565b5f5f5f60608486031215610880575f5ffd5b833561088b81610680565b9250602084013561089b81610680565b9150604084013580151581146108af575f5ffd5b809150509250925092565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b818103818111156108fa576108fa6108ba565b92915050565b808201808211156108fa576108fa6108ba565b5f60208284031215610923575f5ffd5b5051919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f60208284031215610967575f5ffd5b81356105f781610680565b5f82515f5b818110156109915760208186018101518583015201610977565b505f92019182525091905056fea164736f6c634300081e000a /// ``` #[rustfmt::skip] #[allow(clippy::all)] pub static BYTECODE: alloy_sol_types::private::Bytes = alloy_sol_types::private::Bytes::from_static( - b"`\x80`@R4\x80\x15`\x0EW__\xFD[Pa\t\xC5\x80a\0\x1C_9_\xF3\xFE`\x80`@R4\x80\x15a\0\x0FW__\xFD[P`\x046\x10a\0?W_5`\xE0\x1C\x80c\x1DG\xE7\xF4\x14a\0CW\x80c%\x82\xED\xB4\x14a\0mW\x80c;\xBB.\x1D\x14a\0\x82W[__\xFD[a\0Va\0Q6`\x04a\x07\x02V[a\0\x95V[`@Qa\0d\x92\x91\x90a\x07\xC6V[`@Q\x80\x91\x03\x90\xF3[a\0\x80a\0{6`\x04a\x08\x13V[a\x02)V[\0[a\0\x80a\0\x906`\x04a\x08\x88V[a\x03\x1EV[_``30\x14a\x01+W`@Q\x7F\x08\xC3y\xA0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R` `\x04\x82\x01R`8`$\x82\x01R\x7Fonly simulation logic is allowed`D\x82\x01R\x7F to call 'swap' function\0\0\0\0\0\0\0\0`d\x82\x01R`\x84\x01`@Q\x80\x91\x03\x90\xFD[_\x85s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16_`@Q_`@Q\x80\x83\x03\x81\x85\x87Z\xF1\x92PPP=\x80_\x81\x14a\x01\x81W`@Q\x91P`\x1F\x19`?=\x01\x16\x82\x01`@R=\x82R=_` \x84\x01>a\x01\x86V[``\x91P[PP\x90PPa\x01\x96\x87\x87\x8Aa\x04\x8DV[a\x01\xA1\x88\x85\x85a\x05WV[\x91Pa\x01\xAE\x87\x87\x8Aa\x04\x8DV[\x7F\x14\xF5\xB2\xC1\x85\xFC\x03\xC7\\x}\x1F\x0E\x10\xEA\x13|\xC6\xD25\xA0\x04tH\xEF\xF1\x8C\x9A\x17:r,\x80T\x80` \x02` \x01`@Q\x90\x81\x01`@R\x80\x92\x91\x90\x81\x81R` \x01\x82\x80T\x80\x15a\x02\x17W` \x02\x82\x01\x91\x90_R` _ \x90[\x81T\x81R` \x01\x90`\x01\x01\x90\x80\x83\x11a\x02\x03W[PPPPP\x90P\x96P\x96\x94PPPPPV[_Z`@Q\x7FT.\xB7}\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81Rs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x88\x81\x16`\x04\x83\x01R\x87\x81\x16`$\x83\x01R`D\x82\x01\x87\x90R\x85\x81\x16`d\x83\x01R\x84\x81\x16`\x84\x83\x01R\x91\x92P\x90\x88\x16\x90cT.\xB7}\x90`\xA4\x01_`@Q\x80\x83\x03\x81_\x87\x80;\x15\x80\x15a\x02\xB4W__\xFD[PZ\xF1\x15\x80\x15a\x02\xC6W=__>=_\xFD[PPPPZa\x02\xD5\x90\x82a\t\x01V[a\x02\xE1\x90a\x11la\t\x1AV[\x7F\x14\xF5\xB2\xC1\x85\xFC\x03\xC7\\x}\x1F\x0E\x10\xEA\x13|\xC6\xD25\xA0\x04tH\xEF\xF1\x8C\x9A\x17:r+_\x82\x82Ta\x03\x10\x91\x90a\t\x1AV[\x90\x91UPPPPPPPPPV[_Z\x90P\x7F\x14\xF5\xB2\xC1\x85\xFC\x03\xC7\\x}\x1F\x0E\x10\xEA\x13|\xC6\xD25\xA0\x04tH\xEF\xF1\x8C\x9A\x17:r,s\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEEs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x86\x16\x14a\x04\x07W`@Q\x7Fp\xA0\x821\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81Rs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x85\x81\x16`\x04\x83\x01R\x86\x16\x90cp\xA0\x821\x90`$\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x03\xDEW=__>=_\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x04\x02\x91\x90a\t-V[a\x04 V[\x83s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x161[\x81T`\x01\x81\x01\x83U_\x92\x83R` \x90\x92 \x90\x91\x01U\x81\x15a\x04\x87WZa\x04F\x90\x82a\t\x01V[a\x04R\x90a\x11la\t\x1AV[\x7F\x14\xF5\xB2\xC1\x85\xFC\x03\xC7\\x}\x1F\x0E\x10\xEA\x13|\xC6\xD25\xA0\x04tH\xEF\xF1\x8C\x9A\x17:r+_\x82\x82Ta\x04\x81\x91\x90a\t\x1AV[\x90\x91UPP[PPPPV[_[\x82\x81\x10\x15a\x04\x87W0c;\xBB.\x1D\x85\x85\x84\x81\x81\x10a\x04\xAFWa\x04\xAFa\tDV[\x90P` \x02\x01` \x81\x01\x90a\x04\xC4\x91\x90a\tqV[`@Q\x7F\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\xE0\x84\x90\x1B\x16\x81Rs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x91\x82\x16`\x04\x82\x01R\x90\x85\x16`$\x82\x01R_`D\x82\x01R`d\x01_`@Q\x80\x83\x03\x81_\x87\x80;\x15\x80\x15a\x055W__\xFD[PZ\xF1\x15\x80\x15a\x05GW=__>=_\xFD[PP`\x01\x90\x92\x01\x91Pa\x04\x8F\x90PV[__Z\x90Pa\x05\xB2\x84\x84\x80\x80`\x1F\x01` \x80\x91\x04\x02` \x01`@Q\x90\x81\x01`@R\x80\x93\x92\x91\x90\x81\x81R` \x01\x83\x83\x80\x82\x847_\x92\x01\x91\x90\x91RPPs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x89\x16\x92\x91PPa\x05\xF3V[P\x7F\x14\xF5\xB2\xC1\x85\xFC\x03\xC7\\x}\x1F\x0E\x10\xEA\x13|\xC6\xD25\xA0\x04tH\xEF\xF1\x8C\x9A\x17:r+TZa\x05\xE0\x90\x83a\t\x01V[a\x05\xEA\x91\x90a\t\x01V[\x95\x94PPPPPV[``a\x06\0\x83_\x84a\x06\x07V[\x93\x92PPPV[``_\x84s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x84\x84`@Qa\x060\x91\x90a\t\x8CV[_`@Q\x80\x83\x03\x81\x85\x87Z\xF1\x92PPP=\x80_\x81\x14a\x06jW`@Q\x91P`\x1F\x19`?=\x01\x16\x82\x01`@R=\x82R=_` \x84\x01>a\x06oV[``\x91P[P\x92P\x90P\x80a\x06\x81W\x81Q` \x83\x01\xFD[P\x93\x92PPPV[s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x16\x81\x14a\x06\xAAW__\xFD[PV[\x805a\x06\xB8\x81a\x06\x89V[\x91\x90PV[__\x83`\x1F\x84\x01\x12a\x06\xCDW__\xFD[P\x815g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a\x06\xE4W__\xFD[` \x83\x01\x91P\x83` \x82\x85\x01\x01\x11\x15a\x06\xFBW__\xFD[\x92P\x92\x90PV[______`\x80\x87\x89\x03\x12\x15a\x07\x17W__\xFD[\x865a\x07\"\x81a\x06\x89V[\x95P` \x87\x015g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a\x07=W__\xFD[\x87\x01`\x1F\x81\x01\x89\x13a\x07MW__\xFD[\x805g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a\x07cW__\xFD[\x89` \x82`\x05\x1B\x84\x01\x01\x11\x15a\x07wW__\xFD[` \x91\x90\x91\x01\x95P\x93Pa\x07\x8D`@\x88\x01a\x06\xADV[\x92P``\x87\x015g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a\x07\xA8W__\xFD[a\x07\xB4\x89\x82\x8A\x01a\x06\xBDV[\x97\x9A\x96\x99P\x94\x97P\x92\x95\x93\x94\x92PPPV[_`@\x82\x01\x84\x83R`@` \x84\x01R\x80\x84Q\x80\x83R``\x85\x01\x91P` \x86\x01\x92P_[\x81\x81\x10\x15a\x08\x07W\x83Q\x83R` \x93\x84\x01\x93\x90\x92\x01\x91`\x01\x01a\x07\xE9V[P\x90\x96\x95PPPPPPV[______`\xC0\x87\x89\x03\x12\x15a\x08(W__\xFD[\x865a\x083\x81a\x06\x89V[\x95P` \x87\x015a\x08C\x81a\x06\x89V[\x94P`@\x87\x015a\x08S\x81a\x06\x89V[\x93P``\x87\x015\x92P`\x80\x87\x015a\x08j\x81a\x06\x89V[\x91P`\xA0\x87\x015a\x08z\x81a\x06\x89V[\x80\x91PP\x92\x95P\x92\x95P\x92\x95V[___``\x84\x86\x03\x12\x15a\x08\x9AW__\xFD[\x835a\x08\xA5\x81a\x06\x89V[\x92P` \x84\x015a\x08\xB5\x81a\x06\x89V[\x91P`@\x84\x015\x80\x15\x15\x81\x14a\x08\xC9W__\xFD[\x80\x91PP\x92P\x92P\x92V[\x7FNH{q\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0_R`\x11`\x04R`$_\xFD[\x81\x81\x03\x81\x81\x11\x15a\t\x14Wa\t\x14a\x08\xD4V[\x92\x91PPV[\x80\x82\x01\x80\x82\x11\x15a\t\x14Wa\t\x14a\x08\xD4V[_` \x82\x84\x03\x12\x15a\t=W__\xFD[PQ\x91\x90PV[\x7FNH{q\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0_R`2`\x04R`$_\xFD[_` \x82\x84\x03\x12\x15a\t\x81W__\xFD[\x815a\x06\0\x81a\x06\x89V[_\x82Q_[\x81\x81\x10\x15a\t\xABW` \x81\x86\x01\x81\x01Q\x85\x83\x01R\x01a\t\x91V[P_\x92\x01\x91\x82RP\x91\x90PV\xFE\xA1dsolcC\0\x08\x1E\0\n", + b"`\x80`@R4\x80\x15`\x0EW__\xFD[Pa\t\xAB\x80a\0\x1C_9_\xF3\xFE`\x80`@R4\x80\x15a\0\x0FW__\xFD[P`\x046\x10a\0?W_5`\xE0\x1C\x80c\x1DG\xE7\xF4\x14a\0CW\x80c/\xF1\x11\xF5\x14a\0mW\x80c;\xBB.\x1D\x14a\0\x82W[__\xFD[a\0Va\0Q6`\x04a\x06\xF9V[a\0\x95V[`@Qa\0d\x92\x91\x90a\x07\xBDV[`@Q\x80\x91\x03\x90\xF3[a\0\x80a\0{6`\x04a\x08\nV[a\x02)V[\0[a\0\x80a\0\x906`\x04a\x08nV[a\x03\x15V[_``30\x14a\x01+W`@Q\x7F\x08\xC3y\xA0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R` `\x04\x82\x01R`8`$\x82\x01R\x7Fonly simulation logic is allowed`D\x82\x01R\x7F to call 'swap' function\0\0\0\0\0\0\0\0`d\x82\x01R`\x84\x01`@Q\x80\x91\x03\x90\xFD[_\x85s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16_`@Q_`@Q\x80\x83\x03\x81\x85\x87Z\xF1\x92PPP=\x80_\x81\x14a\x01\x81W`@Q\x91P`\x1F\x19`?=\x01\x16\x82\x01`@R=\x82R=_` \x84\x01>a\x01\x86V[``\x91P[PP\x90PPa\x01\x96\x87\x87\x8Aa\x04\x84V[a\x01\xA1\x88\x85\x85a\x05NV[\x91Pa\x01\xAE\x87\x87\x8Aa\x04\x84V[\x7F\x14\xF5\xB2\xC1\x85\xFC\x03\xC7\\x}\x1F\x0E\x10\xEA\x13|\xC6\xD25\xA0\x04tH\xEF\xF1\x8C\x9A\x17:r,\x80T\x80` \x02` \x01`@Q\x90\x81\x01`@R\x80\x92\x91\x90\x81\x81R` \x01\x82\x80T\x80\x15a\x02\x17W` \x02\x82\x01\x91\x90_R` _ \x90[\x81T\x81R` \x01\x90`\x01\x01\x90\x80\x83\x11a\x02\x03W[PPPPP\x90P\x96P\x96\x94PPPPPV[_Z`@Q\x7F[\xBE\x8B?\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81Rs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x87\x81\x16`\x04\x83\x01R\x86\x81\x16`$\x83\x01R`D\x82\x01\x86\x90R\x84\x81\x16`d\x83\x01R\x91\x92P\x90\x87\x16\x90c[\xBE\x8B?\x90`\x84\x01_`@Q\x80\x83\x03\x81_\x87\x80;\x15\x80\x15a\x02\xACW__\xFD[PZ\xF1\x15\x80\x15a\x02\xBEW=__>=_\xFD[PPPPZa\x02\xCD\x90\x82a\x08\xE7V[a\x02\xD9\x90a\x11la\t\0V[\x7F\x14\xF5\xB2\xC1\x85\xFC\x03\xC7\\x}\x1F\x0E\x10\xEA\x13|\xC6\xD25\xA0\x04tH\xEF\xF1\x8C\x9A\x17:r+_\x82\x82Ta\x03\x08\x91\x90a\t\0V[\x90\x91UPPPPPPPPV[_Z\x90P\x7F\x14\xF5\xB2\xC1\x85\xFC\x03\xC7\\x}\x1F\x0E\x10\xEA\x13|\xC6\xD25\xA0\x04tH\xEF\xF1\x8C\x9A\x17:r,s\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEEs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x86\x16\x14a\x03\xFEW`@Q\x7Fp\xA0\x821\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81Rs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x85\x81\x16`\x04\x83\x01R\x86\x16\x90cp\xA0\x821\x90`$\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x03\xD5W=__>=_\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x03\xF9\x91\x90a\t\x13V[a\x04\x17V[\x83s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x161[\x81T`\x01\x81\x01\x83U_\x92\x83R` \x90\x92 \x90\x91\x01U\x81\x15a\x04~WZa\x04=\x90\x82a\x08\xE7V[a\x04I\x90a\x11la\t\0V[\x7F\x14\xF5\xB2\xC1\x85\xFC\x03\xC7\\x}\x1F\x0E\x10\xEA\x13|\xC6\xD25\xA0\x04tH\xEF\xF1\x8C\x9A\x17:r+_\x82\x82Ta\x04x\x91\x90a\t\0V[\x90\x91UPP[PPPPV[_[\x82\x81\x10\x15a\x04~W0c;\xBB.\x1D\x85\x85\x84\x81\x81\x10a\x04\xA6Wa\x04\xA6a\t*V[\x90P` \x02\x01` \x81\x01\x90a\x04\xBB\x91\x90a\tWV[`@Q\x7F\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\xE0\x84\x90\x1B\x16\x81Rs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x91\x82\x16`\x04\x82\x01R\x90\x85\x16`$\x82\x01R_`D\x82\x01R`d\x01_`@Q\x80\x83\x03\x81_\x87\x80;\x15\x80\x15a\x05,W__\xFD[PZ\xF1\x15\x80\x15a\x05>W=__>=_\xFD[PP`\x01\x90\x92\x01\x91Pa\x04\x86\x90PV[__Z\x90Pa\x05\xA9\x84\x84\x80\x80`\x1F\x01` \x80\x91\x04\x02` \x01`@Q\x90\x81\x01`@R\x80\x93\x92\x91\x90\x81\x81R` \x01\x83\x83\x80\x82\x847_\x92\x01\x91\x90\x91RPPs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x89\x16\x92\x91PPa\x05\xEAV[P\x7F\x14\xF5\xB2\xC1\x85\xFC\x03\xC7\\x}\x1F\x0E\x10\xEA\x13|\xC6\xD25\xA0\x04tH\xEF\xF1\x8C\x9A\x17:r+TZa\x05\xD7\x90\x83a\x08\xE7V[a\x05\xE1\x91\x90a\x08\xE7V[\x95\x94PPPPPV[``a\x05\xF7\x83_\x84a\x05\xFEV[\x93\x92PPPV[``_\x84s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x84\x84`@Qa\x06'\x91\x90a\trV[_`@Q\x80\x83\x03\x81\x85\x87Z\xF1\x92PPP=\x80_\x81\x14a\x06aW`@Q\x91P`\x1F\x19`?=\x01\x16\x82\x01`@R=\x82R=_` \x84\x01>a\x06fV[``\x91P[P\x92P\x90P\x80a\x06xW\x81Q` \x83\x01\xFD[P\x93\x92PPPV[s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x16\x81\x14a\x06\xA1W__\xFD[PV[\x805a\x06\xAF\x81a\x06\x80V[\x91\x90PV[__\x83`\x1F\x84\x01\x12a\x06\xC4W__\xFD[P\x815g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a\x06\xDBW__\xFD[` \x83\x01\x91P\x83` \x82\x85\x01\x01\x11\x15a\x06\xF2W__\xFD[\x92P\x92\x90PV[______`\x80\x87\x89\x03\x12\x15a\x07\x0EW__\xFD[\x865a\x07\x19\x81a\x06\x80V[\x95P` \x87\x015g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a\x074W__\xFD[\x87\x01`\x1F\x81\x01\x89\x13a\x07DW__\xFD[\x805g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a\x07ZW__\xFD[\x89` \x82`\x05\x1B\x84\x01\x01\x11\x15a\x07nW__\xFD[` \x91\x90\x91\x01\x95P\x93Pa\x07\x84`@\x88\x01a\x06\xA4V[\x92P``\x87\x015g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a\x07\x9FW__\xFD[a\x07\xAB\x89\x82\x8A\x01a\x06\xB4V[\x97\x9A\x96\x99P\x94\x97P\x92\x95\x93\x94\x92PPPV[_`@\x82\x01\x84\x83R`@` \x84\x01R\x80\x84Q\x80\x83R``\x85\x01\x91P` \x86\x01\x92P_[\x81\x81\x10\x15a\x07\xFEW\x83Q\x83R` \x93\x84\x01\x93\x90\x92\x01\x91`\x01\x01a\x07\xE0V[P\x90\x96\x95PPPPPPV[_____`\xA0\x86\x88\x03\x12\x15a\x08\x1EW__\xFD[\x855a\x08)\x81a\x06\x80V[\x94P` \x86\x015a\x089\x81a\x06\x80V[\x93P`@\x86\x015a\x08I\x81a\x06\x80V[\x92P``\x86\x015\x91P`\x80\x86\x015a\x08`\x81a\x06\x80V[\x80\x91PP\x92\x95P\x92\x95\x90\x93PV[___``\x84\x86\x03\x12\x15a\x08\x80W__\xFD[\x835a\x08\x8B\x81a\x06\x80V[\x92P` \x84\x015a\x08\x9B\x81a\x06\x80V[\x91P`@\x84\x015\x80\x15\x15\x81\x14a\x08\xAFW__\xFD[\x80\x91PP\x92P\x92P\x92V[\x7FNH{q\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0_R`\x11`\x04R`$_\xFD[\x81\x81\x03\x81\x81\x11\x15a\x08\xFAWa\x08\xFAa\x08\xBAV[\x92\x91PPV[\x80\x82\x01\x80\x82\x11\x15a\x08\xFAWa\x08\xFAa\x08\xBAV[_` \x82\x84\x03\x12\x15a\t#W__\xFD[PQ\x91\x90PV[\x7FNH{q\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0_R`2`\x04R`$_\xFD[_` \x82\x84\x03\x12\x15a\tgW__\xFD[\x815a\x05\xF7\x81a\x06\x80V[_\x82Q_[\x81\x81\x10\x15a\t\x91W` \x81\x86\x01\x81\x01Q\x85\x83\x01R\x01a\twV[P_\x92\x01\x91\x82RP\x91\x90PV\xFE\xA1dsolcC\0\x08\x1E\0\n", ); /// The runtime bytecode of the contract, as deployed on the network. /// /// ```text - ///0x608060405234801561000f575f5ffd5b506004361061003f575f3560e01c80631d47e7f4146100435780632582edb41461006d5780633bbb2e1d14610082575b5f5ffd5b610056610051366004610702565b610095565b6040516100649291906107c6565b60405180910390f35b61008061007b366004610813565b610229565b005b610080610090366004610888565b61031e565b5f606033301461012b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f6f6e6c792073696d756c6174696f6e206c6f67696320697320616c6c6f77656460448201527f20746f2063616c6c202773776170272066756e6374696f6e0000000000000000606482015260840160405180910390fd5b5f8573ffffffffffffffffffffffffffffffffffffffff165f6040515f6040518083038185875af1925050503d805f8114610181576040519150601f19603f3d011682016040523d82523d5f602084013e610186565b606091505b505090505061019687878a61048d565b6101a1888585610557565b91506101ae87878a61048d565b7f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722c80548060200260200160405190810160405280929190818152602001828054801561021757602002820191905f5260205f20905b815481526020019060010190808311610203575b50505050509050965096945050505050565b5f5a6040517f542eb77d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8881166004830152878116602483015260448201879052858116606483015284811660848301529192509088169063542eb77d9060a4015f604051808303815f87803b1580156102b4575f5ffd5b505af11580156102c6573d5f5f3e3d5ffd5b505050505a6102d59082610901565b6102e19061116c61091a565b7f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722b5f828254610310919061091a565b909155505050505050505050565b5f5a90507f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722c73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff861614610407576040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85811660048301528616906370a0823190602401602060405180830381865afa1580156103de573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610402919061092d565b610420565b8373ffffffffffffffffffffffffffffffffffffffff16315b81546001810183555f9283526020909220909101558115610487575a6104469082610901565b6104529061116c61091a565b7f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722b5f828254610481919061091a565b90915550505b50505050565b5f5b828110156104875730633bbb2e1d8585848181106104af576104af610944565b90506020020160208101906104c49190610971565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff918216600482015290851660248201525f60448201526064015f604051808303815f87803b158015610535575f5ffd5b505af1158015610547573d5f5f3e3d5ffd5b50506001909201915061048f9050565b5f5f5a90506105b284848080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152505073ffffffffffffffffffffffffffffffffffffffff8916929150506105f3565b507f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722b545a6105e09083610901565b6105ea9190610901565b95945050505050565b6060610600835f84610607565b9392505050565b60605f8473ffffffffffffffffffffffffffffffffffffffff168484604051610630919061098c565b5f6040518083038185875af1925050503d805f811461066a576040519150601f19603f3d011682016040523d82523d5f602084013e61066f565b606091505b50925090508061068157815160208301fd5b509392505050565b73ffffffffffffffffffffffffffffffffffffffff811681146106aa575f5ffd5b50565b80356106b881610689565b919050565b5f5f83601f8401126106cd575f5ffd5b50813567ffffffffffffffff8111156106e4575f5ffd5b6020830191508360208285010111156106fb575f5ffd5b9250929050565b5f5f5f5f5f5f60808789031215610717575f5ffd5b863561072281610689565b9550602087013567ffffffffffffffff81111561073d575f5ffd5b8701601f8101891361074d575f5ffd5b803567ffffffffffffffff811115610763575f5ffd5b8960208260051b8401011115610777575f5ffd5b6020919091019550935061078d604088016106ad565b9250606087013567ffffffffffffffff8111156107a8575f5ffd5b6107b489828a016106bd565b979a9699509497509295939492505050565b5f60408201848352604060208401528084518083526060850191506020860192505f5b818110156108075783518352602093840193909201916001016107e9565b50909695505050505050565b5f5f5f5f5f5f60c08789031215610828575f5ffd5b863561083381610689565b9550602087013561084381610689565b9450604087013561085381610689565b935060608701359250608087013561086a81610689565b915060a087013561087a81610689565b809150509295509295509295565b5f5f5f6060848603121561089a575f5ffd5b83356108a581610689565b925060208401356108b581610689565b9150604084013580151581146108c9575f5ffd5b809150509250925092565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b81810381811115610914576109146108d4565b92915050565b80820180821115610914576109146108d4565b5f6020828403121561093d575f5ffd5b5051919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f60208284031215610981575f5ffd5b813561060081610689565b5f82515f5b818110156109ab5760208186018101518583015201610991565b505f92019182525091905056fea164736f6c634300081e000a + ///0x608060405234801561000f575f5ffd5b506004361061003f575f3560e01c80631d47e7f4146100435780632ff111f51461006d5780633bbb2e1d14610082575b5f5ffd5b6100566100513660046106f9565b610095565b6040516100649291906107bd565b60405180910390f35b61008061007b36600461080a565b610229565b005b61008061009036600461086e565b610315565b5f606033301461012b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f6f6e6c792073696d756c6174696f6e206c6f67696320697320616c6c6f77656460448201527f20746f2063616c6c202773776170272066756e6374696f6e0000000000000000606482015260840160405180910390fd5b5f8573ffffffffffffffffffffffffffffffffffffffff165f6040515f6040518083038185875af1925050503d805f8114610181576040519150601f19603f3d011682016040523d82523d5f602084013e610186565b606091505b505090505061019687878a610484565b6101a188858561054e565b91506101ae87878a610484565b7f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722c80548060200260200160405190810160405280929190818152602001828054801561021757602002820191905f5260205f20905b815481526020019060010190808311610203575b50505050509050965096945050505050565b5f5a6040517f5bbe8b3f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8781166004830152868116602483015260448201869052848116606483015291925090871690635bbe8b3f906084015f604051808303815f87803b1580156102ac575f5ffd5b505af11580156102be573d5f5f3e3d5ffd5b505050505a6102cd90826108e7565b6102d99061116c610900565b7f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722b5f8282546103089190610900565b9091555050505050505050565b5f5a90507f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722c73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73ffffffffffffffffffffffffffffffffffffffff8616146103fe576040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85811660048301528616906370a0823190602401602060405180830381865afa1580156103d5573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103f99190610913565b610417565b8373ffffffffffffffffffffffffffffffffffffffff16315b81546001810183555f928352602090922090910155811561047e575a61043d90826108e7565b6104499061116c610900565b7f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722b5f8282546104789190610900565b90915550505b50505050565b5f5b8281101561047e5730633bbb2e1d8585848181106104a6576104a661092a565b90506020020160208101906104bb9190610957565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff918216600482015290851660248201525f60448201526064015f604051808303815f87803b15801561052c575f5ffd5b505af115801561053e573d5f5f3e3d5ffd5b5050600190920191506104869050565b5f5f5a90506105a984848080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152505073ffffffffffffffffffffffffffffffffffffffff8916929150506105ea565b507f14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff18c9a173a722b545a6105d790836108e7565b6105e191906108e7565b95945050505050565b60606105f7835f846105fe565b9392505050565b60605f8473ffffffffffffffffffffffffffffffffffffffff1684846040516106279190610972565b5f6040518083038185875af1925050503d805f8114610661576040519150601f19603f3d011682016040523d82523d5f602084013e610666565b606091505b50925090508061067857815160208301fd5b509392505050565b73ffffffffffffffffffffffffffffffffffffffff811681146106a1575f5ffd5b50565b80356106af81610680565b919050565b5f5f83601f8401126106c4575f5ffd5b50813567ffffffffffffffff8111156106db575f5ffd5b6020830191508360208285010111156106f2575f5ffd5b9250929050565b5f5f5f5f5f5f6080878903121561070e575f5ffd5b863561071981610680565b9550602087013567ffffffffffffffff811115610734575f5ffd5b8701601f81018913610744575f5ffd5b803567ffffffffffffffff81111561075a575f5ffd5b8960208260051b840101111561076e575f5ffd5b60209190910195509350610784604088016106a4565b9250606087013567ffffffffffffffff81111561079f575f5ffd5b6107ab89828a016106b4565b979a9699509497509295939492505050565b5f60408201848352604060208401528084518083526060850191506020860192505f5b818110156107fe5783518352602093840193909201916001016107e0565b50909695505050505050565b5f5f5f5f5f60a0868803121561081e575f5ffd5b853561082981610680565b9450602086013561083981610680565b9350604086013561084981610680565b925060608601359150608086013561086081610680565b809150509295509295909350565b5f5f5f60608486031215610880575f5ffd5b833561088b81610680565b9250602084013561089b81610680565b9150604084013580151581146108af575f5ffd5b809150509250925092565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b818103818111156108fa576108fa6108ba565b92915050565b808201808211156108fa576108fa6108ba565b5f60208284031215610923575f5ffd5b5051919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f60208284031215610967575f5ffd5b81356105f781610680565b5f82515f5b818110156109915760208186018101518583015201610977565b505f92019182525091905056fea164736f6c634300081e000a /// ``` #[rustfmt::skip] #[allow(clippy::all)] pub static DEPLOYED_BYTECODE: alloy_sol_types::private::Bytes = alloy_sol_types::private::Bytes::from_static( - b"`\x80`@R4\x80\x15a\0\x0FW__\xFD[P`\x046\x10a\0?W_5`\xE0\x1C\x80c\x1DG\xE7\xF4\x14a\0CW\x80c%\x82\xED\xB4\x14a\0mW\x80c;\xBB.\x1D\x14a\0\x82W[__\xFD[a\0Va\0Q6`\x04a\x07\x02V[a\0\x95V[`@Qa\0d\x92\x91\x90a\x07\xC6V[`@Q\x80\x91\x03\x90\xF3[a\0\x80a\0{6`\x04a\x08\x13V[a\x02)V[\0[a\0\x80a\0\x906`\x04a\x08\x88V[a\x03\x1EV[_``30\x14a\x01+W`@Q\x7F\x08\xC3y\xA0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R` `\x04\x82\x01R`8`$\x82\x01R\x7Fonly simulation logic is allowed`D\x82\x01R\x7F to call 'swap' function\0\0\0\0\0\0\0\0`d\x82\x01R`\x84\x01`@Q\x80\x91\x03\x90\xFD[_\x85s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16_`@Q_`@Q\x80\x83\x03\x81\x85\x87Z\xF1\x92PPP=\x80_\x81\x14a\x01\x81W`@Q\x91P`\x1F\x19`?=\x01\x16\x82\x01`@R=\x82R=_` \x84\x01>a\x01\x86V[``\x91P[PP\x90PPa\x01\x96\x87\x87\x8Aa\x04\x8DV[a\x01\xA1\x88\x85\x85a\x05WV[\x91Pa\x01\xAE\x87\x87\x8Aa\x04\x8DV[\x7F\x14\xF5\xB2\xC1\x85\xFC\x03\xC7\\x}\x1F\x0E\x10\xEA\x13|\xC6\xD25\xA0\x04tH\xEF\xF1\x8C\x9A\x17:r,\x80T\x80` \x02` \x01`@Q\x90\x81\x01`@R\x80\x92\x91\x90\x81\x81R` \x01\x82\x80T\x80\x15a\x02\x17W` \x02\x82\x01\x91\x90_R` _ \x90[\x81T\x81R` \x01\x90`\x01\x01\x90\x80\x83\x11a\x02\x03W[PPPPP\x90P\x96P\x96\x94PPPPPV[_Z`@Q\x7FT.\xB7}\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81Rs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x88\x81\x16`\x04\x83\x01R\x87\x81\x16`$\x83\x01R`D\x82\x01\x87\x90R\x85\x81\x16`d\x83\x01R\x84\x81\x16`\x84\x83\x01R\x91\x92P\x90\x88\x16\x90cT.\xB7}\x90`\xA4\x01_`@Q\x80\x83\x03\x81_\x87\x80;\x15\x80\x15a\x02\xB4W__\xFD[PZ\xF1\x15\x80\x15a\x02\xC6W=__>=_\xFD[PPPPZa\x02\xD5\x90\x82a\t\x01V[a\x02\xE1\x90a\x11la\t\x1AV[\x7F\x14\xF5\xB2\xC1\x85\xFC\x03\xC7\\x}\x1F\x0E\x10\xEA\x13|\xC6\xD25\xA0\x04tH\xEF\xF1\x8C\x9A\x17:r+_\x82\x82Ta\x03\x10\x91\x90a\t\x1AV[\x90\x91UPPPPPPPPPV[_Z\x90P\x7F\x14\xF5\xB2\xC1\x85\xFC\x03\xC7\\x}\x1F\x0E\x10\xEA\x13|\xC6\xD25\xA0\x04tH\xEF\xF1\x8C\x9A\x17:r,s\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEEs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x86\x16\x14a\x04\x07W`@Q\x7Fp\xA0\x821\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81Rs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x85\x81\x16`\x04\x83\x01R\x86\x16\x90cp\xA0\x821\x90`$\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x03\xDEW=__>=_\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x04\x02\x91\x90a\t-V[a\x04 V[\x83s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x161[\x81T`\x01\x81\x01\x83U_\x92\x83R` \x90\x92 \x90\x91\x01U\x81\x15a\x04\x87WZa\x04F\x90\x82a\t\x01V[a\x04R\x90a\x11la\t\x1AV[\x7F\x14\xF5\xB2\xC1\x85\xFC\x03\xC7\\x}\x1F\x0E\x10\xEA\x13|\xC6\xD25\xA0\x04tH\xEF\xF1\x8C\x9A\x17:r+_\x82\x82Ta\x04\x81\x91\x90a\t\x1AV[\x90\x91UPP[PPPPV[_[\x82\x81\x10\x15a\x04\x87W0c;\xBB.\x1D\x85\x85\x84\x81\x81\x10a\x04\xAFWa\x04\xAFa\tDV[\x90P` \x02\x01` \x81\x01\x90a\x04\xC4\x91\x90a\tqV[`@Q\x7F\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\xE0\x84\x90\x1B\x16\x81Rs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x91\x82\x16`\x04\x82\x01R\x90\x85\x16`$\x82\x01R_`D\x82\x01R`d\x01_`@Q\x80\x83\x03\x81_\x87\x80;\x15\x80\x15a\x055W__\xFD[PZ\xF1\x15\x80\x15a\x05GW=__>=_\xFD[PP`\x01\x90\x92\x01\x91Pa\x04\x8F\x90PV[__Z\x90Pa\x05\xB2\x84\x84\x80\x80`\x1F\x01` \x80\x91\x04\x02` \x01`@Q\x90\x81\x01`@R\x80\x93\x92\x91\x90\x81\x81R` \x01\x83\x83\x80\x82\x847_\x92\x01\x91\x90\x91RPPs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x89\x16\x92\x91PPa\x05\xF3V[P\x7F\x14\xF5\xB2\xC1\x85\xFC\x03\xC7\\x}\x1F\x0E\x10\xEA\x13|\xC6\xD25\xA0\x04tH\xEF\xF1\x8C\x9A\x17:r+TZa\x05\xE0\x90\x83a\t\x01V[a\x05\xEA\x91\x90a\t\x01V[\x95\x94PPPPPV[``a\x06\0\x83_\x84a\x06\x07V[\x93\x92PPPV[``_\x84s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x84\x84`@Qa\x060\x91\x90a\t\x8CV[_`@Q\x80\x83\x03\x81\x85\x87Z\xF1\x92PPP=\x80_\x81\x14a\x06jW`@Q\x91P`\x1F\x19`?=\x01\x16\x82\x01`@R=\x82R=_` \x84\x01>a\x06oV[``\x91P[P\x92P\x90P\x80a\x06\x81W\x81Q` \x83\x01\xFD[P\x93\x92PPPV[s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x16\x81\x14a\x06\xAAW__\xFD[PV[\x805a\x06\xB8\x81a\x06\x89V[\x91\x90PV[__\x83`\x1F\x84\x01\x12a\x06\xCDW__\xFD[P\x815g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a\x06\xE4W__\xFD[` \x83\x01\x91P\x83` \x82\x85\x01\x01\x11\x15a\x06\xFBW__\xFD[\x92P\x92\x90PV[______`\x80\x87\x89\x03\x12\x15a\x07\x17W__\xFD[\x865a\x07\"\x81a\x06\x89V[\x95P` \x87\x015g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a\x07=W__\xFD[\x87\x01`\x1F\x81\x01\x89\x13a\x07MW__\xFD[\x805g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a\x07cW__\xFD[\x89` \x82`\x05\x1B\x84\x01\x01\x11\x15a\x07wW__\xFD[` \x91\x90\x91\x01\x95P\x93Pa\x07\x8D`@\x88\x01a\x06\xADV[\x92P``\x87\x015g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a\x07\xA8W__\xFD[a\x07\xB4\x89\x82\x8A\x01a\x06\xBDV[\x97\x9A\x96\x99P\x94\x97P\x92\x95\x93\x94\x92PPPV[_`@\x82\x01\x84\x83R`@` \x84\x01R\x80\x84Q\x80\x83R``\x85\x01\x91P` \x86\x01\x92P_[\x81\x81\x10\x15a\x08\x07W\x83Q\x83R` \x93\x84\x01\x93\x90\x92\x01\x91`\x01\x01a\x07\xE9V[P\x90\x96\x95PPPPPPV[______`\xC0\x87\x89\x03\x12\x15a\x08(W__\xFD[\x865a\x083\x81a\x06\x89V[\x95P` \x87\x015a\x08C\x81a\x06\x89V[\x94P`@\x87\x015a\x08S\x81a\x06\x89V[\x93P``\x87\x015\x92P`\x80\x87\x015a\x08j\x81a\x06\x89V[\x91P`\xA0\x87\x015a\x08z\x81a\x06\x89V[\x80\x91PP\x92\x95P\x92\x95P\x92\x95V[___``\x84\x86\x03\x12\x15a\x08\x9AW__\xFD[\x835a\x08\xA5\x81a\x06\x89V[\x92P` \x84\x015a\x08\xB5\x81a\x06\x89V[\x91P`@\x84\x015\x80\x15\x15\x81\x14a\x08\xC9W__\xFD[\x80\x91PP\x92P\x92P\x92V[\x7FNH{q\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0_R`\x11`\x04R`$_\xFD[\x81\x81\x03\x81\x81\x11\x15a\t\x14Wa\t\x14a\x08\xD4V[\x92\x91PPV[\x80\x82\x01\x80\x82\x11\x15a\t\x14Wa\t\x14a\x08\xD4V[_` \x82\x84\x03\x12\x15a\t=W__\xFD[PQ\x91\x90PV[\x7FNH{q\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0_R`2`\x04R`$_\xFD[_` \x82\x84\x03\x12\x15a\t\x81W__\xFD[\x815a\x06\0\x81a\x06\x89V[_\x82Q_[\x81\x81\x10\x15a\t\xABW` \x81\x86\x01\x81\x01Q\x85\x83\x01R\x01a\t\x91V[P_\x92\x01\x91\x82RP\x91\x90PV\xFE\xA1dsolcC\0\x08\x1E\0\n", + b"`\x80`@R4\x80\x15a\0\x0FW__\xFD[P`\x046\x10a\0?W_5`\xE0\x1C\x80c\x1DG\xE7\xF4\x14a\0CW\x80c/\xF1\x11\xF5\x14a\0mW\x80c;\xBB.\x1D\x14a\0\x82W[__\xFD[a\0Va\0Q6`\x04a\x06\xF9V[a\0\x95V[`@Qa\0d\x92\x91\x90a\x07\xBDV[`@Q\x80\x91\x03\x90\xF3[a\0\x80a\0{6`\x04a\x08\nV[a\x02)V[\0[a\0\x80a\0\x906`\x04a\x08nV[a\x03\x15V[_``30\x14a\x01+W`@Q\x7F\x08\xC3y\xA0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R` `\x04\x82\x01R`8`$\x82\x01R\x7Fonly simulation logic is allowed`D\x82\x01R\x7F to call 'swap' function\0\0\0\0\0\0\0\0`d\x82\x01R`\x84\x01`@Q\x80\x91\x03\x90\xFD[_\x85s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16_`@Q_`@Q\x80\x83\x03\x81\x85\x87Z\xF1\x92PPP=\x80_\x81\x14a\x01\x81W`@Q\x91P`\x1F\x19`?=\x01\x16\x82\x01`@R=\x82R=_` \x84\x01>a\x01\x86V[``\x91P[PP\x90PPa\x01\x96\x87\x87\x8Aa\x04\x84V[a\x01\xA1\x88\x85\x85a\x05NV[\x91Pa\x01\xAE\x87\x87\x8Aa\x04\x84V[\x7F\x14\xF5\xB2\xC1\x85\xFC\x03\xC7\\x}\x1F\x0E\x10\xEA\x13|\xC6\xD25\xA0\x04tH\xEF\xF1\x8C\x9A\x17:r,\x80T\x80` \x02` \x01`@Q\x90\x81\x01`@R\x80\x92\x91\x90\x81\x81R` \x01\x82\x80T\x80\x15a\x02\x17W` \x02\x82\x01\x91\x90_R` _ \x90[\x81T\x81R` \x01\x90`\x01\x01\x90\x80\x83\x11a\x02\x03W[PPPPP\x90P\x96P\x96\x94PPPPPV[_Z`@Q\x7F[\xBE\x8B?\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81Rs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x87\x81\x16`\x04\x83\x01R\x86\x81\x16`$\x83\x01R`D\x82\x01\x86\x90R\x84\x81\x16`d\x83\x01R\x91\x92P\x90\x87\x16\x90c[\xBE\x8B?\x90`\x84\x01_`@Q\x80\x83\x03\x81_\x87\x80;\x15\x80\x15a\x02\xACW__\xFD[PZ\xF1\x15\x80\x15a\x02\xBEW=__>=_\xFD[PPPPZa\x02\xCD\x90\x82a\x08\xE7V[a\x02\xD9\x90a\x11la\t\0V[\x7F\x14\xF5\xB2\xC1\x85\xFC\x03\xC7\\x}\x1F\x0E\x10\xEA\x13|\xC6\xD25\xA0\x04tH\xEF\xF1\x8C\x9A\x17:r+_\x82\x82Ta\x03\x08\x91\x90a\t\0V[\x90\x91UPPPPPPPPV[_Z\x90P\x7F\x14\xF5\xB2\xC1\x85\xFC\x03\xC7\\x}\x1F\x0E\x10\xEA\x13|\xC6\xD25\xA0\x04tH\xEF\xF1\x8C\x9A\x17:r,s\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEEs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x86\x16\x14a\x03\xFEW`@Q\x7Fp\xA0\x821\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81Rs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x85\x81\x16`\x04\x83\x01R\x86\x16\x90cp\xA0\x821\x90`$\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x03\xD5W=__>=_\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x03\xF9\x91\x90a\t\x13V[a\x04\x17V[\x83s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x161[\x81T`\x01\x81\x01\x83U_\x92\x83R` \x90\x92 \x90\x91\x01U\x81\x15a\x04~WZa\x04=\x90\x82a\x08\xE7V[a\x04I\x90a\x11la\t\0V[\x7F\x14\xF5\xB2\xC1\x85\xFC\x03\xC7\\x}\x1F\x0E\x10\xEA\x13|\xC6\xD25\xA0\x04tH\xEF\xF1\x8C\x9A\x17:r+_\x82\x82Ta\x04x\x91\x90a\t\0V[\x90\x91UPP[PPPPV[_[\x82\x81\x10\x15a\x04~W0c;\xBB.\x1D\x85\x85\x84\x81\x81\x10a\x04\xA6Wa\x04\xA6a\t*V[\x90P` \x02\x01` \x81\x01\x90a\x04\xBB\x91\x90a\tWV[`@Q\x7F\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\xE0\x84\x90\x1B\x16\x81Rs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x91\x82\x16`\x04\x82\x01R\x90\x85\x16`$\x82\x01R_`D\x82\x01R`d\x01_`@Q\x80\x83\x03\x81_\x87\x80;\x15\x80\x15a\x05,W__\xFD[PZ\xF1\x15\x80\x15a\x05>W=__>=_\xFD[PP`\x01\x90\x92\x01\x91Pa\x04\x86\x90PV[__Z\x90Pa\x05\xA9\x84\x84\x80\x80`\x1F\x01` \x80\x91\x04\x02` \x01`@Q\x90\x81\x01`@R\x80\x93\x92\x91\x90\x81\x81R` \x01\x83\x83\x80\x82\x847_\x92\x01\x91\x90\x91RPPs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x89\x16\x92\x91PPa\x05\xEAV[P\x7F\x14\xF5\xB2\xC1\x85\xFC\x03\xC7\\x}\x1F\x0E\x10\xEA\x13|\xC6\xD25\xA0\x04tH\xEF\xF1\x8C\x9A\x17:r+TZa\x05\xD7\x90\x83a\x08\xE7V[a\x05\xE1\x91\x90a\x08\xE7V[\x95\x94PPPPPV[``a\x05\xF7\x83_\x84a\x05\xFEV[\x93\x92PPPV[``_\x84s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x84\x84`@Qa\x06'\x91\x90a\trV[_`@Q\x80\x83\x03\x81\x85\x87Z\xF1\x92PPP=\x80_\x81\x14a\x06aW`@Q\x91P`\x1F\x19`?=\x01\x16\x82\x01`@R=\x82R=_` \x84\x01>a\x06fV[``\x91P[P\x92P\x90P\x80a\x06xW\x81Q` \x83\x01\xFD[P\x93\x92PPPV[s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x16\x81\x14a\x06\xA1W__\xFD[PV[\x805a\x06\xAF\x81a\x06\x80V[\x91\x90PV[__\x83`\x1F\x84\x01\x12a\x06\xC4W__\xFD[P\x815g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a\x06\xDBW__\xFD[` \x83\x01\x91P\x83` \x82\x85\x01\x01\x11\x15a\x06\xF2W__\xFD[\x92P\x92\x90PV[______`\x80\x87\x89\x03\x12\x15a\x07\x0EW__\xFD[\x865a\x07\x19\x81a\x06\x80V[\x95P` \x87\x015g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a\x074W__\xFD[\x87\x01`\x1F\x81\x01\x89\x13a\x07DW__\xFD[\x805g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a\x07ZW__\xFD[\x89` \x82`\x05\x1B\x84\x01\x01\x11\x15a\x07nW__\xFD[` \x91\x90\x91\x01\x95P\x93Pa\x07\x84`@\x88\x01a\x06\xA4V[\x92P``\x87\x015g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a\x07\x9FW__\xFD[a\x07\xAB\x89\x82\x8A\x01a\x06\xB4V[\x97\x9A\x96\x99P\x94\x97P\x92\x95\x93\x94\x92PPPV[_`@\x82\x01\x84\x83R`@` \x84\x01R\x80\x84Q\x80\x83R``\x85\x01\x91P` \x86\x01\x92P_[\x81\x81\x10\x15a\x07\xFEW\x83Q\x83R` \x93\x84\x01\x93\x90\x92\x01\x91`\x01\x01a\x07\xE0V[P\x90\x96\x95PPPPPPV[_____`\xA0\x86\x88\x03\x12\x15a\x08\x1EW__\xFD[\x855a\x08)\x81a\x06\x80V[\x94P` \x86\x015a\x089\x81a\x06\x80V[\x93P`@\x86\x015a\x08I\x81a\x06\x80V[\x92P``\x86\x015\x91P`\x80\x86\x015a\x08`\x81a\x06\x80V[\x80\x91PP\x92\x95P\x92\x95\x90\x93PV[___``\x84\x86\x03\x12\x15a\x08\x80W__\xFD[\x835a\x08\x8B\x81a\x06\x80V[\x92P` \x84\x015a\x08\x9B\x81a\x06\x80V[\x91P`@\x84\x015\x80\x15\x15\x81\x14a\x08\xAFW__\xFD[\x80\x91PP\x92P\x92P\x92V[\x7FNH{q\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0_R`\x11`\x04R`$_\xFD[\x81\x81\x03\x81\x81\x11\x15a\x08\xFAWa\x08\xFAa\x08\xBAV[\x92\x91PPV[\x80\x82\x01\x80\x82\x11\x15a\x08\xFAWa\x08\xFAa\x08\xBAV[_` \x82\x84\x03\x12\x15a\t#W__\xFD[PQ\x91\x90PV[\x7FNH{q\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0_R`2`\x04R`$_\xFD[_` \x82\x84\x03\x12\x15a\tgW__\xFD[\x815a\x05\xF7\x81a\x06\x80V[_\x82Q_[\x81\x81\x10\x15a\t\x91W` \x81\x86\x01\x81\x01Q\x85\x83\x01R\x01a\twV[P_\x92\x01\x91\x82RP\x91\x90PV\xFE\xA1dsolcC\0\x08\x1E\0\n", ); #[derive(Default, Debug, PartialEq, Eq, Hash)] - /**Function with signature `ensureTradePreconditions(address,address,address,uint256,address,address)` and selector `0x2582edb4`. + /**Function with signature `ensureTradePreconditions(address,address,address,uint256,address)` and selector `0x2ff111f5`. ```solidity - function ensureTradePreconditions(address trader, address settlementContract, address sellToken, uint256 sellAmount, address nativeToken, address spardose) external; + function ensureTradePreconditions(address trader, address settlementContract, address sellToken, uint256 sellAmount, address spardose) external; ```*/ #[allow(non_camel_case_types, non_snake_case, clippy::pub_underscore_fields)] #[derive(Clone)] @@ -168,12 +163,10 @@ pub mod Solver { #[allow(missing_docs)] pub sellAmount: alloy_sol_types::private::primitives::aliases::U256, #[allow(missing_docs)] - pub nativeToken: alloy_sol_types::private::Address, - #[allow(missing_docs)] pub spardose: alloy_sol_types::private::Address, } ///Container type for the return parameters of the - /// [`ensureTradePreconditions(address,address,address,uint256,address, + /// [`ensureTradePreconditions(address,address,address,uint256, /// address)`](ensureTradePreconditionsCall) function. #[allow(non_camel_case_types, non_snake_case, clippy::pub_underscore_fields)] #[derive(Clone)] @@ -195,7 +188,6 @@ pub mod Solver { alloy_sol_types::sol_data::Address, alloy_sol_types::sol_data::Uint<256>, alloy_sol_types::sol_data::Address, - alloy_sol_types::sol_data::Address, ); #[doc(hidden)] type UnderlyingRustTuple<'a> = ( @@ -204,7 +196,6 @@ pub mod Solver { alloy_sol_types::private::Address, alloy_sol_types::private::primitives::aliases::U256, alloy_sol_types::private::Address, - alloy_sol_types::private::Address, ); #[cfg(test)] #[allow(dead_code, unreachable_patterns)] @@ -224,7 +215,6 @@ pub mod Solver { value.settlementContract, value.sellToken, value.sellAmount, - value.nativeToken, value.spardose, ) } @@ -238,8 +228,7 @@ pub mod Solver { settlementContract: tuple.1, sellToken: tuple.2, sellAmount: tuple.3, - nativeToken: tuple.4, - spardose: tuple.5, + spardose: tuple.4, } } } @@ -290,16 +279,15 @@ pub mod Solver { alloy_sol_types::sol_data::Address, alloy_sol_types::sol_data::Uint<256>, alloy_sol_types::sol_data::Address, - alloy_sol_types::sol_data::Address, ); type Return = ensureTradePreconditionsReturn; type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; type ReturnTuple<'a> = (); type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; - const SELECTOR: [u8; 4] = [37u8, 130u8, 237u8, 180u8]; + const SELECTOR: [u8; 4] = [47u8, 241u8, 17u8, 245u8]; const SIGNATURE: &'static str = - "ensureTradePreconditions(address,address,address,uint256,address,address)"; + "ensureTradePreconditions(address,address,address,uint256,address)"; #[inline] fn new<'a>( @@ -323,9 +311,6 @@ pub mod Solver { as alloy_sol_types::SolType>::tokenize( &self.sellAmount, ), - ::tokenize( - &self.nativeToken, - ), ::tokenize( &self.spardose, ), @@ -736,7 +721,7 @@ pub mod Solver { /// Prefer using `SolInterface` methods instead. pub const SELECTORS: &'static [[u8; 4usize]] = &[ [29u8, 71u8, 231u8, 244u8], - [37u8, 130u8, 237u8, 180u8], + [47u8, 241u8, 17u8, 245u8], [59u8, 187u8, 46u8, 29u8], ]; /// The signatures in the same order as `SELECTORS`. @@ -1083,7 +1068,6 @@ pub mod Solver { settlementContract: alloy_sol_types::private::Address, sellToken: alloy_sol_types::private::Address, sellAmount: alloy_sol_types::private::primitives::aliases::U256, - nativeToken: alloy_sol_types::private::Address, spardose: alloy_sol_types::private::Address, ) -> alloy_contract::SolCallBuilder<&P, ensureTradePreconditionsCall, N> { self.call_builder(&ensureTradePreconditionsCall { @@ -1091,7 +1075,6 @@ pub mod Solver { settlementContract, sellToken, sellAmount, - nativeToken, spardose, }) } diff --git a/contracts/generated/contracts-generated/trader/src/lib.rs b/contracts/generated/contracts-generated/trader/src/lib.rs index 9583bc614e..8fb9497e99 100644 --- a/contracts/generated/contracts-generated/trader/src/lib.rs +++ b/contracts/generated/contracts-generated/trader/src/lib.rs @@ -15,7 +15,7 @@ interface Trader { receive() external payable; - function ensureTradePreconditions(address settlementContract, address sellToken, uint256 sellAmount, address nativeToken, address spardose) external; + function ensureTradePreconditions(address settlementContract, address sellToken, uint256 sellAmount, address spardose) external; function isValidSignature(bytes32, bytes memory) external pure returns (bytes4); function safeApprove(address token, address vaultRelayer, uint256 amount) external; } @@ -51,11 +51,6 @@ interface Trader { "type": "uint256", "internalType": "uint256" }, - { - "name": "nativeToken", - "type": "address", - "internalType": "address" - }, { "name": "spardose", "type": "address", @@ -126,27 +121,27 @@ pub mod Trader { /// The creation / init bytecode of the contract. /// /// ```text - ///0x6080604052348015600e575f5ffd5b50610d008061001c5f395ff3fe608060405260043610610037575f3560e01c80631626ba7e1461008d578063542eb77d14610104578063eb5625d9146101255761003e565b3661003e57005b5f6100835f368080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525062010000939250506101449050565b9050805160208201f35b348015610098575f5ffd5b506100cf6100a7366004610b01565b7f1626ba7e000000000000000000000000000000000000000000000000000000009392505050565b6040517fffffffff00000000000000000000000000000000000000000000000000000000909116815260200160405180910390f35b34801561010f575f5ffd5b5061012361011e366004610b9c565b6101c2565b005b348015610130575f5ffd5b5061012361013f366004610c00565b610916565b60605f8373ffffffffffffffffffffffffffffffffffffffff168360405161016c9190610c3e565b5f60405180830381855af49150503d805f81146101a4576040519150601f19603f3d011682016040523d82523d5f602084013e6101a9565b606091505b5092509050806101bb57815160208301fd5b5092915050565b7f02565dba7d68dcbed629110024b7b5e785bfc1a484602045eea513de8a2dcf99805460017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082161790915560ff16156102a3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f70726570617265537761702063616e206f6e6c792062652063616c6c6564206f60448201527f6e6365000000000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b8173ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16036103e4576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201525f9073ffffffffffffffffffffffffffffffffffffffff8616906370a0823190602401602060405180830381865afa158015610340573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103649190610c6a565b9050838110156103e2575f6103798286610c81565b90508047106103e0578373ffffffffffffffffffffffffffffffffffffffff1663d0e30db0826040518263ffffffff1660e01b81526004015f604051808303818588803b1580156103c8575f5ffd5b505af11580156103da573d5f5f3e3d5ffd5b50505050505b505b505b5f8573ffffffffffffffffffffffffffffffffffffffff16639b552cc26040518163ffffffff1660e01b8152600401602060405180830381865afa15801561042e573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104529190610cb9565b6040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff80831660248301529192505f9187169063dd62ed3e90604401602060405180830381865afa1580156104c7573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104eb9190610c6a565b905084811015610748576040517feb5625d900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8088166004830152831660248201525f6044820152309063eb5625d9906064015f604051808303815f87803b158015610567575f5ffd5b505af1925050508015610578575060015b506040517feb5625d900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8088166004830152831660248201527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6044820152309063eb5625d9906064015f604051808303815f87803b15801561060b575f5ffd5b505af192505050801561061c575060015b506040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff83811660248301525f919088169063dd62ed3e90604401602060405180830381865afa158015610690573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106b49190610c6a565b905085811015610746576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f74726164657220646964206e6f7420676976652074686520726571756972656460448201527f20617070726f76616c7300000000000000000000000000000000000000000000606482015260840161029a565b505b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201525f9073ffffffffffffffffffffffffffffffffffffffff8816906370a0823190602401602060405180830381865afa1580156107b2573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107d69190610c6a565b90508581101561090c5773ffffffffffffffffffffffffffffffffffffffff841663494666b688610807848a610c81565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff909216600483015260248201526044015f604051808303815f87803b15801561086f575f5ffd5b505af1925050508015610880575060015b61090c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f74726164657220646f6573206e6f74206861766520656e6f7567682073656c6c60448201527f20746f6b656e0000000000000000000000000000000000000000000000000000606482015260840161029a565b5050505050505050565b61093773ffffffffffffffffffffffffffffffffffffffff8416838361093c565b505050565b6040805173ffffffffffffffffffffffffffffffffffffffff848116602483015260448083018590528351808403909101815260649092019092526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f095ea7b300000000000000000000000000000000000000000000000000000000179052905f906109ce90861683610a46565b90506109d981610a5a565b610a3f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f5361666545524332303a20617070726f76616c206661696c6564000000000000604482015260640161029a565b5050505050565b6060610a53835f84610a7f565b9392505050565b5f81515f1480610a79575081806020019051810190610a799190610cd4565b92915050565b60605f8473ffffffffffffffffffffffffffffffffffffffff168484604051610aa89190610c3e565b5f6040518083038185875af1925050503d805f8114610ae2576040519150601f19603f3d011682016040523d82523d5f602084013e610ae7565b606091505b509250905080610af957815160208301fd5b509392505050565b5f5f5f60408486031215610b13575f5ffd5b83359250602084013567ffffffffffffffff811115610b30575f5ffd5b8401601f81018613610b40575f5ffd5b803567ffffffffffffffff811115610b56575f5ffd5b866020828401011115610b67575f5ffd5b939660209190910195509293505050565b73ffffffffffffffffffffffffffffffffffffffff81168114610b99575f5ffd5b50565b5f5f5f5f5f60a08688031215610bb0575f5ffd5b8535610bbb81610b78565b94506020860135610bcb81610b78565b9350604086013592506060860135610be281610b78565b91506080860135610bf281610b78565b809150509295509295909350565b5f5f5f60608486031215610c12575f5ffd5b8335610c1d81610b78565b92506020840135610c2d81610b78565b929592945050506040919091013590565b5f82515f5b81811015610c5d5760208186018101518583015201610c43565b505f920191825250919050565b5f60208284031215610c7a575f5ffd5b5051919050565b81810381811115610a79577f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f60208284031215610cc9575f5ffd5b8151610a5381610b78565b5f60208284031215610ce4575f5ffd5b81518015158114610a53575f5ffdfea164736f6c634300081e000a + ///0x6080604052348015600e575f5ffd5b50610baa8061001c5f395ff3fe608060405260043610610037575f3560e01c80631626ba7e1461008d5780635bbe8b3f14610104578063eb5625d9146101255761003e565b3661003e57005b5f6100835f368080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525062010000939250506101449050565b9050805160208201f35b348015610098575f5ffd5b506100cf6100a73660046109bf565b7f1626ba7e000000000000000000000000000000000000000000000000000000009392505050565b6040517fffffffff00000000000000000000000000000000000000000000000000000000909116815260200160405180910390f35b34801561010f575f5ffd5b5061012361011e366004610a5a565b6101c2565b005b348015610130575f5ffd5b5061012361013f366004610aaa565b6107d4565b60605f8373ffffffffffffffffffffffffffffffffffffffff168360405161016c9190610ae8565b5f60405180830381855af49150503d805f81146101a4576040519150601f19603f3d011682016040523d82523d5f602084013e6101a9565b606091505b5092509050806101bb57815160208301fd5b5092915050565b7f02565dba7d68dcbed629110024b7b5e785bfc1a484602045eea513de8a2dcf99805460017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082161790915560ff16156102a3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f70726570617265537761702063616e206f6e6c792062652063616c6c6564206f60448201527f6e6365000000000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b5f8473ffffffffffffffffffffffffffffffffffffffff16639b552cc26040518163ffffffff1660e01b8152600401602060405180830381865afa1580156102ed573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103119190610b14565b6040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff80831660248301529192505f9186169063dd62ed3e90604401602060405180830381865afa158015610386573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103aa9190610b2f565b905083811015610607576040517feb5625d900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8087166004830152831660248201525f6044820152309063eb5625d9906064015f604051808303815f87803b158015610426575f5ffd5b505af1925050508015610437575060015b506040517feb5625d900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8087166004830152831660248201527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6044820152309063eb5625d9906064015f604051808303815f87803b1580156104ca575f5ffd5b505af19250505080156104db575060015b506040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff83811660248301525f919087169063dd62ed3e90604401602060405180830381865afa15801561054f573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105739190610b2f565b905084811015610605576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f74726164657220646964206e6f7420676976652074686520726571756972656460448201527f20617070726f76616c7300000000000000000000000000000000000000000000606482015260840161029a565b505b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201525f9073ffffffffffffffffffffffffffffffffffffffff8716906370a0823190602401602060405180830381865afa158015610671573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106959190610b2f565b9050848110156107cb5773ffffffffffffffffffffffffffffffffffffffff841663494666b6876106c68489610b46565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff909216600483015260248201526044015f604051808303815f87803b15801561072e575f5ffd5b505af192505050801561073f575060015b6107cb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f74726164657220646f6573206e6f74206861766520656e6f7567682073656c6c60448201527f20746f6b656e0000000000000000000000000000000000000000000000000000606482015260840161029a565b50505050505050565b6107f573ffffffffffffffffffffffffffffffffffffffff841683836107fa565b505050565b6040805173ffffffffffffffffffffffffffffffffffffffff848116602483015260448083018590528351808403909101815260649092019092526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f095ea7b300000000000000000000000000000000000000000000000000000000179052905f9061088c90861683610904565b905061089781610918565b6108fd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f5361666545524332303a20617070726f76616c206661696c6564000000000000604482015260640161029a565b5050505050565b6060610911835f8461093d565b9392505050565b5f81515f14806109375750818060200190518101906109379190610b7e565b92915050565b60605f8473ffffffffffffffffffffffffffffffffffffffff1684846040516109669190610ae8565b5f6040518083038185875af1925050503d805f81146109a0576040519150601f19603f3d011682016040523d82523d5f602084013e6109a5565b606091505b5092509050806109b757815160208301fd5b509392505050565b5f5f5f604084860312156109d1575f5ffd5b83359250602084013567ffffffffffffffff8111156109ee575f5ffd5b8401601f810186136109fe575f5ffd5b803567ffffffffffffffff811115610a14575f5ffd5b866020828401011115610a25575f5ffd5b939660209190910195509293505050565b73ffffffffffffffffffffffffffffffffffffffff81168114610a57575f5ffd5b50565b5f5f5f5f60808587031215610a6d575f5ffd5b8435610a7881610a36565b93506020850135610a8881610a36565b9250604085013591506060850135610a9f81610a36565b939692955090935050565b5f5f5f60608486031215610abc575f5ffd5b8335610ac781610a36565b92506020840135610ad781610a36565b929592945050506040919091013590565b5f82515f5b81811015610b075760208186018101518583015201610aed565b505f920191825250919050565b5f60208284031215610b24575f5ffd5b815161091181610a36565b5f60208284031215610b3f575f5ffd5b5051919050565b81810381811115610937577f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f60208284031215610b8e575f5ffd5b81518015158114610911575f5ffdfea164736f6c634300081e000a /// ``` #[rustfmt::skip] #[allow(clippy::all)] pub static BYTECODE: alloy_sol_types::private::Bytes = alloy_sol_types::private::Bytes::from_static( - b"`\x80`@R4\x80\x15`\x0EW__\xFD[Pa\r\0\x80a\0\x1C_9_\xF3\xFE`\x80`@R`\x046\x10a\x007W_5`\xE0\x1C\x80c\x16&\xBA~\x14a\0\x8DW\x80cT.\xB7}\x14a\x01\x04W\x80c\xEBV%\xD9\x14a\x01%Wa\0>V[6a\0>W\0[_a\0\x83_6\x80\x80`\x1F\x01` \x80\x91\x04\x02` \x01`@Q\x90\x81\x01`@R\x80\x93\x92\x91\x90\x81\x81R` \x01\x83\x83\x80\x82\x847_\x92\x01\x91\x90\x91RPb\x01\0\0\x93\x92PPa\x01D\x90PV[\x90P\x80Q` \x82\x01\xF3[4\x80\x15a\0\x98W__\xFD[Pa\0\xCFa\0\xA76`\x04a\x0B\x01V[\x7F\x16&\xBA~\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x93\x92PPPV[`@Q\x7F\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x90\x91\x16\x81R` \x01`@Q\x80\x91\x03\x90\xF3[4\x80\x15a\x01\x0FW__\xFD[Pa\x01#a\x01\x1E6`\x04a\x0B\x9CV[a\x01\xC2V[\0[4\x80\x15a\x010W__\xFD[Pa\x01#a\x01?6`\x04a\x0C\0V[a\t\x16V[``_\x83s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x83`@Qa\x01l\x91\x90a\x0C>V[_`@Q\x80\x83\x03\x81\x85Z\xF4\x91PP=\x80_\x81\x14a\x01\xA4W`@Q\x91P`\x1F\x19`?=\x01\x16\x82\x01`@R=\x82R=_` \x84\x01>a\x01\xA9V[``\x91P[P\x92P\x90P\x80a\x01\xBBW\x81Q` \x83\x01\xFD[P\x92\x91PPV[\x7F\x02V]\xBA}h\xDC\xBE\xD6)\x11\0$\xB7\xB5\xE7\x85\xBF\xC1\xA4\x84` E\xEE\xA5\x13\xDE\x8A-\xCF\x99\x80T`\x01\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\x82\x16\x17\x90\x91U`\xFF\x16\x15a\x02\xA3W`@Q\x7F\x08\xC3y\xA0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R` `\x04\x82\x01R`#`$\x82\x01R\x7FprepareSwap can only be called o`D\x82\x01R\x7Fnce\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`d\x82\x01R`\x84\x01[`@Q\x80\x91\x03\x90\xFD[\x81s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x84s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x03a\x03\xE4W`@Q\x7Fp\xA0\x821\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R0`\x04\x82\x01R_\x90s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x86\x16\x90cp\xA0\x821\x90`$\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x03@W=__>=_\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x03d\x91\x90a\x0CjV[\x90P\x83\x81\x10\x15a\x03\xE2W_a\x03y\x82\x86a\x0C\x81V[\x90P\x80G\x10a\x03\xE0W\x83s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16c\xD0\xE3\r\xB0\x82`@Q\x82c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01_`@Q\x80\x83\x03\x81\x85\x88\x80;\x15\x80\x15a\x03\xC8W__\xFD[PZ\xF1\x15\x80\x15a\x03\xDAW=__>=_\xFD[PPPPP[P[P[_\x85s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16c\x9BU,\xC2`@Q\x81c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x04.W=__>=_\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x04R\x91\x90a\x0C\xB9V[`@Q\x7F\xDDb\xED>\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R0`\x04\x82\x01Rs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x83\x16`$\x83\x01R\x91\x92P_\x91\x87\x16\x90c\xDDb\xED>\x90`D\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x04\xC7W=__>=_\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x04\xEB\x91\x90a\x0CjV[\x90P\x84\x81\x10\x15a\x07HW`@Q\x7F\xEBV%\xD9\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81Rs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x88\x16`\x04\x83\x01R\x83\x16`$\x82\x01R_`D\x82\x01R0\x90c\xEBV%\xD9\x90`d\x01_`@Q\x80\x83\x03\x81_\x87\x80;\x15\x80\x15a\x05gW__\xFD[PZ\xF1\x92PPP\x80\x15a\x05xWP`\x01[P`@Q\x7F\xEBV%\xD9\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81Rs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x88\x16`\x04\x83\x01R\x83\x16`$\x82\x01R\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF`D\x82\x01R0\x90c\xEBV%\xD9\x90`d\x01_`@Q\x80\x83\x03\x81_\x87\x80;\x15\x80\x15a\x06\x0BW__\xFD[PZ\xF1\x92PPP\x80\x15a\x06\x1CWP`\x01[P`@Q\x7F\xDDb\xED>\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R0`\x04\x82\x01Rs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x83\x81\x16`$\x83\x01R_\x91\x90\x88\x16\x90c\xDDb\xED>\x90`D\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x06\x90W=__>=_\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x06\xB4\x91\x90a\x0CjV[\x90P\x85\x81\x10\x15a\x07FW`@Q\x7F\x08\xC3y\xA0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R` `\x04\x82\x01R`*`$\x82\x01R\x7Ftrader did not give the required`D\x82\x01R\x7F approvals\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`d\x82\x01R`\x84\x01a\x02\x9AV[P[`@Q\x7Fp\xA0\x821\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R0`\x04\x82\x01R_\x90s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x88\x16\x90cp\xA0\x821\x90`$\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x07\xB2W=__>=_\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x07\xD6\x91\x90a\x0CjV[\x90P\x85\x81\x10\x15a\t\x0CWs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x84\x16cIFf\xB6\x88a\x08\x07\x84\x8Aa\x0C\x81V[`@Q\x7F\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\xE0\x85\x90\x1B\x16\x81Rs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x90\x92\x16`\x04\x83\x01R`$\x82\x01R`D\x01_`@Q\x80\x83\x03\x81_\x87\x80;\x15\x80\x15a\x08oW__\xFD[PZ\xF1\x92PPP\x80\x15a\x08\x80WP`\x01[a\t\x0CW`@Q\x7F\x08\xC3y\xA0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R` `\x04\x82\x01R`&`$\x82\x01R\x7Ftrader does not have enough sell`D\x82\x01R\x7F token\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`d\x82\x01R`\x84\x01a\x02\x9AV[PPPPPPPPV[a\t7s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x84\x16\x83\x83a\tV[_`@Q\x80\x83\x03\x81\x85\x87Z\xF1\x92PPP=\x80_\x81\x14a\n\xE2W`@Q\x91P`\x1F\x19`?=\x01\x16\x82\x01`@R=\x82R=_` \x84\x01>a\n\xE7V[``\x91P[P\x92P\x90P\x80a\n\xF9W\x81Q` \x83\x01\xFD[P\x93\x92PPPV[___`@\x84\x86\x03\x12\x15a\x0B\x13W__\xFD[\x835\x92P` \x84\x015g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a\x0B0W__\xFD[\x84\x01`\x1F\x81\x01\x86\x13a\x0B@W__\xFD[\x805g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a\x0BVW__\xFD[\x86` \x82\x84\x01\x01\x11\x15a\x0BgW__\xFD[\x93\x96` \x91\x90\x91\x01\x95P\x92\x93PPPV[s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x16\x81\x14a\x0B\x99W__\xFD[PV[_____`\xA0\x86\x88\x03\x12\x15a\x0B\xB0W__\xFD[\x855a\x0B\xBB\x81a\x0BxV[\x94P` \x86\x015a\x0B\xCB\x81a\x0BxV[\x93P`@\x86\x015\x92P``\x86\x015a\x0B\xE2\x81a\x0BxV[\x91P`\x80\x86\x015a\x0B\xF2\x81a\x0BxV[\x80\x91PP\x92\x95P\x92\x95\x90\x93PV[___``\x84\x86\x03\x12\x15a\x0C\x12W__\xFD[\x835a\x0C\x1D\x81a\x0BxV[\x92P` \x84\x015a\x0C-\x81a\x0BxV[\x92\x95\x92\x94PPP`@\x91\x90\x91\x015\x90V[_\x82Q_[\x81\x81\x10\x15a\x0C]W` \x81\x86\x01\x81\x01Q\x85\x83\x01R\x01a\x0CCV[P_\x92\x01\x91\x82RP\x91\x90PV[_` \x82\x84\x03\x12\x15a\x0CzW__\xFD[PQ\x91\x90PV[\x81\x81\x03\x81\x81\x11\x15a\nyW\x7FNH{q\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0_R`\x11`\x04R`$_\xFD[_` \x82\x84\x03\x12\x15a\x0C\xC9W__\xFD[\x81Qa\nS\x81a\x0BxV[_` \x82\x84\x03\x12\x15a\x0C\xE4W__\xFD[\x81Q\x80\x15\x15\x81\x14a\nSW__\xFD\xFE\xA1dsolcC\0\x08\x1E\0\n", + b"`\x80`@R4\x80\x15`\x0EW__\xFD[Pa\x0B\xAA\x80a\0\x1C_9_\xF3\xFE`\x80`@R`\x046\x10a\x007W_5`\xE0\x1C\x80c\x16&\xBA~\x14a\0\x8DW\x80c[\xBE\x8B?\x14a\x01\x04W\x80c\xEBV%\xD9\x14a\x01%Wa\0>V[6a\0>W\0[_a\0\x83_6\x80\x80`\x1F\x01` \x80\x91\x04\x02` \x01`@Q\x90\x81\x01`@R\x80\x93\x92\x91\x90\x81\x81R` \x01\x83\x83\x80\x82\x847_\x92\x01\x91\x90\x91RPb\x01\0\0\x93\x92PPa\x01D\x90PV[\x90P\x80Q` \x82\x01\xF3[4\x80\x15a\0\x98W__\xFD[Pa\0\xCFa\0\xA76`\x04a\t\xBFV[\x7F\x16&\xBA~\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x93\x92PPPV[`@Q\x7F\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x90\x91\x16\x81R` \x01`@Q\x80\x91\x03\x90\xF3[4\x80\x15a\x01\x0FW__\xFD[Pa\x01#a\x01\x1E6`\x04a\nZV[a\x01\xC2V[\0[4\x80\x15a\x010W__\xFD[Pa\x01#a\x01?6`\x04a\n\xAAV[a\x07\xD4V[``_\x83s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x83`@Qa\x01l\x91\x90a\n\xE8V[_`@Q\x80\x83\x03\x81\x85Z\xF4\x91PP=\x80_\x81\x14a\x01\xA4W`@Q\x91P`\x1F\x19`?=\x01\x16\x82\x01`@R=\x82R=_` \x84\x01>a\x01\xA9V[``\x91P[P\x92P\x90P\x80a\x01\xBBW\x81Q` \x83\x01\xFD[P\x92\x91PPV[\x7F\x02V]\xBA}h\xDC\xBE\xD6)\x11\0$\xB7\xB5\xE7\x85\xBF\xC1\xA4\x84` E\xEE\xA5\x13\xDE\x8A-\xCF\x99\x80T`\x01\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\x82\x16\x17\x90\x91U`\xFF\x16\x15a\x02\xA3W`@Q\x7F\x08\xC3y\xA0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R` `\x04\x82\x01R`#`$\x82\x01R\x7FprepareSwap can only be called o`D\x82\x01R\x7Fnce\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`d\x82\x01R`\x84\x01[`@Q\x80\x91\x03\x90\xFD[_\x84s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16c\x9BU,\xC2`@Q\x81c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x02\xEDW=__>=_\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x03\x11\x91\x90a\x0B\x14V[`@Q\x7F\xDDb\xED>\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R0`\x04\x82\x01Rs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x83\x16`$\x83\x01R\x91\x92P_\x91\x86\x16\x90c\xDDb\xED>\x90`D\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x03\x86W=__>=_\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x03\xAA\x91\x90a\x0B/V[\x90P\x83\x81\x10\x15a\x06\x07W`@Q\x7F\xEBV%\xD9\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81Rs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x87\x16`\x04\x83\x01R\x83\x16`$\x82\x01R_`D\x82\x01R0\x90c\xEBV%\xD9\x90`d\x01_`@Q\x80\x83\x03\x81_\x87\x80;\x15\x80\x15a\x04&W__\xFD[PZ\xF1\x92PPP\x80\x15a\x047WP`\x01[P`@Q\x7F\xEBV%\xD9\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81Rs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x87\x16`\x04\x83\x01R\x83\x16`$\x82\x01R\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF`D\x82\x01R0\x90c\xEBV%\xD9\x90`d\x01_`@Q\x80\x83\x03\x81_\x87\x80;\x15\x80\x15a\x04\xCAW__\xFD[PZ\xF1\x92PPP\x80\x15a\x04\xDBWP`\x01[P`@Q\x7F\xDDb\xED>\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R0`\x04\x82\x01Rs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x83\x81\x16`$\x83\x01R_\x91\x90\x87\x16\x90c\xDDb\xED>\x90`D\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x05OW=__>=_\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x05s\x91\x90a\x0B/V[\x90P\x84\x81\x10\x15a\x06\x05W`@Q\x7F\x08\xC3y\xA0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R` `\x04\x82\x01R`*`$\x82\x01R\x7Ftrader did not give the required`D\x82\x01R\x7F approvals\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`d\x82\x01R`\x84\x01a\x02\x9AV[P[`@Q\x7Fp\xA0\x821\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R0`\x04\x82\x01R_\x90s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x87\x16\x90cp\xA0\x821\x90`$\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x06qW=__>=_\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x06\x95\x91\x90a\x0B/V[\x90P\x84\x81\x10\x15a\x07\xCBWs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x84\x16cIFf\xB6\x87a\x06\xC6\x84\x89a\x0BFV[`@Q\x7F\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\xE0\x85\x90\x1B\x16\x81Rs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x90\x92\x16`\x04\x83\x01R`$\x82\x01R`D\x01_`@Q\x80\x83\x03\x81_\x87\x80;\x15\x80\x15a\x07.W__\xFD[PZ\xF1\x92PPP\x80\x15a\x07?WP`\x01[a\x07\xCBW`@Q\x7F\x08\xC3y\xA0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R` `\x04\x82\x01R`&`$\x82\x01R\x7Ftrader does not have enough sell`D\x82\x01R\x7F token\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`d\x82\x01R`\x84\x01a\x02\x9AV[PPPPPPPV[a\x07\xF5s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x84\x16\x83\x83a\x07\xFAV[PPPV[`@\x80Qs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x84\x81\x16`$\x83\x01R`D\x80\x83\x01\x85\x90R\x83Q\x80\x84\x03\x90\x91\x01\x81R`d\x90\x92\x01\x90\x92R` \x81\x01\x80Q{\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x7F\t^\xA7\xB3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x17\x90R\x90_\x90a\x08\x8C\x90\x86\x16\x83a\t\x04V[\x90Pa\x08\x97\x81a\t\x18V[a\x08\xFDW`@Q\x7F\x08\xC3y\xA0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R` `\x04\x82\x01R`\x1A`$\x82\x01R\x7FSafeERC20: approval failed\0\0\0\0\0\0`D\x82\x01R`d\x01a\x02\x9AV[PPPPPV[``a\t\x11\x83_\x84a\t=V[\x93\x92PPPV[_\x81Q_\x14\x80a\t7WP\x81\x80` \x01\x90Q\x81\x01\x90a\t7\x91\x90a\x0B~V[\x92\x91PPV[``_\x84s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x84\x84`@Qa\tf\x91\x90a\n\xE8V[_`@Q\x80\x83\x03\x81\x85\x87Z\xF1\x92PPP=\x80_\x81\x14a\t\xA0W`@Q\x91P`\x1F\x19`?=\x01\x16\x82\x01`@R=\x82R=_` \x84\x01>a\t\xA5V[``\x91P[P\x92P\x90P\x80a\t\xB7W\x81Q` \x83\x01\xFD[P\x93\x92PPPV[___`@\x84\x86\x03\x12\x15a\t\xD1W__\xFD[\x835\x92P` \x84\x015g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a\t\xEEW__\xFD[\x84\x01`\x1F\x81\x01\x86\x13a\t\xFEW__\xFD[\x805g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a\n\x14W__\xFD[\x86` \x82\x84\x01\x01\x11\x15a\n%W__\xFD[\x93\x96` \x91\x90\x91\x01\x95P\x92\x93PPPV[s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x16\x81\x14a\nWW__\xFD[PV[____`\x80\x85\x87\x03\x12\x15a\nmW__\xFD[\x845a\nx\x81a\n6V[\x93P` \x85\x015a\n\x88\x81a\n6V[\x92P`@\x85\x015\x91P``\x85\x015a\n\x9F\x81a\n6V[\x93\x96\x92\x95P\x90\x93PPV[___``\x84\x86\x03\x12\x15a\n\xBCW__\xFD[\x835a\n\xC7\x81a\n6V[\x92P` \x84\x015a\n\xD7\x81a\n6V[\x92\x95\x92\x94PPP`@\x91\x90\x91\x015\x90V[_\x82Q_[\x81\x81\x10\x15a\x0B\x07W` \x81\x86\x01\x81\x01Q\x85\x83\x01R\x01a\n\xEDV[P_\x92\x01\x91\x82RP\x91\x90PV[_` \x82\x84\x03\x12\x15a\x0B$W__\xFD[\x81Qa\t\x11\x81a\n6V[_` \x82\x84\x03\x12\x15a\x0B?W__\xFD[PQ\x91\x90PV[\x81\x81\x03\x81\x81\x11\x15a\t7W\x7FNH{q\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0_R`\x11`\x04R`$_\xFD[_` \x82\x84\x03\x12\x15a\x0B\x8EW__\xFD[\x81Q\x80\x15\x15\x81\x14a\t\x11W__\xFD\xFE\xA1dsolcC\0\x08\x1E\0\n", ); /// The runtime bytecode of the contract, as deployed on the network. /// /// ```text - ///0x608060405260043610610037575f3560e01c80631626ba7e1461008d578063542eb77d14610104578063eb5625d9146101255761003e565b3661003e57005b5f6100835f368080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525062010000939250506101449050565b9050805160208201f35b348015610098575f5ffd5b506100cf6100a7366004610b01565b7f1626ba7e000000000000000000000000000000000000000000000000000000009392505050565b6040517fffffffff00000000000000000000000000000000000000000000000000000000909116815260200160405180910390f35b34801561010f575f5ffd5b5061012361011e366004610b9c565b6101c2565b005b348015610130575f5ffd5b5061012361013f366004610c00565b610916565b60605f8373ffffffffffffffffffffffffffffffffffffffff168360405161016c9190610c3e565b5f60405180830381855af49150503d805f81146101a4576040519150601f19603f3d011682016040523d82523d5f602084013e6101a9565b606091505b5092509050806101bb57815160208301fd5b5092915050565b7f02565dba7d68dcbed629110024b7b5e785bfc1a484602045eea513de8a2dcf99805460017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082161790915560ff16156102a3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f70726570617265537761702063616e206f6e6c792062652063616c6c6564206f60448201527f6e6365000000000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b8173ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16036103e4576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201525f9073ffffffffffffffffffffffffffffffffffffffff8616906370a0823190602401602060405180830381865afa158015610340573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103649190610c6a565b9050838110156103e2575f6103798286610c81565b90508047106103e0578373ffffffffffffffffffffffffffffffffffffffff1663d0e30db0826040518263ffffffff1660e01b81526004015f604051808303818588803b1580156103c8575f5ffd5b505af11580156103da573d5f5f3e3d5ffd5b50505050505b505b505b5f8573ffffffffffffffffffffffffffffffffffffffff16639b552cc26040518163ffffffff1660e01b8152600401602060405180830381865afa15801561042e573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104529190610cb9565b6040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff80831660248301529192505f9187169063dd62ed3e90604401602060405180830381865afa1580156104c7573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104eb9190610c6a565b905084811015610748576040517feb5625d900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8088166004830152831660248201525f6044820152309063eb5625d9906064015f604051808303815f87803b158015610567575f5ffd5b505af1925050508015610578575060015b506040517feb5625d900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8088166004830152831660248201527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6044820152309063eb5625d9906064015f604051808303815f87803b15801561060b575f5ffd5b505af192505050801561061c575060015b506040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff83811660248301525f919088169063dd62ed3e90604401602060405180830381865afa158015610690573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106b49190610c6a565b905085811015610746576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f74726164657220646964206e6f7420676976652074686520726571756972656460448201527f20617070726f76616c7300000000000000000000000000000000000000000000606482015260840161029a565b505b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201525f9073ffffffffffffffffffffffffffffffffffffffff8816906370a0823190602401602060405180830381865afa1580156107b2573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107d69190610c6a565b90508581101561090c5773ffffffffffffffffffffffffffffffffffffffff841663494666b688610807848a610c81565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff909216600483015260248201526044015f604051808303815f87803b15801561086f575f5ffd5b505af1925050508015610880575060015b61090c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f74726164657220646f6573206e6f74206861766520656e6f7567682073656c6c60448201527f20746f6b656e0000000000000000000000000000000000000000000000000000606482015260840161029a565b5050505050505050565b61093773ffffffffffffffffffffffffffffffffffffffff8416838361093c565b505050565b6040805173ffffffffffffffffffffffffffffffffffffffff848116602483015260448083018590528351808403909101815260649092019092526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f095ea7b300000000000000000000000000000000000000000000000000000000179052905f906109ce90861683610a46565b90506109d981610a5a565b610a3f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f5361666545524332303a20617070726f76616c206661696c6564000000000000604482015260640161029a565b5050505050565b6060610a53835f84610a7f565b9392505050565b5f81515f1480610a79575081806020019051810190610a799190610cd4565b92915050565b60605f8473ffffffffffffffffffffffffffffffffffffffff168484604051610aa89190610c3e565b5f6040518083038185875af1925050503d805f8114610ae2576040519150601f19603f3d011682016040523d82523d5f602084013e610ae7565b606091505b509250905080610af957815160208301fd5b509392505050565b5f5f5f60408486031215610b13575f5ffd5b83359250602084013567ffffffffffffffff811115610b30575f5ffd5b8401601f81018613610b40575f5ffd5b803567ffffffffffffffff811115610b56575f5ffd5b866020828401011115610b67575f5ffd5b939660209190910195509293505050565b73ffffffffffffffffffffffffffffffffffffffff81168114610b99575f5ffd5b50565b5f5f5f5f5f60a08688031215610bb0575f5ffd5b8535610bbb81610b78565b94506020860135610bcb81610b78565b9350604086013592506060860135610be281610b78565b91506080860135610bf281610b78565b809150509295509295909350565b5f5f5f60608486031215610c12575f5ffd5b8335610c1d81610b78565b92506020840135610c2d81610b78565b929592945050506040919091013590565b5f82515f5b81811015610c5d5760208186018101518583015201610c43565b505f920191825250919050565b5f60208284031215610c7a575f5ffd5b5051919050565b81810381811115610a79577f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f60208284031215610cc9575f5ffd5b8151610a5381610b78565b5f60208284031215610ce4575f5ffd5b81518015158114610a53575f5ffdfea164736f6c634300081e000a + ///0x608060405260043610610037575f3560e01c80631626ba7e1461008d5780635bbe8b3f14610104578063eb5625d9146101255761003e565b3661003e57005b5f6100835f368080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525062010000939250506101449050565b9050805160208201f35b348015610098575f5ffd5b506100cf6100a73660046109bf565b7f1626ba7e000000000000000000000000000000000000000000000000000000009392505050565b6040517fffffffff00000000000000000000000000000000000000000000000000000000909116815260200160405180910390f35b34801561010f575f5ffd5b5061012361011e366004610a5a565b6101c2565b005b348015610130575f5ffd5b5061012361013f366004610aaa565b6107d4565b60605f8373ffffffffffffffffffffffffffffffffffffffff168360405161016c9190610ae8565b5f60405180830381855af49150503d805f81146101a4576040519150601f19603f3d011682016040523d82523d5f602084013e6101a9565b606091505b5092509050806101bb57815160208301fd5b5092915050565b7f02565dba7d68dcbed629110024b7b5e785bfc1a484602045eea513de8a2dcf99805460017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082161790915560ff16156102a3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f70726570617265537761702063616e206f6e6c792062652063616c6c6564206f60448201527f6e6365000000000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b5f8473ffffffffffffffffffffffffffffffffffffffff16639b552cc26040518163ffffffff1660e01b8152600401602060405180830381865afa1580156102ed573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103119190610b14565b6040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff80831660248301529192505f9186169063dd62ed3e90604401602060405180830381865afa158015610386573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103aa9190610b2f565b905083811015610607576040517feb5625d900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8087166004830152831660248201525f6044820152309063eb5625d9906064015f604051808303815f87803b158015610426575f5ffd5b505af1925050508015610437575060015b506040517feb5625d900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8087166004830152831660248201527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6044820152309063eb5625d9906064015f604051808303815f87803b1580156104ca575f5ffd5b505af19250505080156104db575060015b506040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff83811660248301525f919087169063dd62ed3e90604401602060405180830381865afa15801561054f573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105739190610b2f565b905084811015610605576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f74726164657220646964206e6f7420676976652074686520726571756972656460448201527f20617070726f76616c7300000000000000000000000000000000000000000000606482015260840161029a565b505b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201525f9073ffffffffffffffffffffffffffffffffffffffff8716906370a0823190602401602060405180830381865afa158015610671573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106959190610b2f565b9050848110156107cb5773ffffffffffffffffffffffffffffffffffffffff841663494666b6876106c68489610b46565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff909216600483015260248201526044015f604051808303815f87803b15801561072e575f5ffd5b505af192505050801561073f575060015b6107cb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f74726164657220646f6573206e6f74206861766520656e6f7567682073656c6c60448201527f20746f6b656e0000000000000000000000000000000000000000000000000000606482015260840161029a565b50505050505050565b6107f573ffffffffffffffffffffffffffffffffffffffff841683836107fa565b505050565b6040805173ffffffffffffffffffffffffffffffffffffffff848116602483015260448083018590528351808403909101815260649092019092526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f095ea7b300000000000000000000000000000000000000000000000000000000179052905f9061088c90861683610904565b905061089781610918565b6108fd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f5361666545524332303a20617070726f76616c206661696c6564000000000000604482015260640161029a565b5050505050565b6060610911835f8461093d565b9392505050565b5f81515f14806109375750818060200190518101906109379190610b7e565b92915050565b60605f8473ffffffffffffffffffffffffffffffffffffffff1684846040516109669190610ae8565b5f6040518083038185875af1925050503d805f81146109a0576040519150601f19603f3d011682016040523d82523d5f602084013e6109a5565b606091505b5092509050806109b757815160208301fd5b509392505050565b5f5f5f604084860312156109d1575f5ffd5b83359250602084013567ffffffffffffffff8111156109ee575f5ffd5b8401601f810186136109fe575f5ffd5b803567ffffffffffffffff811115610a14575f5ffd5b866020828401011115610a25575f5ffd5b939660209190910195509293505050565b73ffffffffffffffffffffffffffffffffffffffff81168114610a57575f5ffd5b50565b5f5f5f5f60808587031215610a6d575f5ffd5b8435610a7881610a36565b93506020850135610a8881610a36565b9250604085013591506060850135610a9f81610a36565b939692955090935050565b5f5f5f60608486031215610abc575f5ffd5b8335610ac781610a36565b92506020840135610ad781610a36565b929592945050506040919091013590565b5f82515f5b81811015610b075760208186018101518583015201610aed565b505f920191825250919050565b5f60208284031215610b24575f5ffd5b815161091181610a36565b5f60208284031215610b3f575f5ffd5b5051919050565b81810381811115610937577f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f60208284031215610b8e575f5ffd5b81518015158114610911575f5ffdfea164736f6c634300081e000a /// ``` #[rustfmt::skip] #[allow(clippy::all)] pub static DEPLOYED_BYTECODE: alloy_sol_types::private::Bytes = alloy_sol_types::private::Bytes::from_static( - b"`\x80`@R`\x046\x10a\x007W_5`\xE0\x1C\x80c\x16&\xBA~\x14a\0\x8DW\x80cT.\xB7}\x14a\x01\x04W\x80c\xEBV%\xD9\x14a\x01%Wa\0>V[6a\0>W\0[_a\0\x83_6\x80\x80`\x1F\x01` \x80\x91\x04\x02` \x01`@Q\x90\x81\x01`@R\x80\x93\x92\x91\x90\x81\x81R` \x01\x83\x83\x80\x82\x847_\x92\x01\x91\x90\x91RPb\x01\0\0\x93\x92PPa\x01D\x90PV[\x90P\x80Q` \x82\x01\xF3[4\x80\x15a\0\x98W__\xFD[Pa\0\xCFa\0\xA76`\x04a\x0B\x01V[\x7F\x16&\xBA~\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x93\x92PPPV[`@Q\x7F\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x90\x91\x16\x81R` \x01`@Q\x80\x91\x03\x90\xF3[4\x80\x15a\x01\x0FW__\xFD[Pa\x01#a\x01\x1E6`\x04a\x0B\x9CV[a\x01\xC2V[\0[4\x80\x15a\x010W__\xFD[Pa\x01#a\x01?6`\x04a\x0C\0V[a\t\x16V[``_\x83s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x83`@Qa\x01l\x91\x90a\x0C>V[_`@Q\x80\x83\x03\x81\x85Z\xF4\x91PP=\x80_\x81\x14a\x01\xA4W`@Q\x91P`\x1F\x19`?=\x01\x16\x82\x01`@R=\x82R=_` \x84\x01>a\x01\xA9V[``\x91P[P\x92P\x90P\x80a\x01\xBBW\x81Q` \x83\x01\xFD[P\x92\x91PPV[\x7F\x02V]\xBA}h\xDC\xBE\xD6)\x11\0$\xB7\xB5\xE7\x85\xBF\xC1\xA4\x84` E\xEE\xA5\x13\xDE\x8A-\xCF\x99\x80T`\x01\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\x82\x16\x17\x90\x91U`\xFF\x16\x15a\x02\xA3W`@Q\x7F\x08\xC3y\xA0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R` `\x04\x82\x01R`#`$\x82\x01R\x7FprepareSwap can only be called o`D\x82\x01R\x7Fnce\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`d\x82\x01R`\x84\x01[`@Q\x80\x91\x03\x90\xFD[\x81s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x84s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x03a\x03\xE4W`@Q\x7Fp\xA0\x821\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R0`\x04\x82\x01R_\x90s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x86\x16\x90cp\xA0\x821\x90`$\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x03@W=__>=_\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x03d\x91\x90a\x0CjV[\x90P\x83\x81\x10\x15a\x03\xE2W_a\x03y\x82\x86a\x0C\x81V[\x90P\x80G\x10a\x03\xE0W\x83s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16c\xD0\xE3\r\xB0\x82`@Q\x82c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01_`@Q\x80\x83\x03\x81\x85\x88\x80;\x15\x80\x15a\x03\xC8W__\xFD[PZ\xF1\x15\x80\x15a\x03\xDAW=__>=_\xFD[PPPPP[P[P[_\x85s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16c\x9BU,\xC2`@Q\x81c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x04.W=__>=_\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x04R\x91\x90a\x0C\xB9V[`@Q\x7F\xDDb\xED>\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R0`\x04\x82\x01Rs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x83\x16`$\x83\x01R\x91\x92P_\x91\x87\x16\x90c\xDDb\xED>\x90`D\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x04\xC7W=__>=_\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x04\xEB\x91\x90a\x0CjV[\x90P\x84\x81\x10\x15a\x07HW`@Q\x7F\xEBV%\xD9\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81Rs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x88\x16`\x04\x83\x01R\x83\x16`$\x82\x01R_`D\x82\x01R0\x90c\xEBV%\xD9\x90`d\x01_`@Q\x80\x83\x03\x81_\x87\x80;\x15\x80\x15a\x05gW__\xFD[PZ\xF1\x92PPP\x80\x15a\x05xWP`\x01[P`@Q\x7F\xEBV%\xD9\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81Rs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x88\x16`\x04\x83\x01R\x83\x16`$\x82\x01R\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF`D\x82\x01R0\x90c\xEBV%\xD9\x90`d\x01_`@Q\x80\x83\x03\x81_\x87\x80;\x15\x80\x15a\x06\x0BW__\xFD[PZ\xF1\x92PPP\x80\x15a\x06\x1CWP`\x01[P`@Q\x7F\xDDb\xED>\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R0`\x04\x82\x01Rs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x83\x81\x16`$\x83\x01R_\x91\x90\x88\x16\x90c\xDDb\xED>\x90`D\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x06\x90W=__>=_\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x06\xB4\x91\x90a\x0CjV[\x90P\x85\x81\x10\x15a\x07FW`@Q\x7F\x08\xC3y\xA0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R` `\x04\x82\x01R`*`$\x82\x01R\x7Ftrader did not give the required`D\x82\x01R\x7F approvals\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`d\x82\x01R`\x84\x01a\x02\x9AV[P[`@Q\x7Fp\xA0\x821\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R0`\x04\x82\x01R_\x90s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x88\x16\x90cp\xA0\x821\x90`$\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x07\xB2W=__>=_\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x07\xD6\x91\x90a\x0CjV[\x90P\x85\x81\x10\x15a\t\x0CWs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x84\x16cIFf\xB6\x88a\x08\x07\x84\x8Aa\x0C\x81V[`@Q\x7F\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\xE0\x85\x90\x1B\x16\x81Rs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x90\x92\x16`\x04\x83\x01R`$\x82\x01R`D\x01_`@Q\x80\x83\x03\x81_\x87\x80;\x15\x80\x15a\x08oW__\xFD[PZ\xF1\x92PPP\x80\x15a\x08\x80WP`\x01[a\t\x0CW`@Q\x7F\x08\xC3y\xA0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R` `\x04\x82\x01R`&`$\x82\x01R\x7Ftrader does not have enough sell`D\x82\x01R\x7F token\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`d\x82\x01R`\x84\x01a\x02\x9AV[PPPPPPPPV[a\t7s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x84\x16\x83\x83a\tV[_`@Q\x80\x83\x03\x81\x85\x87Z\xF1\x92PPP=\x80_\x81\x14a\n\xE2W`@Q\x91P`\x1F\x19`?=\x01\x16\x82\x01`@R=\x82R=_` \x84\x01>a\n\xE7V[``\x91P[P\x92P\x90P\x80a\n\xF9W\x81Q` \x83\x01\xFD[P\x93\x92PPPV[___`@\x84\x86\x03\x12\x15a\x0B\x13W__\xFD[\x835\x92P` \x84\x015g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a\x0B0W__\xFD[\x84\x01`\x1F\x81\x01\x86\x13a\x0B@W__\xFD[\x805g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a\x0BVW__\xFD[\x86` \x82\x84\x01\x01\x11\x15a\x0BgW__\xFD[\x93\x96` \x91\x90\x91\x01\x95P\x92\x93PPPV[s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x16\x81\x14a\x0B\x99W__\xFD[PV[_____`\xA0\x86\x88\x03\x12\x15a\x0B\xB0W__\xFD[\x855a\x0B\xBB\x81a\x0BxV[\x94P` \x86\x015a\x0B\xCB\x81a\x0BxV[\x93P`@\x86\x015\x92P``\x86\x015a\x0B\xE2\x81a\x0BxV[\x91P`\x80\x86\x015a\x0B\xF2\x81a\x0BxV[\x80\x91PP\x92\x95P\x92\x95\x90\x93PV[___``\x84\x86\x03\x12\x15a\x0C\x12W__\xFD[\x835a\x0C\x1D\x81a\x0BxV[\x92P` \x84\x015a\x0C-\x81a\x0BxV[\x92\x95\x92\x94PPP`@\x91\x90\x91\x015\x90V[_\x82Q_[\x81\x81\x10\x15a\x0C]W` \x81\x86\x01\x81\x01Q\x85\x83\x01R\x01a\x0CCV[P_\x92\x01\x91\x82RP\x91\x90PV[_` \x82\x84\x03\x12\x15a\x0CzW__\xFD[PQ\x91\x90PV[\x81\x81\x03\x81\x81\x11\x15a\nyW\x7FNH{q\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0_R`\x11`\x04R`$_\xFD[_` \x82\x84\x03\x12\x15a\x0C\xC9W__\xFD[\x81Qa\nS\x81a\x0BxV[_` \x82\x84\x03\x12\x15a\x0C\xE4W__\xFD[\x81Q\x80\x15\x15\x81\x14a\nSW__\xFD\xFE\xA1dsolcC\0\x08\x1E\0\n", + b"`\x80`@R`\x046\x10a\x007W_5`\xE0\x1C\x80c\x16&\xBA~\x14a\0\x8DW\x80c[\xBE\x8B?\x14a\x01\x04W\x80c\xEBV%\xD9\x14a\x01%Wa\0>V[6a\0>W\0[_a\0\x83_6\x80\x80`\x1F\x01` \x80\x91\x04\x02` \x01`@Q\x90\x81\x01`@R\x80\x93\x92\x91\x90\x81\x81R` \x01\x83\x83\x80\x82\x847_\x92\x01\x91\x90\x91RPb\x01\0\0\x93\x92PPa\x01D\x90PV[\x90P\x80Q` \x82\x01\xF3[4\x80\x15a\0\x98W__\xFD[Pa\0\xCFa\0\xA76`\x04a\t\xBFV[\x7F\x16&\xBA~\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x93\x92PPPV[`@Q\x7F\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x90\x91\x16\x81R` \x01`@Q\x80\x91\x03\x90\xF3[4\x80\x15a\x01\x0FW__\xFD[Pa\x01#a\x01\x1E6`\x04a\nZV[a\x01\xC2V[\0[4\x80\x15a\x010W__\xFD[Pa\x01#a\x01?6`\x04a\n\xAAV[a\x07\xD4V[``_\x83s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x83`@Qa\x01l\x91\x90a\n\xE8V[_`@Q\x80\x83\x03\x81\x85Z\xF4\x91PP=\x80_\x81\x14a\x01\xA4W`@Q\x91P`\x1F\x19`?=\x01\x16\x82\x01`@R=\x82R=_` \x84\x01>a\x01\xA9V[``\x91P[P\x92P\x90P\x80a\x01\xBBW\x81Q` \x83\x01\xFD[P\x92\x91PPV[\x7F\x02V]\xBA}h\xDC\xBE\xD6)\x11\0$\xB7\xB5\xE7\x85\xBF\xC1\xA4\x84` E\xEE\xA5\x13\xDE\x8A-\xCF\x99\x80T`\x01\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\x82\x16\x17\x90\x91U`\xFF\x16\x15a\x02\xA3W`@Q\x7F\x08\xC3y\xA0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R` `\x04\x82\x01R`#`$\x82\x01R\x7FprepareSwap can only be called o`D\x82\x01R\x7Fnce\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`d\x82\x01R`\x84\x01[`@Q\x80\x91\x03\x90\xFD[_\x84s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16c\x9BU,\xC2`@Q\x81c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x02\xEDW=__>=_\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x03\x11\x91\x90a\x0B\x14V[`@Q\x7F\xDDb\xED>\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R0`\x04\x82\x01Rs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x83\x16`$\x83\x01R\x91\x92P_\x91\x86\x16\x90c\xDDb\xED>\x90`D\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x03\x86W=__>=_\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x03\xAA\x91\x90a\x0B/V[\x90P\x83\x81\x10\x15a\x06\x07W`@Q\x7F\xEBV%\xD9\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81Rs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x87\x16`\x04\x83\x01R\x83\x16`$\x82\x01R_`D\x82\x01R0\x90c\xEBV%\xD9\x90`d\x01_`@Q\x80\x83\x03\x81_\x87\x80;\x15\x80\x15a\x04&W__\xFD[PZ\xF1\x92PPP\x80\x15a\x047WP`\x01[P`@Q\x7F\xEBV%\xD9\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81Rs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x87\x16`\x04\x83\x01R\x83\x16`$\x82\x01R\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF`D\x82\x01R0\x90c\xEBV%\xD9\x90`d\x01_`@Q\x80\x83\x03\x81_\x87\x80;\x15\x80\x15a\x04\xCAW__\xFD[PZ\xF1\x92PPP\x80\x15a\x04\xDBWP`\x01[P`@Q\x7F\xDDb\xED>\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R0`\x04\x82\x01Rs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x83\x81\x16`$\x83\x01R_\x91\x90\x87\x16\x90c\xDDb\xED>\x90`D\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x05OW=__>=_\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x05s\x91\x90a\x0B/V[\x90P\x84\x81\x10\x15a\x06\x05W`@Q\x7F\x08\xC3y\xA0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R` `\x04\x82\x01R`*`$\x82\x01R\x7Ftrader did not give the required`D\x82\x01R\x7F approvals\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`d\x82\x01R`\x84\x01a\x02\x9AV[P[`@Q\x7Fp\xA0\x821\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R0`\x04\x82\x01R_\x90s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x87\x16\x90cp\xA0\x821\x90`$\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x06qW=__>=_\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x06\x95\x91\x90a\x0B/V[\x90P\x84\x81\x10\x15a\x07\xCBWs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x84\x16cIFf\xB6\x87a\x06\xC6\x84\x89a\x0BFV[`@Q\x7F\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\xE0\x85\x90\x1B\x16\x81Rs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x90\x92\x16`\x04\x83\x01R`$\x82\x01R`D\x01_`@Q\x80\x83\x03\x81_\x87\x80;\x15\x80\x15a\x07.W__\xFD[PZ\xF1\x92PPP\x80\x15a\x07?WP`\x01[a\x07\xCBW`@Q\x7F\x08\xC3y\xA0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R` `\x04\x82\x01R`&`$\x82\x01R\x7Ftrader does not have enough sell`D\x82\x01R\x7F token\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`d\x82\x01R`\x84\x01a\x02\x9AV[PPPPPPPV[a\x07\xF5s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x84\x16\x83\x83a\x07\xFAV[PPPV[`@\x80Qs\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x84\x81\x16`$\x83\x01R`D\x80\x83\x01\x85\x90R\x83Q\x80\x84\x03\x90\x91\x01\x81R`d\x90\x92\x01\x90\x92R` \x81\x01\x80Q{\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x7F\t^\xA7\xB3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x17\x90R\x90_\x90a\x08\x8C\x90\x86\x16\x83a\t\x04V[\x90Pa\x08\x97\x81a\t\x18V[a\x08\xFDW`@Q\x7F\x08\xC3y\xA0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81R` `\x04\x82\x01R`\x1A`$\x82\x01R\x7FSafeERC20: approval failed\0\0\0\0\0\0`D\x82\x01R`d\x01a\x02\x9AV[PPPPPV[``a\t\x11\x83_\x84a\t=V[\x93\x92PPPV[_\x81Q_\x14\x80a\t7WP\x81\x80` \x01\x90Q\x81\x01\x90a\t7\x91\x90a\x0B~V[\x92\x91PPV[``_\x84s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\x84\x84`@Qa\tf\x91\x90a\n\xE8V[_`@Q\x80\x83\x03\x81\x85\x87Z\xF1\x92PPP=\x80_\x81\x14a\t\xA0W`@Q\x91P`\x1F\x19`?=\x01\x16\x82\x01`@R=\x82R=_` \x84\x01>a\t\xA5V[``\x91P[P\x92P\x90P\x80a\t\xB7W\x81Q` \x83\x01\xFD[P\x93\x92PPPV[___`@\x84\x86\x03\x12\x15a\t\xD1W__\xFD[\x835\x92P` \x84\x015g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a\t\xEEW__\xFD[\x84\x01`\x1F\x81\x01\x86\x13a\t\xFEW__\xFD[\x805g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a\n\x14W__\xFD[\x86` \x82\x84\x01\x01\x11\x15a\n%W__\xFD[\x93\x96` \x91\x90\x91\x01\x95P\x92\x93PPPV[s\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x16\x81\x14a\nWW__\xFD[PV[____`\x80\x85\x87\x03\x12\x15a\nmW__\xFD[\x845a\nx\x81a\n6V[\x93P` \x85\x015a\n\x88\x81a\n6V[\x92P`@\x85\x015\x91P``\x85\x015a\n\x9F\x81a\n6V[\x93\x96\x92\x95P\x90\x93PPV[___``\x84\x86\x03\x12\x15a\n\xBCW__\xFD[\x835a\n\xC7\x81a\n6V[\x92P` \x84\x015a\n\xD7\x81a\n6V[\x92\x95\x92\x94PPP`@\x91\x90\x91\x015\x90V[_\x82Q_[\x81\x81\x10\x15a\x0B\x07W` \x81\x86\x01\x81\x01Q\x85\x83\x01R\x01a\n\xEDV[P_\x92\x01\x91\x82RP\x91\x90PV[_` \x82\x84\x03\x12\x15a\x0B$W__\xFD[\x81Qa\t\x11\x81a\n6V[_` \x82\x84\x03\x12\x15a\x0B?W__\xFD[PQ\x91\x90PV[\x81\x81\x03\x81\x81\x11\x15a\t7W\x7FNH{q\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0_R`\x11`\x04R`$_\xFD[_` \x82\x84\x03\x12\x15a\x0B\x8EW__\xFD[\x81Q\x80\x15\x15\x81\x14a\t\x11W__\xFD\xFE\xA1dsolcC\0\x08\x1E\0\n", ); #[derive(Default, Debug, PartialEq, Eq, Hash)] - /**Function with signature `ensureTradePreconditions(address,address,uint256,address,address)` and selector `0x542eb77d`. + /**Function with signature `ensureTradePreconditions(address,address,uint256,address)` and selector `0x5bbe8b3f`. ```solidity - function ensureTradePreconditions(address settlementContract, address sellToken, uint256 sellAmount, address nativeToken, address spardose) external; + function ensureTradePreconditions(address settlementContract, address sellToken, uint256 sellAmount, address spardose) external; ```*/ #[allow(non_camel_case_types, non_snake_case, clippy::pub_underscore_fields)] #[derive(Clone)] @@ -158,12 +153,10 @@ pub mod Trader { #[allow(missing_docs)] pub sellAmount: alloy_sol_types::private::primitives::aliases::U256, #[allow(missing_docs)] - pub nativeToken: alloy_sol_types::private::Address, - #[allow(missing_docs)] pub spardose: alloy_sol_types::private::Address, } ///Container type for the return parameters of the - /// [`ensureTradePreconditions(address,address,uint256,address, + /// [`ensureTradePreconditions(address,address,uint256, /// address)`](ensureTradePreconditionsCall) function. #[allow(non_camel_case_types, non_snake_case, clippy::pub_underscore_fields)] #[derive(Clone)] @@ -184,7 +177,6 @@ pub mod Trader { alloy_sol_types::sol_data::Address, alloy_sol_types::sol_data::Uint<256>, alloy_sol_types::sol_data::Address, - alloy_sol_types::sol_data::Address, ); #[doc(hidden)] type UnderlyingRustTuple<'a> = ( @@ -192,7 +184,6 @@ pub mod Trader { alloy_sol_types::private::Address, alloy_sol_types::private::primitives::aliases::U256, alloy_sol_types::private::Address, - alloy_sol_types::private::Address, ); #[cfg(test)] #[allow(dead_code, unreachable_patterns)] @@ -211,7 +202,6 @@ pub mod Trader { value.settlementContract, value.sellToken, value.sellAmount, - value.nativeToken, value.spardose, ) } @@ -224,8 +214,7 @@ pub mod Trader { settlementContract: tuple.0, sellToken: tuple.1, sellAmount: tuple.2, - nativeToken: tuple.3, - spardose: tuple.4, + spardose: tuple.3, } } } @@ -275,16 +264,15 @@ pub mod Trader { alloy_sol_types::sol_data::Address, alloy_sol_types::sol_data::Uint<256>, alloy_sol_types::sol_data::Address, - alloy_sol_types::sol_data::Address, ); type Return = ensureTradePreconditionsReturn; type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; type ReturnTuple<'a> = (); type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; - const SELECTOR: [u8; 4] = [84u8, 46u8, 183u8, 125u8]; + const SELECTOR: [u8; 4] = [91u8, 190u8, 139u8, 63u8]; const SIGNATURE: &'static str = - "ensureTradePreconditions(address,address,uint256,address,address)"; + "ensureTradePreconditions(address,address,uint256,address)"; #[inline] fn new<'a>( @@ -305,9 +293,6 @@ pub mod Trader { as alloy_sol_types::SolType>::tokenize( &self.sellAmount, ), - ::tokenize( - &self.nativeToken, - ), ::tokenize( &self.spardose, ), @@ -679,7 +664,7 @@ pub mod Trader { /// Prefer using `SolInterface` methods instead. pub const SELECTORS: &'static [[u8; 4usize]] = &[ [22u8, 38u8, 186u8, 126u8], - [84u8, 46u8, 183u8, 125u8], + [91u8, 190u8, 139u8, 63u8], [235u8, 86u8, 37u8, 217u8], ]; /// The signatures in the same order as `SELECTORS`. @@ -1027,14 +1012,12 @@ pub mod Trader { settlementContract: alloy_sol_types::private::Address, sellToken: alloy_sol_types::private::Address, sellAmount: alloy_sol_types::private::primitives::aliases::U256, - nativeToken: alloy_sol_types::private::Address, spardose: alloy_sol_types::private::Address, ) -> alloy_contract::SolCallBuilder<&P, ensureTradePreconditionsCall, N> { self.call_builder(&ensureTradePreconditionsCall { settlementContract, sellToken, sellAmount, - nativeToken, spardose, }) } diff --git a/contracts/solidity/Solver.sol b/contracts/solidity/Solver.sol index b141f28653..0698e0a47d 100644 --- a/contracts/solidity/Solver.sol +++ b/contracts/solidity/Solver.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.30; -import { IERC20, INativeERC20 } from "./interfaces/IERC20.sol"; +import { IERC20 } from "./interfaces/IERC20.sol"; import { Interaction, Trade, ISettlement } from "./interfaces/ISettlement.sol"; import { Caller } from "./libraries/Caller.sol"; import { Math } from "./libraries/Math.sol"; @@ -117,7 +117,6 @@ contract Solver layout at 0x14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff1 ISettlement settlementContract, address sellToken, uint256 sellAmount, - address nativeToken, address spardose ) external { uint256 gasStart = gasleft(); @@ -125,7 +124,6 @@ contract Solver layout at 0x14f5b2c185fc03c75c787d1f0e10ea137cc6d235a0047448eff1 settlementContract, sellToken, sellAmount, - nativeToken, spardose ); // Account for costs of gas used outside of metered section. diff --git a/contracts/solidity/Trader.sol b/contracts/solidity/Trader.sol index f73e805ec9..be5dcdc158 100644 --- a/contracts/solidity/Trader.sol +++ b/contracts/solidity/Trader.sol @@ -55,34 +55,15 @@ contract Trader layout at 0x02565dba7d68dcbed629110024b7b5e785bfc1a484602045eea5 /// a stable address in tests. /// @param sellToken - token being sold by the trade /// @param sellAmount - expected amount to be sold according to the quote - /// @param nativeToken - ERC20 version of the chain's native token /// @param spardose - piggy bank for requesting additional funds function ensureTradePreconditions( ISettlement settlementContract, address sellToken, uint256 sellAmount, - address nativeToken, address spardose ) external { require(!triggerInitialization(), "prepareSwap can only be called once"); - if (sellToken == nativeToken) { - uint256 availableNativeToken = IERC20(sellToken).balanceOf(address(this)); - if (availableNativeToken < sellAmount) { - uint256 amountToWrap = sellAmount - availableNativeToken; - // If the user has sufficient balance, simulate the wrapping the missing - // `ETH` so the user doesn't have to spend gas on that just to get a quote. - // If they are happy with the quote and want to create an order they will - // actually have to do the wrapping, though. Note that we don't attempt to - // wrap if the user doesn't have sufficient `ETH` balance, since that would - // revert. Instead, we fall-through so that we handle insufficient sell - // token balances uniformly for all tokens. - if (address(this).balance >= amountToWrap) { - INativeERC20(nativeToken).deposit{value: amountToWrap}(); - } - } - } - address vaultRelayer = settlementContract.vaultRelayer(); uint256 currentAllowance = IERC20(sellToken).allowance(address(this), vaultRelayer); if (currentAllowance < sellAmount) { diff --git a/crates/price-estimation/src/trade_verifier/mod.rs b/crates/price-estimation/src/trade_verifier/mod.rs index 2bca6404e5..a96bcd1fb3 100644 --- a/crates/price-estimation/src/trade_verifier/mod.rs +++ b/crates/price-estimation/src/trade_verifier/mod.rs @@ -521,7 +521,6 @@ impl TradeVerifier { settlementContract: *self.settlement.address(), sellToken: query.sell_token, sellAmount: sell_amount, - nativeToken: self.simulator.native_token, spardose: Self::SPARDOSE, } .abi_encode(); From 9aec4950dbee8bf7aeee8f9c4d24961fe58d3805 Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Thu, 30 Apr 2026 08:41:13 +0000 Subject: [PATCH 29/44] Nicer API for handling state overrides --- crates/orderbook/src/orderbook.rs | 4 +- crates/simulator/src/lib.rs | 1 - crates/simulator/src/simulation_builder.rs | 92 ++++---- crates/simulator/src/simulation_encoding.rs | 208 ++++++++++++++---- .../simulator/src/state_override_helpers.rs | 68 ------ 5 files changed, 219 insertions(+), 154 deletions(-) delete mode 100644 crates/simulator/src/state_override_helpers.rs diff --git a/crates/orderbook/src/orderbook.rs b/crates/orderbook/src/orderbook.rs index d7fcad8bb8..acc718b694 100644 --- a/crates/orderbook/src/orderbook.rs +++ b/crates/orderbook/src/orderbook.rs @@ -652,7 +652,7 @@ impl Orderbook { .unwrap_or(simulation_builder::Block::Latest), ) .with_prices(simulation_builder::Prices::Limit) - .fund_settlement_contract_with_buy_tokens() + .with_override(simulation_builder::AccountOverrideRequest::BuyTokensForBuffers) .from_solver(simulation_builder::Solver::Fake(None)) .build() .await @@ -710,7 +710,7 @@ impl Orderbook { .unwrap_or(simulation_builder::Block::Latest), ) .with_prices(simulation_builder::Prices::Limit) - .fund_settlement_contract_with_buy_tokens() + .with_override(simulation_builder::AccountOverrideRequest::BuyTokensForBuffers) .from_solver(simulation_builder::Solver::Fake(None)) .build() .await diff --git a/crates/simulator/src/lib.rs b/crates/simulator/src/lib.rs index a0d66dd65f..2725227850 100644 --- a/crates/simulator/src/lib.rs +++ b/crates/simulator/src/lib.rs @@ -2,7 +2,6 @@ pub mod encoding; pub mod ethereum; pub mod simulation_builder; mod simulation_encoding; -pub mod state_override_helpers; pub mod swap_simulator; pub mod tenderly; mod utils; diff --git a/crates/simulator/src/simulation_builder.rs b/crates/simulator/src/simulation_builder.rs index e4f1d21563..c51bc9073b 100644 --- a/crates/simulator/src/simulation_builder.rs +++ b/crates/simulator/src/simulation_builder.rs @@ -3,7 +3,7 @@ use { encoding::{EncodedSettlement, WrapperCall}, tenderly::dto::StateObject, }, - alloy_primitives::{Address, Bytes, TxKind, U256}, + alloy_primitives::{Address, B256, Bytes, TxKind, U256}, alloy_provider::{DynProvider, Provider}, alloy_rpc_types::{ TransactionRequest, @@ -12,7 +12,7 @@ use { alloy_sol_types::SolCall, alloy_transport::RpcError, anyhow::{Context, Result}, - balance_overrides::{BalanceOverrideRequest, BalanceOverriding}, + balance_overrides::BalanceOverriding, ethrpc::block_stream::CurrentBlockWatcher, model::{ DomainSeparator, @@ -81,8 +81,7 @@ impl SettlementSimulator { solver: None, auction_id: None, state_overrides: StateOverride::default(), - fund_settlement_contract: false, - fund_requests: vec![], + account_override_requests: vec![], block: Block::Latest, } } @@ -114,8 +113,7 @@ pub struct SimulationBuilder { pub(crate) auction_id: Option, pub(crate) state_overrides: StateOverride, pub(crate) simulator: SettlementSimulator, - pub(crate) fund_settlement_contract: bool, - pub(crate) fund_requests: Vec, + pub(crate) account_override_requests: Vec, pub(crate) block: Block, } @@ -162,16 +160,6 @@ impl SimulationBuilder { self } - pub fn state_override( - mut self, - address: Address, - account_override: impl Into, - ) -> Self { - self.state_overrides - .insert(address, account_override.into()); - self - } - pub fn at_block(mut self, block: Block) -> Self { self.block = block; self @@ -234,28 +222,12 @@ impl SimulationBuilder { Ok(self) } - /// Override the settlement contract's buy token balance so it can pay out - /// the order without any external liquidity. The required amount is derived - /// from the order's executed amount and clearing prices at `build()` time. - pub fn fund_settlement_contract_with_buy_tokens(mut self) -> Self { - self.fund_settlement_contract = true; - self - } - - /// Override the token balance of an arbitrary address. Useful for seeding - /// an intermediate funding contract (e.g. Spardose) with sell tokens before - /// the simulation runs. - pub fn fund_address_with_tokens( - mut self, - holder: Address, - token: Address, - amount: U256, - ) -> Self { - self.fund_requests.push(BalanceOverrideRequest { - token, - holder, - amount, - }); + /// Queues an [`AccountOverrideRequest`] to be resolved and applied during + /// [`build`](Self::build). Multiple requests may target the same address; + /// non-conflicting fields are merged and conflicts produce + /// [`BuildError::ConflictingStateOverrides`]. + pub fn with_override(mut self, request: AccountOverrideRequest) -> Self { + self.account_override_requests.push(request); self } @@ -515,4 +487,48 @@ pub enum BuildError { AppDataParse(#[source] serde_json::Error), #[error("both wrappers and flashloans cannot be encoded in the same settlement")] FlashloanWrappersIncompatible, + #[error("conflicting state overrides for the same account: {0}")] + ConflictingStateOverrides(#[source] MergeConflict), +} + +pub enum AccountOverrideRequest { + /// Gives the address a huge amount of ETH. + SufficientEthBalance(Address), + /// Allowlists an address as a solver to let it settle orders. + AuthenticateAddress(Address), + /// Computes necessary state overrides for the requested balance. + Balance { + holder: Address, + token: Address, + amount: U256, + }, + /// Gives the settlement contract enough buy tokens to pay for all + /// orders. + BuyTokensForBuffers, + /// Deploys the provided code at the requested address. + Code { account: Address, code: Bytes }, + /// Allows to build fully custom overrides for the most exotic use cases. + Custom { + account: Address, + state: AccountOverride, + }, + // TODO: add Allowance +} + +/// Error returned when two [`AccountOverride`]s set the same field for the same +/// address and cannot be merged. +#[derive(Debug, thiserror::Error)] +pub enum MergeConflict { + #[error("both overrides set the ETH balance")] + Balance, + #[error("both overrides set the nonce")] + Nonce, + #[error("both overrides set the contract code")] + Code, + #[error("both overrides replace the full storage state")] + State, + #[error("overrides use incompatible storage strategies (state vs state_diff)")] + StateAndStateDiff, + #[error("both overrides write storage slot {0}")] + StateDiffSlot(B256), } diff --git a/crates/simulator/src/simulation_encoding.rs b/crates/simulator/src/simulation_encoding.rs index 0fd48b8a9d..0798ed7fb3 100644 --- a/crates/simulator/src/simulation_encoding.rs +++ b/crates/simulator/src/simulation_encoding.rs @@ -8,27 +8,31 @@ use { encode_wrapper_settlement, }, simulation_builder::{ + AccountOverrideRequest, Block, BuildError, EthCallInputs, ExecutionAmount, + MergeConflict, Order, Prices, SimulationBuilder, Solver, WrapperConfig, }, - state_override_helpers::{EthBalanceOverride, SolverAllowlisting}, }, - alloy_primitives::{Address, Bytes, U256}, - alloy_rpc_types::TransactionRequest, + alloy_primitives::{Address, B256, Bytes, U256, keccak256}, + alloy_rpc_types::{ + TransactionRequest, + state::{AccountOverride, StateOverride}, + }, alloy_sol_types::SolCall, balance_overrides::BalanceOverrideRequest, model::order::OrderKind, }; pub(crate) async fn encode( - builder: SimulationBuilder, + mut builder: SimulationBuilder, customize: impl FnOnce(&mut EncodedSettlement), ) -> Result { let order = builder.order.as_ref().ok_or(BuildError::NoOrder)?; @@ -65,18 +69,28 @@ pub(crate) async fn encode( .position(|t| *t == order.data.buy_token) .ok_or(BuildError::MissingBuyToken)?; - // Compute before clearing_prices is moved into EncodedSettlement below. - let fund_amount = builder.fund_settlement_contract.then(|| { - let base_amount = match order.data.kind { - OrderKind::Sell => clearing_prices[sell_token_index] - .saturating_mul(executed_amount) - .checked_div(clearing_prices[buy_token_index]) - .unwrap_or(U256::MAX), - OrderKind::Buy => executed_amount, - }; - // give 1 wei extra to avoid issues with rounding divisions - base_amount.saturating_add(U256::ONE) - }); + // Replace BuyTokensForBuffers placeholders with concrete Balance requests + // now that the required amounts are known. Must happen before clearing_prices + // is moved into EncodedSettlement. + for request in &mut builder.account_override_requests { + if matches!(request, AccountOverrideRequest::BuyTokensForBuffers) { + let amount = match order.data.kind { + OrderKind::Sell => clearing_prices[sell_token_index] + .saturating_mul(executed_amount) + .checked_div(clearing_prices[buy_token_index]) + .unwrap_or(U256::MAX), + OrderKind::Buy => executed_amount, + } + // give 1 wei extra to avoid issues with rounding divisions + .saturating_add(U256::ONE); + + *request = AccountOverrideRequest::Balance { + holder: *builder.simulator.0.settlement.address(), + token: order.data.buy_token, + amount, + }; + } + } let trade = encode_trade( &order.data, @@ -138,44 +152,72 @@ pub(crate) async fn encode( _ => (*builder.simulator.0.settlement.address(), settle_calldata), }; - let mut state_overrides = builder.state_overrides; let from = match builder.solver { Some(Solver::Real(addr)) => addr, Some(Solver::Fake(opt)) => { let addr = opt.unwrap_or_else(Address::random); - state_overrides.insert(addr, EthBalanceOverride(U256::MAX / U256::from(2)).into()); - state_overrides.insert( - builder.simulator.0.authenticator, - SolverAllowlisting(addr).into(), - ); + builder + .account_override_requests + .push(AccountOverrideRequest::SufficientEthBalance(addr)); + builder + .account_override_requests + .push(AccountOverrideRequest::AuthenticateAddress(addr)); addr } None => return Err(BuildError::NoSolver), }; - if let Some(amount) = fund_amount { - let (address, state_override) = builder - .simulator - .0 - .balance_overrides - .state_override(BalanceOverrideRequest { - token: order.data.buy_token, - holder: *builder.simulator.0.settlement.address(), + let mut state_overrides = builder.state_overrides; + for request in builder.account_override_requests { + let (address, account_override) = match request { + AccountOverrideRequest::SufficientEthBalance(addr) => ( + addr, + AccountOverride::default().with_balance(U256::MAX / U256::from(2)), + ), + AccountOverrideRequest::AuthenticateAddress(addr) => { + // GPv2AllowListAuthentication stores `mapping(address => bool) managers` + // at storage slot 1. Solidity mapping key: keccak256(address_padded ++ + // slot_padded). + let mut buf = [0u8; 64]; + buf[12..32].copy_from_slice(addr.as_slice()); + buf[32..64].copy_from_slice(&U256::ONE.to_be_bytes::<32>()); + let slot = keccak256(buf); + ( + builder.simulator.0.authenticator, + AccountOverride::default() + .with_state_diff(std::iter::once((slot, B256::with_last_byte(1)))), + ) + } + AccountOverrideRequest::Balance { + holder, + token, amount, - }) - .await - .ok_or(BuildError::FailedToOverrideBalances)?; - state_overrides.insert(address, state_override); - } - - for request in builder.fund_requests { - let (address, account_override) = builder - .simulator - .0 - .balance_overrides - .state_override(request) - .await - .ok_or(BuildError::FailedToOverrideBalances)?; - state_overrides.insert(address, account_override); + } => builder + .simulator + .0 + .balance_overrides + .state_override(BalanceOverrideRequest { + token, + holder, + amount, + }) + .await + .ok_or(BuildError::FailedToOverrideBalances)?, + AccountOverrideRequest::BuyTokensForBuffers => { + unreachable!( + "replaced with specific Balance requests before state overrides get computed" + ) + } + AccountOverrideRequest::Code { account, code } => ( + account, + AccountOverride { + code: Some(code), + ..Default::default() + }, + ), + AccountOverrideRequest::Custom { account, state } => (account, state), + }; + apply_account_override(&mut state_overrides, address, account_override) + .map_err(BuildError::ConflictingStateOverrides)?; } Ok(EthCallInputs { @@ -221,3 +263,79 @@ async fn executed_amount( } }) } + +/// Merges `new` into `existing` field by field. +/// +/// Returns [`MergeConflict`] if both overrides write the same field. +/// Non-conflicting `state_diff` entries are combined into a single map. +fn merge_account_override( + existing: &mut AccountOverride, + new: AccountOverride, +) -> Result<(), MergeConflict> { + if new.balance.is_some() { + if existing.balance.is_some() { + return Err(MergeConflict::Balance); + } + existing.balance = new.balance; + } + if new.nonce.is_some() { + if existing.nonce.is_some() { + return Err(MergeConflict::Nonce); + } + existing.nonce = new.nonce; + } + if new.code.is_some() { + if existing.code.is_some() { + return Err(MergeConflict::Code); + } + existing.code = new.code; + } + match (new.state, new.state_diff) { + (Some(new_state), None) => { + if existing.state.is_some() { + return Err(MergeConflict::State); + } + if existing.state_diff.is_some() { + return Err(MergeConflict::StateAndStateDiff); + } + existing.state = Some(new_state); + } + (None, Some(new_diff)) => { + if existing.state.is_some() { + return Err(MergeConflict::StateAndStateDiff); + } + match &mut existing.state_diff { + None => existing.state_diff = Some(new_diff), + Some(existing_diff) => { + for (slot, value) in new_diff { + if existing_diff.contains_key(&slot) { + return Err(MergeConflict::StateDiffSlot(slot)); + } + existing_diff.insert(slot, value); + } + } + } + } + (None, None) => {} + // alloy does not allow both simultaneously, treat as incompatible + (Some(_), Some(_)) => return Err(MergeConflict::StateAndStateDiff), + } + Ok(()) +} + +/// Applies `new` to the override map for `address`. +/// +/// If `address` already has an entry, the overrides are merged via +/// [`merge_account_override`]. Returns an error on conflict. +pub fn apply_account_override( + overrides: &mut StateOverride, + address: Address, + new: AccountOverride, +) -> Result<(), MergeConflict> { + if let Some(existing) = overrides.get_mut(&address) { + merge_account_override(existing, new) + } else { + overrides.insert(address, new); + Ok(()) + } +} diff --git a/crates/simulator/src/state_override_helpers.rs b/crates/simulator/src/state_override_helpers.rs deleted file mode 100644 index b3aec19451..0000000000 --- a/crates/simulator/src/state_override_helpers.rs +++ /dev/null @@ -1,68 +0,0 @@ -use { - alloy_primitives::{Address, B256, Bytes, U256, keccak256}, - alloy_rpc_types::state::AccountOverride, - std::iter, -}; -pub use { - balance_overrides::{BalanceOverrideRequest, BalanceOverrides, BalanceOverriding}, - configs::balance_overrides::Strategy, -}; - -/// Deploys a fake ERC-1271 contract at a given address so that signature -/// verification succeeds unconditionally. Pass to -/// [`crate::simulation_builder::SimulationBuilder::state_override`] with the -/// order owner's address. -pub struct FakeUser; - -impl From for AccountOverride { - fn from(_fake_user: FakeUser) -> Self { - let code = Bytes::from_static(&[ - 0x63, 0x16, 0x26, 0xba, 0x7e, // PUSH4 0x1626ba7e - 0x60, 0xe0, // PUSH1 224 - 0x1b, // SHL → 0x1626ba7e left-aligned in 32-byte word - 0x60, 0x00, // PUSH1 0x00 - 0x52, // MSTORE - 0x60, 0x20, // PUSH1 0x20 (return 32 bytes) - 0x60, 0x00, // PUSH1 0x00 (from offset 0) - 0xf3, // RETURN - ]); - Self { - code: Some(code), - ..Default::default() - } - } -} - -/// Sets the ETH balance of an address to the given value. -pub struct EthBalanceOverride(pub U256); - -impl From for AccountOverride { - fn from(EthBalanceOverride(balance): EthBalanceOverride) -> Self { - Self { - balance: Some(balance), - ..Default::default() - } - } -} - -/// Overrides the authenticator contract's storage to allowlist a single solver -/// address. Pass to -/// [`crate::simulation_builder::SimulationBuilder::state_override`] -/// with the authenticator contract's address. -pub struct SolverAllowlisting(pub Address); - -impl From for AccountOverride { - fn from(SolverAllowlisting(solver): SolverAllowlisting) -> Self { - // GPv2AllowListAuthentication stores `mapping(address => bool) managers` - // at storage slot 1. Solidity mapping key: keccak256(address_padded ++ - // slot_padded). - let mut buf = [0u8; 64]; - buf[12..32].copy_from_slice(solver.as_slice()); - buf[32..64].copy_from_slice(&U256::ONE.to_be_bytes::<32>()); - let slot = keccak256(buf); - Self { - state_diff: Some(iter::once((slot, B256::with_last_byte(1))).collect()), - ..Default::default() - } - } -} From 0a092dc757f1fa98d396b239e4a12164aab3fd9f Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Thu, 30 Apr 2026 08:54:47 +0000 Subject: [PATCH 30/44] build final state overrides in separate function --- Cargo.lock | 1 + crates/simulator/Cargo.toml | 1 + crates/simulator/src/simulation_builder.rs | 2 - crates/simulator/src/simulation_encoding.rs | 130 ++++++++++++-------- 4 files changed, 78 insertions(+), 56 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 92dfd80b0a..9596fd87fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7794,6 +7794,7 @@ dependencies = [ "derive_more 1.0.0", "eth-domain-types", "ethrpc", + "futures", "gas-price-estimation", "hex-literal", "http-client", diff --git a/crates/simulator/Cargo.toml b/crates/simulator/Cargo.toml index 3f5d900acd..1cb1960dfe 100644 --- a/crates/simulator/Cargo.toml +++ b/crates/simulator/Cargo.toml @@ -16,6 +16,7 @@ alloy-sol-types = { workspace = true } alloy-transport = { workspace = true } anyhow = { workspace = true } app-data = { workspace = true } +futures = { workspace = true } async-trait = { workspace = true } balance-overrides = { workspace = true } cached = { workspace = true } diff --git a/crates/simulator/src/simulation_builder.rs b/crates/simulator/src/simulation_builder.rs index c51bc9073b..e0770cfcb9 100644 --- a/crates/simulator/src/simulation_builder.rs +++ b/crates/simulator/src/simulation_builder.rs @@ -80,7 +80,6 @@ impl SettlementSimulator { prices: None, solver: None, auction_id: None, - state_overrides: StateOverride::default(), account_override_requests: vec![], block: Block::Latest, } @@ -111,7 +110,6 @@ pub struct SimulationBuilder { pub(crate) prices: Option, pub(crate) solver: Option, pub(crate) auction_id: Option, - pub(crate) state_overrides: StateOverride, pub(crate) simulator: SettlementSimulator, pub(crate) account_override_requests: Vec, pub(crate) block: Block, diff --git a/crates/simulator/src/simulation_encoding.rs b/crates/simulator/src/simulation_encoding.rs index 0798ed7fb3..b96e344932 100644 --- a/crates/simulator/src/simulation_encoding.rs +++ b/crates/simulator/src/simulation_encoding.rs @@ -27,8 +27,9 @@ use { state::{AccountOverride, StateOverride}, }, alloy_sol_types::SolCall, - balance_overrides::BalanceOverrideRequest, + balance_overrides::{BalanceOverrideRequest, BalanceOverriding}, model::order::OrderKind, + std::sync::Arc, }; pub(crate) async fn encode( @@ -166,59 +167,12 @@ pub(crate) async fn encode( } None => return Err(BuildError::NoSolver), }; - let mut state_overrides = builder.state_overrides; - for request in builder.account_override_requests { - let (address, account_override) = match request { - AccountOverrideRequest::SufficientEthBalance(addr) => ( - addr, - AccountOverride::default().with_balance(U256::MAX / U256::from(2)), - ), - AccountOverrideRequest::AuthenticateAddress(addr) => { - // GPv2AllowListAuthentication stores `mapping(address => bool) managers` - // at storage slot 1. Solidity mapping key: keccak256(address_padded ++ - // slot_padded). - let mut buf = [0u8; 64]; - buf[12..32].copy_from_slice(addr.as_slice()); - buf[32..64].copy_from_slice(&U256::ONE.to_be_bytes::<32>()); - let slot = keccak256(buf); - ( - builder.simulator.0.authenticator, - AccountOverride::default() - .with_state_diff(std::iter::once((slot, B256::with_last_byte(1)))), - ) - } - AccountOverrideRequest::Balance { - holder, - token, - amount, - } => builder - .simulator - .0 - .balance_overrides - .state_override(BalanceOverrideRequest { - token, - holder, - amount, - }) - .await - .ok_or(BuildError::FailedToOverrideBalances)?, - AccountOverrideRequest::BuyTokensForBuffers => { - unreachable!( - "replaced with specific Balance requests before state overrides get computed" - ) - } - AccountOverrideRequest::Code { account, code } => ( - account, - AccountOverride { - code: Some(code), - ..Default::default() - }, - ), - AccountOverrideRequest::Custom { account, state } => (account, state), - }; - apply_account_override(&mut state_overrides, address, account_override) - .map_err(BuildError::ConflictingStateOverrides)?; - } + let state_overrides = build_final_state_overrides( + builder.account_override_requests, + Arc::clone(&builder.simulator.0.balance_overrides), + builder.simulator.0.authenticator, + ) + .await?; Ok(EthCallInputs { request: TransactionRequest { @@ -264,6 +218,74 @@ async fn executed_amount( }) } +/// Resolves all [`AccountOverrideRequest`]s concurrently, merges them +/// and returns the final [`StateOverride`]. +async fn build_final_state_overrides( + requests: Vec, + balance_overrides: Arc, + authenticator: Address, +) -> Result { + let futures = requests.into_iter().map(|request| { + let balance_overrides = Arc::clone(&balance_overrides); + async move { + match request { + AccountOverrideRequest::SufficientEthBalance(addr) => Ok(( + addr, + AccountOverride::default().with_balance(U256::MAX / U256::from(2)), + )), + AccountOverrideRequest::AuthenticateAddress(addr) => { + // GPv2AllowListAuthentication stores `mapping(address => bool) managers` + // at storage slot 1. Solidity mapping key: keccak256(address_padded ++ + // slot_padded). + let mut buf = [0u8; 64]; + buf[12..32].copy_from_slice(addr.as_slice()); + buf[32..64].copy_from_slice(&U256::ONE.to_be_bytes::<32>()); + let slot = keccak256(buf); + Ok(( + authenticator, + AccountOverride::default() + .with_state_diff(std::iter::once((slot, B256::with_last_byte(1)))), + )) + } + AccountOverrideRequest::Balance { + holder, + token, + amount, + } => balance_overrides + .state_override(BalanceOverrideRequest { + token, + holder, + amount, + }) + .await + .ok_or(BuildError::FailedToOverrideBalances), + AccountOverrideRequest::BuyTokensForBuffers => { + unreachable!( + "replaced with specific Balance requests before state overrides get \ + computed" + ) + } + AccountOverrideRequest::Code { account, code } => Ok(( + account, + AccountOverride { + code: Some(code), + ..Default::default() + }, + )), + AccountOverrideRequest::Custom { account, state } => Ok((account, state)), + } + } + }); + let resolved_overrides = futures::future::try_join_all(futures).await?; + + let mut state_overrides = StateOverride::default(); + for (address, account_override) in resolved_overrides { + apply_account_override(&mut state_overrides, address, account_override) + .map_err(BuildError::ConflictingStateOverrides)?; + } + Ok(state_overrides) +} + /// Merges `new` into `existing` field by field. /// /// Returns [`MergeConflict`] if both overrides write the same field. From ba042f01d64780f521cd9b9e3f7a12662750fdb8 Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Thu, 30 Apr 2026 10:51:30 +0000 Subject: [PATCH 31/44] claude suggested changes --- crates/orderbook/src/orderbook.rs | 10 ++++- crates/orderbook/src/run.rs | 1 + .../src/trade_verifier/mod.rs | 2 + crates/simulator/src/simulation_builder.rs | 38 +++++++++++++++++-- crates/simulator/src/simulation_encoding.rs | 21 +++++++++- 5 files changed, 65 insertions(+), 7 deletions(-) diff --git a/crates/orderbook/src/orderbook.rs b/crates/orderbook/src/orderbook.rs index acc718b694..5312b13814 100644 --- a/crates/orderbook/src/orderbook.rs +++ b/crates/orderbook/src/orderbook.rs @@ -642,7 +642,10 @@ impl Orderbook { .add_order( simulation_builder::Order::new(order.data) .with_signature(order.metadata.owner, order.signature) - .with_executed_amount(simulation_builder::ExecutionAmount::Remaining), + .fill_at( + simulation_builder::ExecutionAmount::Remaining, + simulation_builder::PriceEncoding::Exact, + ), ) .parameters_from_app_data(&full_app_data) .context("failed to parse app data")? @@ -699,7 +702,10 @@ impl Orderbook { partially_fillable: request.partially_fillable, }) .with_signature(request.owner, request.signature) - .with_executed_amount(simulation_builder::ExecutionAmount::Full), + .fill_at( + simulation_builder::ExecutionAmount::Full, + simulation_builder::PriceEncoding::Exact, + ), ) .parameters_from_app_data(&request.app_data) .context("failed to parse app data")? diff --git a/crates/orderbook/src/run.rs b/crates/orderbook/src/run.rs index ae9cdee469..08b3c67d55 100644 --- a/crates/orderbook/src/run.rs +++ b/crates/orderbook/src/run.rs @@ -424,6 +424,7 @@ pub async fn run(config: Configuration) { settlement_contract.clone(), Default::default(), hooks_trampoline_address, + *native_token.address(), balance_overrider.clone(), current_block_stream.clone(), tenderly, diff --git a/crates/price-estimation/src/trade_verifier/mod.rs b/crates/price-estimation/src/trade_verifier/mod.rs index a96bcd1fb3..1f6d0d0278 100644 --- a/crates/price-estimation/src/trade_verifier/mod.rs +++ b/crates/price-estimation/src/trade_verifier/mod.rs @@ -486,6 +486,8 @@ impl TradeVerifier { .call() .await .context("could not fetch authenticator")?; + // TODO: when switching to the `SettlementSimulator` use + // `StateOverrideRequest::AuthenticateAddress` for this overrides.insert( authenticator, AccountOverride { diff --git a/crates/simulator/src/simulation_builder.rs b/crates/simulator/src/simulation_builder.rs index e0770cfcb9..b69aa27e6c 100644 --- a/crates/simulator/src/simulation_builder.rs +++ b/crates/simulator/src/simulation_builder.rs @@ -1,6 +1,6 @@ use { crate::{ - encoding::{EncodedSettlement, WrapperCall}, + encoding::{EncodedSettlement, EncodedTrade, WrapperCall}, tenderly::dto::StateObject, }, alloy_primitives::{Address, B256, Bytes, TxKind, U256}, @@ -34,6 +34,7 @@ pub(crate) struct Inner { pub(crate) authenticator: Address, pub(crate) flash_loan_router: Address, pub(crate) hooks_trampoline: Address, + pub(crate) native_token: Address, pub(crate) balance_overrides: Arc, pub(crate) provider: DynProvider, pub(crate) domain_separator: DomainSeparator, @@ -47,6 +48,7 @@ impl SettlementSimulator { settlement: contracts::GPv2Settlement::Instance, flash_loan_router: Address, hooks_trampoline: Address, + native_token: Address, balance_overrides: Arc, current_block: CurrentBlockWatcher, tenderly: Option>, @@ -60,6 +62,7 @@ impl SettlementSimulator { authenticator, flash_loan_router, hooks_trampoline, + native_token, balance_overrides, provider, domain_separator, @@ -69,6 +72,10 @@ impl SettlementSimulator { }))) } + pub fn native_token(&self) -> Address { + self.0.native_token + } + pub fn new_simulation_builder(&self) -> SimulationBuilder { SimulationBuilder { simulator: self.clone(), @@ -81,6 +88,7 @@ impl SettlementSimulator { solver: None, auction_id: None, account_override_requests: vec![], + extra_trades: vec![], block: Block::Latest, } } @@ -112,6 +120,7 @@ pub struct SimulationBuilder { pub(crate) auction_id: Option, pub(crate) simulator: SettlementSimulator, pub(crate) account_override_requests: Vec, + pub(crate) extra_trades: Vec, pub(crate) block: Block, } @@ -229,6 +238,13 @@ impl SimulationBuilder { self } + /// Appends pre-encoded trades (e.g. JIT orders) to the settlement. + /// These are appended after the primary order's trade entry. + pub fn add_extra_trades(mut self, trades: Vec) -> Self { + self.extra_trades.extend(trades); + self + } + /// Finishes the simulation struct based on the configuration thus far. pub async fn build(self) -> Result { self.build_with_modifications(|_| {}).await @@ -290,6 +306,19 @@ pub enum ExecutionAmount { Explicit(U256), } +/// How the limit price of an order's trade entry should be encoded. +pub enum PriceEncoding { + /// Encode the exact sell_amount / buy_amount from the order as the limit + /// price. Default for production settlements. + Exact, + /// Set limit prices maximally permissive so the settlement always passes, + /// regardless of how many tokens the trader actually receives. Used for + /// quote verification so the actual out_amount can be measured afterward. + /// Sell orders: buy_amount = 0. Buy orders: sell_amount = max(sell_amount, + /// u128::MAX). + Disadvantageous, +} + /// A simulator-specific order that bundles the data needed to encode a trade. /// /// Construct with [`Order::new`] and add optional fields via the builder @@ -302,6 +331,7 @@ pub struct Order { pub(crate) pre_interactions: Vec, pub(crate) post_interactions: Vec, pub(crate) executed_amount: ExecutionAmount, + pub(crate) price_encoding: PriceEncoding, } /// Configuration for wrapping the settlement in a flashloan or custom wrapper @@ -328,6 +358,7 @@ impl Order { pre_interactions: vec![], post_interactions: vec![], executed_amount: ExecutionAmount::Remaining, + price_encoding: PriceEncoding::Exact, } } @@ -347,8 +378,9 @@ impl Order { self } - pub fn with_executed_amount(mut self, amount: ExecutionAmount) -> Self { - self.executed_amount = amount; + pub fn fill_at(mut self, execution: ExecutionAmount, price: PriceEncoding) -> Self { + self.executed_amount = execution; + self.price_encoding = price; self } } diff --git a/crates/simulator/src/simulation_encoding.rs b/crates/simulator/src/simulation_encoding.rs index b96e344932..8652e8fac4 100644 --- a/crates/simulator/src/simulation_encoding.rs +++ b/crates/simulator/src/simulation_encoding.rs @@ -15,6 +15,7 @@ use { ExecutionAmount, MergeConflict, Order, + PriceEncoding, Prices, SimulationBuilder, Solver, @@ -93,8 +94,21 @@ pub(crate) async fn encode( } } + let trade_data = match order.price_encoding { + PriceEncoding::Exact => std::borrow::Cow::Borrowed(&order.data), + PriceEncoding::Disadvantageous => { + let mut d = order.data.clone(); + match d.kind { + model::order::OrderKind::Sell => d.buy_amount = U256::ZERO, + model::order::OrderKind::Buy => { + d.sell_amount = d.sell_amount.max(U256::from(u128::MAX)) + } + } + std::borrow::Cow::Owned(d) + } + }; let trade = encode_trade( - &order.data, + &trade_data, &order.signature, order.owner, sell_token_index, @@ -105,10 +119,13 @@ pub(crate) async fn encode( let order_pre = &order.pre_interactions; let order_post = &order.post_interactions; + let mut trades = vec![trade]; + trades.extend(builder.extra_trades); + let mut settlement = EncodedSettlement { tokens, clearing_prices, - trades: vec![trade], + trades, interactions: Interactions { // order's pre-hooks run before any additional pre-interactions pre: encode_interactions(order_pre.iter().chain(&builder.pre_interactions)), From bfc562762b838169578d04de17fd7649bf171ff3 Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Thu, 30 Apr 2026 12:51:35 +0000 Subject: [PATCH 32/44] use `SettlementSimulator` in trade verification --- crates/e2e/tests/e2e/quote_verification.rs | 28 +- crates/orderbook/src/orderbook.rs | 6 +- crates/price-estimation/src/factory.rs | 25 +- .../src/trade_verifier/mod.rs | 300 ++++++++++-------- crates/simulator/src/simulation_builder.rs | 64 ++-- crates/simulator/src/simulation_encoding.rs | 36 +-- 6 files changed, 232 insertions(+), 227 deletions(-) diff --git a/crates/e2e/tests/e2e/quote_verification.rs b/crates/e2e/tests/e2e/quote_verification.rs index a7561b96ad..6d1f5aa749 100644 --- a/crates/e2e/tests/e2e/quote_verification.rs +++ b/crates/e2e/tests/e2e/quote_verification.rs @@ -12,7 +12,7 @@ use { price_estimation::BalanceOverridesConfig, test_util::TestDefault, }, - contracts::ERC20, + contracts::{ERC20, GPv2Settlement}, e2e::setup::*, ethrpc::{Web3, alloy::CallBuilderExt}, model::{ @@ -28,7 +28,7 @@ use { trade_verifier::{PriceQuery, TradeVerifier, TradeVerifying}, }, serde_json::json, - simulator::swap_simulator::SwapSimulator, + simulator::simulation_builder::SettlementSimulator, std::{collections::HashMap, sync::Arc}, }; @@ -189,32 +189,34 @@ async fn test_bypass_verification_for_rfq_quotes(web3: Web3) { .unwrap(); let onchain = OnchainComponents::deployed(web3.clone()).await; let balance_overrides = Arc::new(BalanceOverrides::default()); - let gas_limit = 12_000_000; - let simulator = SwapSimulator::new( - balance_overrides.clone(), + let gas_limit = 12_000_000u64; + let settlement_contract = GPv2Settlement::GPv2Settlement::new( *onchain.contracts().gp_settlement.address(), + web3.provider.clone(), + ); + let simulator = SettlementSimulator::new( + settlement_contract, + Default::default(), + Default::default(), *onchain.contracts().weth.address(), + balance_overrides.clone(), block_stream.clone(), - web3.clone(), - gas_limit, + None, ) .await .unwrap(); let verifier = TradeVerifier::new( - web3.clone(), - None, simulator, + gas_limit, + None, Arc::new(web3.clone()), balance_overrides, - *onchain.contracts().gp_settlement.address(), BigDecimal::zero(), Default::default(), 0, u32::MAX, - ) - .await - .unwrap(); + ); let verify_trade = |tx_origin| { let verifier = verifier.clone(); diff --git a/crates/orderbook/src/orderbook.rs b/crates/orderbook/src/orderbook.rs index 5312b13814..afb38f4bbd 100644 --- a/crates/orderbook/src/orderbook.rs +++ b/crates/orderbook/src/orderbook.rs @@ -644,7 +644,7 @@ impl Orderbook { .with_signature(order.metadata.owner, order.signature) .fill_at( simulation_builder::ExecutionAmount::Remaining, - simulation_builder::PriceEncoding::Exact, + simulation_builder::PriceEncoding::LimitPrice, ), ) .parameters_from_app_data(&full_app_data) @@ -654,7 +654,6 @@ impl Orderbook { .map(simulation_builder::Block::Number) .unwrap_or(simulation_builder::Block::Latest), ) - .with_prices(simulation_builder::Prices::Limit) .with_override(simulation_builder::AccountOverrideRequest::BuyTokensForBuffers) .from_solver(simulation_builder::Solver::Fake(None)) .build() @@ -704,7 +703,7 @@ impl Orderbook { .with_signature(request.owner, request.signature) .fill_at( simulation_builder::ExecutionAmount::Full, - simulation_builder::PriceEncoding::Exact, + simulation_builder::PriceEncoding::LimitPrice, ), ) .parameters_from_app_data(&request.app_data) @@ -715,7 +714,6 @@ impl Orderbook { .map(simulation_builder::Block::Number) .unwrap_or(simulation_builder::Block::Latest), ) - .with_prices(simulation_builder::Prices::Limit) .with_override(simulation_builder::AccountOverrideRequest::BuyTokensForBuffers) .from_solver(simulation_builder::Solver::Fake(None)) .build() diff --git a/crates/price-estimation/src/factory.rs b/crates/price-estimation/src/factory.rs index 6dc1e9c971..cafa889f0d 100644 --- a/crates/price-estimation/src/factory.rs +++ b/crates/price-estimation/src/factory.rs @@ -21,14 +21,14 @@ use { anyhow::{Context as _, Result}, bad_tokens::list_based::DenyListedTokens, configs::price_estimation::PriceEstimation, - contracts::WETH9, + contracts::{GPv2Settlement, WETH9}, ethrpc::{Web3, alloy::ProviderLabelingExt, block_stream::CurrentBlockWatcher}, gas_price_estimation::GasPriceEstimating, http_client::HttpClientFactory, number::nonzero::NonZeroU256, rate_limit::RateLimiter, reqwest::Url, - simulator::{swap_simulator::SwapSimulator, tenderly}, + simulator::{simulation_builder::SettlementSimulator, tenderly}, std::{collections::HashMap, num::NonZeroUsize, sync::Arc}, token_info::TokenInfoFetching, }; @@ -106,29 +106,30 @@ impl<'a> PriceEstimatorFactory<'a> { network.chain.id().to_string(), )) as Arc }); - let simulator = SwapSimulator::new( - balance_overrides.clone(), - network.settlement, + let settlement_contract = + GPv2Settlement::GPv2Settlement::new(network.settlement, web3.provider.clone()); + let simulator = SettlementSimulator::new( + settlement_contract, + Default::default(), + Default::default(), network.native_token, + balance_overrides.clone(), network.block_stream.clone(), - web3.clone(), - args.max_gas_per_tx, + tenderly.clone(), ) .await?; let verifier = TradeVerifier::new( - web3, - tenderly, simulator, + args.max_gas_per_tx, + tenderly, components.code_fetcher.clone(), balance_overrides, - network.settlement, args.quote_inaccuracy_limit.clone(), args.tokens_without_verification.iter().cloned().collect(), args.min_gas_amount_for_unverified_quotes, args.max_gas_amount_for_unverified_quotes, - ) - .await?; + ); Ok(Some(Arc::new(verifier))) } diff --git a/crates/price-estimation/src/trade_verifier/mod.rs b/crates/price-estimation/src/trade_verifier/mod.rs index 1f6d0d0278..3060ed41c1 100644 --- a/crates/price-estimation/src/trade_verifier/mod.rs +++ b/crates/price-estimation/src/trade_verifier/mod.rs @@ -2,7 +2,6 @@ use { super::{Estimate, Verification}, crate::{ trade_finding::{ - Interaction, QuoteExecution, TradeKind, external::dto::{self, Side}, @@ -12,20 +11,20 @@ use { }, ::alloy::sol_types::SolCall, alloy::{ - primitives::{Address, U256, address, aliases::I512, map::AddressMap}, - rpc::types::{eth::state::StateOverride, state::AccountOverride}, + primitives::{Address, Bytes, U256, address, aliases::I512}, + rpc::types::state::AccountOverride, }, anyhow::{Context, Result}, balance_overrides::{BalanceOverrideRequest, BalanceOverriding}, bigdecimal::BigDecimal, contracts::{ - GPv2Settlement, + WETH9, support::{AnyoneAuthenticator, Solver, Spardose, Trader}, }, - ethrpc::Web3, model::{ DomainSeparator, - order::{OrderData, OrderKind}, + interaction::InteractionData, + order::{BUY_ETH_ADDRESS, OrderData, OrderKind}, signature::{Signature, SigningScheme}, }, num::BigRational, @@ -39,8 +38,14 @@ use { nonzero::NonZeroU256, }, simulator::{ - encoding::{EncodedTrade, InteractionEncoding, encode_trade}, - swap_simulator::{EncodedSwap, SwapSimulator, TradeEncoding}, + encoding::{EncodedTrade, encode_trade}, + simulation_builder::{ + self as sim_builder, + ExecutionAmount, + PriceEncoding, + SettlementSimulator, + Solver as SimSolver, + }, tenderly::{self}, }, std::{ @@ -69,10 +74,10 @@ pub trait TradeVerifying: Send + Sync + 'static { #[derive(Clone)] pub struct TradeVerifier { tenderly: Option>, - simulator: SwapSimulator, + simulator: SettlementSimulator, + gas_limit: u64, code_fetcher: Arc, balance_overrides: Arc, - settlement: GPv2Settlement::Instance, quote_inaccuracy_limit: BigRational, tokens_without_verification: HashSet
, min_gas_amount_for_unverified_quotes: u32, @@ -84,36 +89,33 @@ impl TradeVerifier { const TRADER_IMPL: Address = address!("0000000000000000000000000000000000010000"); #[expect(clippy::too_many_arguments)] - pub async fn new( - web3: Web3, + pub fn new( + simulator: SettlementSimulator, + gas_limit: u64, tenderly: Option>, - simulator: SwapSimulator, code_fetcher: Arc, balance_overrides: Arc, - settlement: Address, quote_inaccuracy_limit: BigDecimal, tokens_without_verification: HashSet
, min_gas_amount_for_unverified_quotes: u32, max_gas_amount_for_unverified_quotes: u32, - ) -> Result { + ) -> Self { assert!( min_gas_amount_for_unverified_quotes <= max_gas_amount_for_unverified_quotes, "gas floor ({min_gas_amount_for_unverified_quotes}) exceeds gas ceiling \ ({max_gas_amount_for_unverified_quotes}) for unverified quotes" ); - let settlement_contract = - GPv2Settlement::GPv2Settlement::new(settlement, web3.provider.clone()); - Ok(Self { + Self { tenderly, simulator, + gas_limit, code_fetcher, balance_overrides, - settlement: settlement_contract, quote_inaccuracy_limit: big_decimal_to_big_rational("e_inaccuracy_limit), tokens_without_verification, min_gas_amount_for_unverified_quotes, max_gas_amount_for_unverified_quotes, - }) + } } async fn verify_inner( @@ -155,92 +157,158 @@ impl TradeVerifier { query.in_amount.get(), ), }; - let simulator_query = simulator::swap_simulator::Query { - sell_token: query.sell_token, - buy_token: query.buy_token, - kind: query.kind, - sell_amount, - buy_amount, - receiver: verification.receiver, - sell_token_source: verification.sell_token_source, - buy_token_destination: verification.buy_token_destination, - from: verification.from, - tx_origin: trade.tx_origin(), - solver: solver_address, - tokens: tokens.clone(), - clearing_prices, - wrappers: Default::default(), + // Determine effective receiver (zero means owner receives) + let effective_receiver = if verification.receiver.is_zero() { + verification.from + } else { + verification.receiver }; - let mut swap = self - .simulator - .fake_swap(&simulator_query, TradeEncoding::Disadvantageous) - .await - .map_err(Error::SimulationFailed)?; + // storeBalance interactions bracket the settlement to measure the actual + // out_amount + let (tracked_token, tracked_owner) = match query.kind { + OrderKind::Sell => (query.buy_token, effective_receiver), + OrderKind::Buy => (query.sell_token, verification.from), + }; + let store_balance = InteractionData { + target: solver_address, + value: U256::ZERO, + call_data: Solver::Solver::storeBalanceCall { + token: tracked_token, + owner: tracked_owner, + countGas: true, + } + .abi_encode(), + }; - swap.overrides.extend(overrides); + // WETH unwrap so ETH buy orders can pay out native tokens + let weth_unwrap = (query.buy_token == BUY_ETH_ADDRESS).then(|| InteractionData { + target: self.simulator.native_token(), + value: U256::ZERO, + call_data: WETH9::WETH9::withdrawCall { wad: buy_amount }.abi_encode(), + }); - let mut pre_interactions = verification + // pre: [verification.pre, trade.pre, trade_setup, storeBalance_before] + let pre_interactions: Vec = map_interactions_data( + verification .pre_interactions .iter() - // pre_interactions introduced by the solver - .chain(trade.pre_interactions()) - .map(InteractionEncoding::encode) - .collect::>(); - - // Join custom pre_interactions in the following order: - // pre_interactions, trade setup interaction, encoded swap pre interactions - pre_interactions.extend([self - .trade_setup_interaction(out_amount, &verification, query, trade) - .encode()]); - pre_interactions.extend(swap.settlement.interactions.pre); - swap.settlement.interactions.pre = pre_interactions; - - // Join interactions introduced by the solver, set up in the following order: - // trade interactions, encoded swap interactions - let interactions = trade.interactions().map(InteractionEncoding::encode); - swap.settlement.interactions.main = interactions + .chain(trade.pre_interactions()), + ) + .into_iter() + .chain([self.trade_setup_interaction(out_amount, &verification, query, trade)]) + .chain([store_balance.clone()]) + .collect(); + + // main: [trade.main, weth_unwrap] + let main_interactions: Vec = map_interactions_data(trade.interactions()) .into_iter() - .chain(swap.settlement.interactions.main) + .chain(weth_unwrap) .collect(); - // Join post interactions in the following order: - // encoded swap post interactions, verification post interactions, - let post_interactions = verification - .post_interactions - .iter() - .map(InteractionEncoding::encode); - swap.settlement.interactions.post = swap - .settlement - .interactions - .post - .into_iter() - .chain(post_interactions) + // post: [storeBalance_after, verification.post] + let post_interactions: Vec = std::iter::once(store_balance) + .chain(map_interactions_data(verification.post_interactions.iter())) .collect(); - add_balance_queries(&mut swap, query, &verification); + let jit_trades = match trade { + TradeKind::Regular(t) => { + encode_jit_orders(&t.jit_orders, &tokens, &self.simulator.domain_separator())? + } + _ => vec![], + }; - if let TradeKind::Regular(trade) = trade { - swap.settlement.trades.extend(encode_jit_orders( - &trade.jit_orders, - &swap.settlement.tokens, - &self.simulator.domain_separator, - )?); - } - let block = *self.simulator.current_block.borrow(); - let output = self + // Set limit amounts to always pass the settlement check so the actual + // out_amount can be measured via the storeBalance interactions. + let (fake_sell_amount, fake_buy_amount) = match query.kind { + OrderKind::Sell => (sell_amount.get(), U256::ZERO), + OrderKind::Buy => (sell_amount.get().max(U256::from(u128::MAX)), buy_amount), + }; + let fake_order = OrderData { + sell_token: query.sell_token, + sell_amount: fake_sell_amount, + buy_token: query.buy_token, + buy_amount: fake_buy_amount, + receiver: Some(verification.receiver), + valid_to: u32::MAX, + app_data: Default::default(), + fee_amount: U256::ZERO, + kind: query.kind, + partially_fillable: false, + sell_token_balance: verification.sell_token_source, + buy_token_balance: verification.buy_token_destination, + }; + + let mut eth_call_inputs = self .simulator - .simulate_swap_with_solver(swap, block) - .await?; + .new_simulation_builder() + .add_order( + sim_builder::Order::new(fake_order) + .with_signature( + verification.from, + Signature::default_with(SigningScheme::Eip1271), + ) + .fill_at( + ExecutionAmount::Full, + PriceEncoding::Custom { + tokens: tokens.clone(), + clearing_prices, + }, + ), + ) + .from_solver(SimSolver::Real(solver_address)) + .with_pre_interactions(pre_interactions) + .with_main_interactions(main_interactions) + .with_post_interactions(post_interactions) + .add_extra_trades(jit_trades) + .build() + .await + .map_err(|e| Error::SimulationFailed(anyhow::anyhow!("{e}")))?; + + eth_call_inputs.state_overrides.extend(overrides); + + let settlement_target = eth_call_inputs + .request + .to + .as_ref() + .and_then(|t| t.to()) + .copied() + .expect("settlement target is always set"); + let calldata: Bytes = eth_call_inputs + .request + .input + .input + .clone() + .unwrap_or_default(); + + let solver_contract = Solver::Instance::new(solver_address, self.simulator.provider()); + let swap_call = solver_contract + .swap( + settlement_target, + tokens.clone(), + effective_receiver, + calldata, + ) + .from(solver_address) + .gas(self.gas_limit); + + let tx = swap_call.clone().into_transaction_request(); + let result = swap_call + .call() + .overrides(eth_call_inputs.state_overrides.clone()) + .block(eth_call_inputs.block.into()) + .await; if let Some(tenderly) = &self.tenderly - && let Err(err) = - tenderly.log_simulation_command(output.tx, output.overrides, block.number.into()) + && let Err(err) = tenderly.log_simulation_command( + tx, + eth_call_inputs.state_overrides, + eth_call_inputs.block.into(), + ) { tracing::debug!(?err, "could not log tenderly simulation command"); } - let output = output - .result + let output = result .context("failed to simulate quote") .map_err(Error::SimulationFailed); @@ -288,7 +356,7 @@ impl TradeVerifier { // It looks like the contract lost a lot of sell tokens but only because it was // the trader and had to pay for the trade. Adjust tokens lost downward. - if verification.from == *self.settlement.address() { + if verification.from == self.simulator.settlement_address() { summary .tokens_lost .entry(query.sell_token) @@ -297,7 +365,7 @@ impl TradeVerifier { // It looks like the contract gained a lot of buy tokens (negative loss) but // only because it was the receiver and got the payout. Adjust the tokens lost // upward. - if verification.receiver == *self.settlement.address() { + if verification.receiver == self.simulator.settlement_address() { summary .tokens_lost .entry(query.buy_token) @@ -371,8 +439,8 @@ impl TradeVerifier { verification: &mut Verification, query: &PriceQuery, trade: &TradeKind, - ) -> Result { - let mut overrides = AddressMap::default(); + ) -> Result { + let mut overrides = alloy::primitives::map::AddressMap::default(); // Provide mocked balances if possible to the spardose to allow it to // give some balances to the trader in order to verify trades even for @@ -480,16 +548,8 @@ impl TradeVerifier { .tx_origin() .is_some_and(|origin| origin != trade.solver()) { - let authenticator = self - .settlement - .authenticator() - .call() - .await - .context("could not fetch authenticator")?; - // TODO: when switching to the `SettlementSimulator` use - // `StateOverrideRequest::AuthenticateAddress` for this overrides.insert( - authenticator, + self.simulator.authenticator_address(), AccountOverride { code: Some(AnyoneAuthenticator::AnyoneAuthenticator::DEPLOYED_BYTECODE.clone()), ..Default::default() @@ -513,23 +573,23 @@ impl TradeVerifier { verification: &Verification, query: &PriceQuery, trade: &TradeKind, - ) -> Interaction { + ) -> InteractionData { let sell_amount = match query.kind { OrderKind::Sell => query.in_amount.get(), OrderKind::Buy => *out_amount, }; let setup_call = Solver::Solver::ensureTradePreconditionsCall { trader: verification.from, - settlementContract: *self.settlement.address(), + settlementContract: self.simulator.settlement_address(), sellToken: query.sell_token, sellAmount: sell_amount, spardose: Self::SPARDOSE, } .abi_encode(); - Interaction { + InteractionData { target: trade.solver(), value: U256::ZERO, - data: setup_call, + call_data: setup_call, } } } @@ -619,36 +679,6 @@ impl TradeVerifying for TradeVerifier { } } -fn add_balance_queries(swap: &mut EncodedSwap, query: &PriceQuery, verification: &Verification) { - let (token, owner) = match query.kind { - // track how much `buy_token` the `receiver` actually got - OrderKind::Sell => { - let receiver = match verification.receiver.is_zero() { - // Settlement contract sends fund to owner if receiver is the 0 address. - true => verification.from, - false => verification.receiver, - }; - - (query.buy_token, receiver) - } - // track how much `sell_token` the `from` address actually spent - OrderKind::Buy => (query.sell_token, verification.from), - }; - let query_balance_call = Solver::Solver::storeBalanceCall { - token, - owner, - countGas: true, - } - .abi_encode(); - - let interaction = (swap.solver, U256::ZERO, query_balance_call.into()); - - // query balance query at the end of pre-interactions - swap.settlement.interactions.pre.push(interaction.clone()); - // query balance right after we payed out all `buy_token` - swap.settlement.interactions.post.insert(0, interaction); -} - /// Analyzed output of `Solver::settle` smart contract call. #[derive(Debug)] struct SettleOutput { diff --git a/crates/simulator/src/simulation_builder.rs b/crates/simulator/src/simulation_builder.rs index b69aa27e6c..97c5f4bf2e 100644 --- a/crates/simulator/src/simulation_builder.rs +++ b/crates/simulator/src/simulation_builder.rs @@ -76,6 +76,18 @@ impl SettlementSimulator { self.0.native_token } + pub fn provider(&self) -> DynProvider { + self.0.provider.clone() + } + + pub fn settlement_address(&self) -> Address { + *self.0.settlement.address() + } + + pub fn authenticator_address(&self) -> Address { + self.0.authenticator + } + pub fn new_simulation_builder(&self) -> SimulationBuilder { SimulationBuilder { simulator: self.clone(), @@ -84,7 +96,6 @@ impl SettlementSimulator { main_interactions: vec![], post_interactions: vec![], wrapper: WrapperConfig::NoWrapper, - prices: None, solver: None, auction_id: None, account_override_requests: vec![], @@ -115,7 +126,6 @@ pub struct SimulationBuilder { pub(crate) main_interactions: Vec, pub(crate) post_interactions: Vec, pub(crate) wrapper: WrapperConfig, - pub(crate) prices: Option, pub(crate) solver: Option, pub(crate) auction_id: Option, pub(crate) simulator: SettlementSimulator, @@ -152,11 +162,6 @@ impl SimulationBuilder { self } - pub fn with_prices(mut self, prices: Prices) -> Self { - self.prices = Some(prices); - self - } - pub fn from_solver(mut self, solver: Solver) -> Self { self.solver = Some(solver); self @@ -276,23 +281,6 @@ pub enum Solver { Fake(Option
), } -/// How clearing prices are determined for the encoded settlement. -pub enum Prices { - /// Derive clearing prices directly from the order's limit price. - /// - /// Sets `price[sell_token] = buy_amount` and `price[buy_token] = - /// sell_amount`, exactly satisfying the order's limit with no surplus. - /// This should NOT be used when encoding solutions you actually want - /// to submit. - Limit, - // TODO: check how this can be made nicer. - /// Explicit token list and matching clearing prices. - Explicit { - tokens: Vec
, - clearing_prices: Vec, - }, -} - /// How much of an order should be filled during simulation. pub enum ExecutionAmount { /// Fill the full order amount (sell_amount for sell orders, buy_amount for @@ -306,17 +294,21 @@ pub enum ExecutionAmount { Explicit(U256), } -/// How the limit price of an order's trade entry should be encoded. +/// How clearing prices are determined for the encoded settlement. pub enum PriceEncoding { - /// Encode the exact sell_amount / buy_amount from the order as the limit - /// price. Default for production settlements. - Exact, - /// Set limit prices maximally permissive so the settlement always passes, - /// regardless of how many tokens the trader actually receives. Used for - /// quote verification so the actual out_amount can be measured afterward. - /// Sell orders: buy_amount = 0. Buy orders: sell_amount = max(sell_amount, - /// u128::MAX). - Disadvantageous, + /// Derive clearing prices directly from the order's limit price. + /// + /// Sets `price[sell_token] = buy_amount` and `price[buy_token] = + /// sell_amount`, exactly satisfying the order's limit with no surplus. + LimitPrice, + /// Explicit token list and matching clearing prices. Use this when the + /// clearing prices differ from the order's limit — e.g. in trade + /// verification where the order amounts are set to always pass the limit + /// check and the solver's quoted prices are supplied separately. + Custom { + tokens: Vec
, + clearing_prices: Vec, + }, } /// A simulator-specific order that bundles the data needed to encode a trade. @@ -358,7 +350,7 @@ impl Order { pre_interactions: vec![], post_interactions: vec![], executed_amount: ExecutionAmount::Remaining, - price_encoding: PriceEncoding::Exact, + price_encoding: PriceEncoding::LimitPrice, } } @@ -509,8 +501,6 @@ pub enum BuildError { MissingBuyToken, #[error("could not override token balances to fund settlement contract")] FailedToOverrideBalances, - #[error("no strategy to compute the price vector was chosen")] - NoPriceEncoding, #[error("failed to query filled amount from settlement contract: {0}")] FilledAmountQuery(#[source] anyhow::Error), #[error("failed to parse app data: {0}")] diff --git a/crates/simulator/src/simulation_encoding.rs b/crates/simulator/src/simulation_encoding.rs index 8652e8fac4..9211c641df 100644 --- a/crates/simulator/src/simulation_encoding.rs +++ b/crates/simulator/src/simulation_encoding.rs @@ -16,7 +16,6 @@ use { MergeConflict, Order, PriceEncoding, - Prices, SimulationBuilder, Solver, WrapperConfig, @@ -46,20 +45,18 @@ pub(crate) async fn encode( let executed_amount = executed_amount(&builder, order, block).await?; - let (tokens, clearing_prices) = match builder.prices { - Some(Prices::Explicit { - tokens, - clearing_prices, - }) => (tokens, clearing_prices), - // At limit price: price[sell_token] = buy_amount, price[buy_token] = sell_amount. - // This makes sell_amount * price[sell] / price[buy] = buy_amount exactly. - Some(Prices::Limit) => ( + // At limit price: price[sell_token] = buy_amount, price[buy_token] = + // sell_amount. This makes sell_amount * price[sell] / price[buy] = + // buy_amount exactly. + let (tokens, clearing_prices) = match &order.price_encoding { + PriceEncoding::LimitPrice => ( vec![order.data.sell_token, order.data.buy_token], vec![order.data.buy_amount, order.data.sell_amount], ), - None => { - return Err(BuildError::NoPriceEncoding); - } + PriceEncoding::Custom { + tokens, + clearing_prices, + } => (tokens.clone(), clearing_prices.clone()), }; let sell_token_index = tokens @@ -94,21 +91,8 @@ pub(crate) async fn encode( } } - let trade_data = match order.price_encoding { - PriceEncoding::Exact => std::borrow::Cow::Borrowed(&order.data), - PriceEncoding::Disadvantageous => { - let mut d = order.data.clone(); - match d.kind { - model::order::OrderKind::Sell => d.buy_amount = U256::ZERO, - model::order::OrderKind::Buy => { - d.sell_amount = d.sell_amount.max(U256::from(u128::MAX)) - } - } - std::borrow::Cow::Owned(d) - } - }; let trade = encode_trade( - &trade_data, + &order.data, &order.signature, order.owner, sell_token_index, From ff58e63bb6662b983f559ef705116a601c14a6aa Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Thu, 30 Apr 2026 14:09:10 +0000 Subject: [PATCH 33/44] Remove old code --- crates/orderbook/src/lib.rs | 1 - crates/orderbook/src/order_simulator.rs | 299 --------------------- crates/simulator/src/lib.rs | 1 - crates/simulator/src/swap_simulator.rs | 338 ------------------------ 4 files changed, 639 deletions(-) delete mode 100644 crates/orderbook/src/order_simulator.rs delete mode 100644 crates/simulator/src/swap_simulator.rs diff --git a/crates/orderbook/src/lib.rs b/crates/orderbook/src/lib.rs index c5c92d4a6a..2b380334fa 100644 --- a/crates/orderbook/src/lib.rs +++ b/crates/orderbook/src/lib.rs @@ -5,7 +5,6 @@ pub mod database; pub mod dto; mod ipfs; mod ipfs_app_data; -pub mod order_simulator; pub mod orderbook; mod quoter; pub mod run; diff --git a/crates/orderbook/src/order_simulator.rs b/crates/orderbook/src/order_simulator.rs deleted file mode 100644 index b6a604732b..0000000000 --- a/crates/orderbook/src/order_simulator.rs +++ /dev/null @@ -1,299 +0,0 @@ -use { - crate::dto::OrderSimulationResult, - alloy::{ - eips::BlockId, - primitives::{Address, Bytes, U256}, - rpc::types::state::AccountOverride, - }, - anyhow::{Context, Result, anyhow}, - app_data::WrapperCall, - balance_overrides::BalanceOverrideRequest, - contracts::support::{AnyoneAuthenticator, Trader}, - eth_domain_types::{BlockNo, NonZeroU256}, - model::order::Order, - shared::remaining_amounts, - simulator::{ - encoding::InteractionEncoding, - swap_simulator::{EncodedSwap, Query, SwapSimulator, TradeEncoding}, - tenderly, - }, - thiserror::Error, -}; -pub struct OrderSimulator { - simulator: SwapSimulator, - tenderly: Option>, - chain_id: String, -} - -#[derive(Error, Debug)] -pub enum Error { - #[error("simulation could not be created")] - Other(anyhow::Error), - #[error("malformed input")] - MalformedInput(anyhow::Error), -} - -impl OrderSimulator { - pub fn new( - simulator: SwapSimulator, - chain_id: String, - tenderly: Option>, - ) -> Self { - Self { - simulator, - tenderly, - chain_id, - } - } - - /// Calculates the remaining sell and buy amounts - /// Returns a tuple of (remaining_sell, remaining_buy) - async fn remaining_amounts( - &self, - order: &Order, - block: Option, - ) -> Result<(U256, U256), Error> { - let mut filled_amount_call = self - .simulator - .settlement - .filledAmount(Bytes::from(order.metadata.uid.0)); - - if let Some(block) = block { - filled_amount_call = filled_amount_call.block(block); - } - let executed_amount = filled_amount_call - .call() - .await - .map_err(|err| Error::Other(anyhow!(err)))?; - - let remaining_order = remaining_amounts::Order { - kind: order.data.kind, - buy_amount: order.data.buy_amount, - sell_amount: order.data.sell_amount, - fee_amount: order.data.fee_amount, - executed_amount, - partially_fillable: order.data.partially_fillable, - }; - let remaining = remaining_amounts::Remaining::from_order(&remaining_order) - .with_context(|| { - format!( - "could not compute remaining amounts for order {}", - order.metadata.uid - ) - }) - .map_err(Error::Other)?; - let remaining_sell = remaining - .remaining(order.data.sell_amount) - .context("overflow computing remaining sell amount") - .map_err(Error::Other)?; - let remaining_buy = remaining - .remaining(order.data.buy_amount) - .context("overflow computing remaining buy amount") - .map_err(Error::Other)?; - - Ok((remaining_sell, remaining_buy)) - } - - /// Encodes an order for simulation. - /// - /// `executed_amount` overrides how much of the order has already been - /// filled (in the order's fill token: sell token for sell orders, buy - /// token for buy orders). When `None`, the executed amount is taken from - /// the order's metadata, which reflects the actual on-chain fill state. - pub async fn encode_order( - &self, - order: &Order, - wrappers: Vec, - block: Option, - ) -> Result { - let tokens = vec![order.data.sell_token, order.data.buy_token]; - // Clearing prices represent the limit price of the order; both order kinds - // produce the same ratio: [buy_amount, sell_amount] for [sell_token, - // buy_token]. - let clearing_prices = vec![order.data.buy_amount, order.data.sell_amount]; - let solver = Address::random(); - let (remaining_sell, remaining_buy) = - self.remaining_amounts(order, block.map(Into::into)).await?; - let query = Query { - sell_amount: NonZeroU256::try_from(remaining_sell).map_err(|err| { - Error::MalformedInput(anyhow!("sell_amount `{}`: {err}", remaining_sell)) - })?, - sell_token: order.data.sell_token, - buy_amount: remaining_buy, - buy_token: order.data.buy_token, - kind: order.data.kind, - receiver: order.data.receiver.unwrap_or(order.metadata.owner), - sell_token_source: order.data.sell_token_balance, - buy_token_destination: order.data.buy_token_balance, - from: order.metadata.owner, - tx_origin: None, - clearing_prices, - solver, - tokens, - wrappers: wrappers - .iter() - .map(|wrapper| simulator::encoding::WrapperCall { - address: wrapper.address, - data: wrapper.data.clone().into(), - }) - .collect(), - }; - - let swap = self - .simulator - .fake_swap(&query, TradeEncoding::Simple) - .await - .map_err(Error::Other)?; - let swap = add_interactions(swap, order); - let swap = self.add_state_overrides(&query, swap).await?; - - Ok(swap) - } - - /// Simulates a swap of the provided EncodedSwap. - /// - /// The result contains the transaction simulation error (if any) - /// and a full API request object that can be used to resimulate the swap - /// using Tenderly. - pub async fn simulate_swap( - &self, - swap: EncodedSwap, - block_number: Option, - ) -> Result { - let block_number = - block_number.unwrap_or_else(|| self.simulator.current_block.borrow().number); - let result = self - .simulator - .simulate_settle_call(swap, block_number) - .await - .map_err(Error::Other)?; - - let tenderly_request = simulator::tenderly::dto::Request { - transaction_index: None, - save: Some(true), - save_if_fails: Some(true), - ..simulator::tenderly::prepare_request( - self.chain_id.clone(), - &result.tx, - result.overrides, - BlockNo(block_number), - ) - .map_err(|err| Error::Other(anyhow!(err)))? - }; - - let tenderly_url = match &self.tenderly { - Some(api) => match api.simulate_and_share(tenderly_request.clone()).await { - Ok(url) => Some(url), - Err(err) => { - tracing::warn!(?err, "failed to create Tenderly simulation"); - None - } - }, - None => None, - }; - - Ok(OrderSimulationResult { - tenderly_request, - tenderly_url, - error: result.result.err().map(|err| err.to_string()), - }) - } - - pub async fn add_state_overrides( - &self, - query: &Query, - mut swap: EncodedSwap, - ) -> Result { - // Override authenticator with AnyoneAuthenticator so our fake solver is - // accepted. - let authenticator = self - .simulator - .settlement - .authenticator() - .call() - .await - .context("could not fetch authenticator") - .map_err(Error::Other)?; - swap.overrides.insert( - authenticator, - AccountOverride { - code: Some(AnyoneAuthenticator::AnyoneAuthenticator::DEPLOYED_BYTECODE.clone()), - ..Default::default() - }, - ); - - // Set up fake solver. - swap.overrides.insert( - query.solver, - AccountOverride { - // Allow solver simulations to proceed even if the real account holds no ETH. - // The number is obscenely large, but not max to avoid potential overflows. - // We had this set to eth(1), but some simulations require more than that on non-ETH - // networks e.g. polygon so it led to reverts. - balance: Some(U256::MAX / U256::from(2)), - ..Default::default() - }, - ); - - // Override trader address with Trader bytecode so EIP-1271 signature - // verification works for EOA traders (settlement calls isValidSignature - // on the trader address, which would revert for plain EOAs). - swap.overrides.insert( - query.from, - AccountOverride { - code: Some(Trader::Trader::DEPLOYED_BYTECODE.clone()), - ..Default::default() - }, - ); - - // Fund the settlement contract with enough buy tokens to be paid out. - // Add 1 to account for ceiling division in the settlement contract's - // executedBuyAmount calculation, which can be 1 unit above remaining_buy. - match self - .simulator - .balance_overrides - .state_override(BalanceOverrideRequest { - token: query.buy_token, - holder: *self.simulator.settlement.address(), - amount: query.buy_amount + U256::ONE, - }) - .await - { - Some((token, balance_override)) => { - swap.overrides.insert(token, balance_override); - } - None => { - tracing::warn!("Could not set state balance override for the settlement contract"); - } - }; - - Ok(swap) - } -} - -fn add_interactions(mut swap: EncodedSwap, order: &Order) -> EncodedSwap { - // Add order pre interactions before encoded swap's pre interactions - let pre_interactions = order - .interactions - .pre - .iter() - .map(InteractionEncoding::encode) - .collect(); - // Prepend order pre_interactions so they run first - let settlement_pre_interactions = - std::mem::replace(&mut swap.settlement.interactions.pre, pre_interactions); - swap.settlement - .interactions - .pre - .extend(settlement_pre_interactions); - - // Add order post interactions after encoded swap's post interactions - let post_interactions = order - .interactions - .post - .iter() - .map(InteractionEncoding::encode); - swap.settlement.interactions.post.extend(post_interactions); - - swap -} diff --git a/crates/simulator/src/lib.rs b/crates/simulator/src/lib.rs index 2725227850..ac90a39592 100644 --- a/crates/simulator/src/lib.rs +++ b/crates/simulator/src/lib.rs @@ -2,7 +2,6 @@ pub mod encoding; pub mod ethereum; pub mod simulation_builder; mod simulation_encoding; -pub mod swap_simulator; pub mod tenderly; mod utils; diff --git a/crates/simulator/src/swap_simulator.rs b/crates/simulator/src/swap_simulator.rs deleted file mode 100644 index bf0b9f840d..0000000000 --- a/crates/simulator/src/swap_simulator.rs +++ /dev/null @@ -1,338 +0,0 @@ -use { - crate::encoding::{ - EncodedSettlement, - EncodedTrade, - Interactions, - WrapperCall, - encode_trade, - encode_wrapper_settlement, - }, - alloy_primitives::{Address, Bytes, U256}, - alloy_provider::Provider, - alloy_rpc_types::{TransactionRequest, state::StateOverride}, - alloy_sol_types::SolCall, - anyhow::{Context, Result, anyhow}, - balance_overrides::BalanceOverriding, - contracts::{ - GPv2Settlement::{self}, - WETH9, - support::Solver::{self, Solver::swapReturn}, - }, - eth_domain_types::NonZeroU256, - ethrpc::{ - Web3, - block_stream::{BlockInfo, CurrentBlockWatcher}, - }, - model::{ - DomainSeparator, - order::{BUY_ETH_ADDRESS, BuyTokenDestination, OrderData, OrderKind, SellTokenSource}, - signature::{Signature, SigningScheme}, - }, - std::sync::Arc, -}; - -/// Query for the Swap Simulator to prepare a fake settlement with -/// Contains the minimum data required to encode a fake settlement -#[derive(Debug)] -pub struct Query { - pub sell_token: Address, - pub sell_amount: NonZeroU256, - pub buy_token: Address, - pub buy_amount: U256, - pub kind: OrderKind, - pub receiver: Address, - pub sell_token_source: SellTokenSource, - pub buy_token_destination: BuyTokenDestination, - pub from: Address, - pub tx_origin: Option
, - pub solver: Address, - pub tokens: Vec
, - pub clearing_prices: Vec, - pub wrappers: Vec, -} - -/// Controls how the trade is encoded for the provided Query -#[derive(Clone, Debug)] -pub enum TradeEncoding { - /// Encodes the trade amounts exactly as in the Query - Simple, - /// Encodes a trade with the most disadvantageous in and out amounts - /// possible (while taking possible overflows into account). Should the - /// trader not receive the amount promised by the [`Query`] the - /// simulation will still work and the actual out amount can be computed - /// afterwards. - Disadvantageous, -} - -#[derive(Clone)] -pub struct SwapSimulator { - pub balance_overrides: Arc, - pub settlement: GPv2Settlement::Instance, - pub native_token: Address, - pub domain_separator: DomainSeparator, - pub current_block: CurrentBlockWatcher, - pub web3: Web3, - pub gas_limit: u64, -} - -pub struct EncodedSwap { - pub settlement: EncodedSettlement, - pub overrides: StateOverride, - pub wrappers: Vec, - pub solver: Address, - pub receiver: Address, -} - -/// The output of a swap simulation. -/// -/// Contains the transaction request that was used to perform the simulation -/// (useful for introspection), The used state overrides and simulation result -/// The result is of generic type O, and depends on the type of simulation: -/// - solver swap simulation returns Solver::swapResult -/// - generic simulation returns Bytes -pub struct SwapSimulation { - pub tx: TransactionRequest, - pub overrides: StateOverride, - pub result: Result, -} - -impl SwapSimulator { - pub async fn new( - balance_overrides: Arc, - settlement: Address, - native_token: Address, - current_block: CurrentBlockWatcher, - web3: Web3, - gas_limit: u64, - ) -> Result { - let settlement = GPv2Settlement::GPv2Settlement::new(settlement, web3.provider.clone()); - let domain_separator = DomainSeparator(settlement.domainSeparator().call().await?.0); - - Ok(Self { - balance_overrides, - settlement, - native_token, - current_block, - web3, - gas_limit, - domain_separator, - }) - } - - /// Creates a fake swap based on the provided query - /// The result can be further post processed depending on the needs - /// - /// It can then be simulated with SwapSimulator::simulate_swap - /// - /// The trade_encoding controls if the trade should be encoded as-is, - /// based on the Query or if it should be encoded as the most - /// disadvantegous trade possible. - /// - /// The TradeEncoding::Disadvantegous is useful for price verification since - /// the resulting out amounts can be calculated later while allowing the - /// simulation to pass. - pub async fn fake_swap( - &self, - query: &Query, - trade_encoding: TradeEncoding, - ) -> Result { - let overrides = StateOverride::default(); - - let pre_interactions = Vec::new(); - let mut interactions = Vec::new(); - - if query.buy_token == BUY_ETH_ADDRESS { - // Because the `driver` manages `WETH` unwraps under the hood the `TradeFinder` - // does not have to emit unwraps to pay out `ETH` in a trade. - // However, for the simulation to be successful this has to happen so we do it - // ourselves here. - interactions.push(( - self.native_token, - U256::ZERO, - WETH9::WETH9::withdrawCall { - wad: query.buy_amount, - } - .abi_encode() - .into(), - )); - tracing::trace!("adding unwrap interaction for paying out ETH"); - } - - Ok(EncodedSwap { - settlement: EncodedSettlement { - tokens: query.tokens.to_vec(), - clearing_prices: query.clearing_prices.to_vec(), - trades: vec![encode_fake_trade(query, trade_encoding)?], - interactions: Interactions { - pre: pre_interactions, - main: interactions, - post: Vec::new(), - }, - }, - solver: query.tx_origin.unwrap_or(query.solver), - receiver: query.receiver, - overrides, - wrappers: query.wrappers.clone(), - }) - } - - /// For wrapped settlements, the Solver contract must call the first wrapper - /// (not the settlement directly). The wrapper then chains to the - /// settlement. For non-wrapped settlements, the Solver calls the - /// settlement contract directly. - fn get_target_and_calldata(&self, swap: &EncodedSwap) -> (Address, Bytes) { - if !swap.wrappers.is_empty() { - encode_wrapper_settlement(&swap.wrappers, swap.settlement.into_settle_call()) - .expect("wrappers is not empty") - } else { - ( - *self.settlement.address(), - swap.settlement.into_settle_call(), - ) - } - } - - /// Simulates a solver call to settlement contract with the provided swap - /// data. The swap call result is contained in the returned - /// SwapSimulation struct, along with the original TransactionRequest - /// and State overrides (if needed to be logged, or processed elsewhere). - /// - /// The caller supplies the `block` so the gas-price computation, the - /// pinned `.call()` block, and any downstream logging all reference the - /// same snapshot of chain state. - pub async fn simulate_swap_with_solver( - &self, - swap: EncodedSwap, - block: BlockInfo, - ) -> Result> { - let (settlement_target, calldata) = self.get_target_and_calldata(&swap); - let solver = Solver::Instance::new(swap.solver, self.web3.provider.clone()); - let overrides = swap.overrides; - - let swap = solver - .swap( - settlement_target, - swap.settlement.tokens.clone(), - swap.receiver, - calldata, - ) - .from(swap.solver) - .gas(self.gas_limit) - .gas_price( - u128::try_from(block.gas_price.saturating_mul(U256::from(2))) - .map_err(|err| anyhow!(err)) - .context("converting gas price to u128")?, - ); - - // Save the transaction request, so the caller can inspect it. - // For example, to create a tenderly API request and provide the ability to - // simulate it. - let tx = swap.clone().into_transaction_request(); - let result = swap - .call() - .overrides(overrides.clone()) - .block(block.number.into()) - .await - .map_err(|err| anyhow!(err)) - .context("failed to simulate swap"); - - Ok(SwapSimulation { - tx, - overrides, - result, - }) - } - - /// Simulate settle call on the latest block - pub async fn simulate_settle_call_on_latest( - &self, - swap: EncodedSwap, - ) -> Result> { - let block_number = self.current_block.borrow().number; - self.simulate_settle_call(swap, block_number).await - } - - pub async fn simulate_settle_call( - &self, - swap: EncodedSwap, - block_number: u64, - ) -> Result> { - let (settlement_target, calldata) = self.get_target_and_calldata(&swap); - - let overrides = swap.overrides; - let tx = TransactionRequest { - from: Some(swap.solver), - to: Some(settlement_target.into()), - input: calldata.into(), - gas: Some(self.gas_limit), - ..Default::default() - }; - - let result = self - .web3 - .provider - .call(tx.clone()) - .overrides(overrides.clone()) - .block(block_number.into()) - .await - .map_err(|err| anyhow!(err)); - - Ok(SwapSimulation { - tx, - overrides, - result, - }) - } -} - -fn encode_fake_trade(query: &Query, trade_encoding: TradeEncoding) -> Result { - let (sell_amount, buy_amount) = match trade_encoding { - TradeEncoding::Simple => (query.sell_amount.into(), query.buy_amount), - TradeEncoding::Disadvantageous => match query.kind { - OrderKind::Sell => (query.sell_amount.get(), U256::ZERO), - OrderKind::Buy => ( - query.sell_amount.get().max(U256::from(u128::MAX)), - query.buy_amount, - ), - }, - }; - - let fake_order = OrderData { - sell_token: query.sell_token, - sell_amount, - buy_token: query.buy_token, - buy_amount, - receiver: Some(query.receiver), - valid_to: u32::MAX, - app_data: Default::default(), - fee_amount: U256::ZERO, - kind: query.kind, - partially_fillable: false, - sell_token_balance: query.sell_token_source, - buy_token_balance: query.buy_token_destination, - }; - - let fake_signature = Signature::default_with(SigningScheme::Eip1271); - let encoded_trade = encode_trade( - &fake_order, - &fake_signature, - query.from, - // the tokens set length is small so the linear search is acceptable - query - .tokens - .iter() - .position(|token| token == &query.sell_token) - .context("missing sell token index")?, - query - .tokens - .iter() - .position(|token| token == &query.buy_token) - .context("missing buy token index")?, - match query.kind { - OrderKind::Sell => query.sell_amount.get(), - OrderKind::Buy => query.buy_amount, - }, - ); - - Ok(encoded_trade) -} From 104b759fc0c1b0ba1df3c915881750fcab4a51ec Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Thu, 30 Apr 2026 14:45:11 +0000 Subject: [PATCH 34/44] assemble state overrides via simulator crate --- crates/e2e/tests/e2e/quote_verification.rs | 3 +- crates/price-estimation/src/factory.rs | 3 +- .../src/trade_verifier/mod.rs | 161 +++++++----------- crates/simulator/src/simulation_builder.rs | 2 +- crates/simulator/src/simulation_encoding.rs | 4 +- 5 files changed, 65 insertions(+), 108 deletions(-) diff --git a/crates/e2e/tests/e2e/quote_verification.rs b/crates/e2e/tests/e2e/quote_verification.rs index 6d1f5aa749..606f141bd9 100644 --- a/crates/e2e/tests/e2e/quote_verification.rs +++ b/crates/e2e/tests/e2e/quote_verification.rs @@ -199,7 +199,7 @@ async fn test_bypass_verification_for_rfq_quotes(web3: Web3) { Default::default(), Default::default(), *onchain.contracts().weth.address(), - balance_overrides.clone(), + balance_overrides, block_stream.clone(), None, ) @@ -211,7 +211,6 @@ async fn test_bypass_verification_for_rfq_quotes(web3: Web3) { gas_limit, None, Arc::new(web3.clone()), - balance_overrides, BigDecimal::zero(), Default::default(), 0, diff --git a/crates/price-estimation/src/factory.rs b/crates/price-estimation/src/factory.rs index cafa889f0d..4de8a55d71 100644 --- a/crates/price-estimation/src/factory.rs +++ b/crates/price-estimation/src/factory.rs @@ -113,7 +113,7 @@ impl<'a> PriceEstimatorFactory<'a> { Default::default(), Default::default(), network.native_token, - balance_overrides.clone(), + balance_overrides, network.block_stream.clone(), tenderly.clone(), ) @@ -124,7 +124,6 @@ impl<'a> PriceEstimatorFactory<'a> { args.max_gas_per_tx, tenderly, components.code_fetcher.clone(), - balance_overrides, args.quote_inaccuracy_limit.clone(), args.tokens_without_verification.iter().cloned().collect(), args.min_gas_amount_for_unverified_quotes, diff --git a/crates/price-estimation/src/trade_verifier/mod.rs b/crates/price-estimation/src/trade_verifier/mod.rs index 3060ed41c1..5c6c5c14c6 100644 --- a/crates/price-estimation/src/trade_verifier/mod.rs +++ b/crates/price-estimation/src/trade_verifier/mod.rs @@ -10,16 +10,12 @@ use { trade_verifier::code_fetching::CodeFetching, }, ::alloy::sol_types::SolCall, - alloy::{ - primitives::{Address, Bytes, U256, address, aliases::I512}, - rpc::types::state::AccountOverride, - }, + alloy::primitives::{Address, Bytes, U256, address, aliases::I512}, anyhow::{Context, Result}, - balance_overrides::{BalanceOverrideRequest, BalanceOverriding}, bigdecimal::BigDecimal, contracts::{ WETH9, - support::{AnyoneAuthenticator, Solver, Spardose, Trader}, + support::{Solver, Spardose, Trader}, }, model::{ DomainSeparator, @@ -41,6 +37,7 @@ use { encoding::{EncodedTrade, encode_trade}, simulation_builder::{ self as sim_builder, + AccountOverrideRequest, ExecutionAmount, PriceEncoding, SettlementSimulator, @@ -77,7 +74,6 @@ pub struct TradeVerifier { simulator: SettlementSimulator, gas_limit: u64, code_fetcher: Arc, - balance_overrides: Arc, quote_inaccuracy_limit: BigRational, tokens_without_verification: HashSet
, min_gas_amount_for_unverified_quotes: u32, @@ -94,7 +90,6 @@ impl TradeVerifier { gas_limit: u64, tenderly: Option>, code_fetcher: Arc, - balance_overrides: Arc, quote_inaccuracy_limit: BigDecimal, tokens_without_verification: HashSet
, min_gas_amount_for_unverified_quotes: u32, @@ -110,7 +105,6 @@ impl TradeVerifier { simulator, gas_limit, code_fetcher, - balance_overrides, quote_inaccuracy_limit: big_decimal_to_big_rational("e_inaccuracy_limit), tokens_without_verification, min_gas_amount_for_unverified_quotes, @@ -126,8 +120,17 @@ impl TradeVerifier { out_amount: &U256, ) -> Result { let start = std::time::Instant::now(); - let overrides = self - .prepare_state_overrides(&mut verification, query, trade) + + if verification.from.is_zero() { + verification.from = Address::random(); + tracing::debug!( + trader = ?verification.from, + "use random trader address with fake balances" + ); + } + + let override_requests = self + .prepare_state_overrides(&verification, query, trade) .await?; // Use `tx_origin` if response indicates that a special address is needed for @@ -239,7 +242,7 @@ impl TradeVerifier { buy_token_balance: verification.buy_token_destination, }; - let mut eth_call_inputs = self + let mut builder = self .simulator .new_simulation_builder() .add_order( @@ -260,13 +263,17 @@ impl TradeVerifier { .with_pre_interactions(pre_interactions) .with_main_interactions(main_interactions) .with_post_interactions(post_interactions) - .add_extra_trades(jit_trades) + .add_extra_trades(jit_trades); + + for req in override_requests { + builder = builder.with_override(req); + } + + let eth_call_inputs = builder .build() .await .map_err(|e| Error::SimulationFailed(anyhow::anyhow!("{e}")))?; - eth_call_inputs.state_overrides.extend(overrides); - let settlement_target = eth_call_inputs .request .to @@ -436,12 +443,20 @@ impl TradeVerifier { /// trade. async fn prepare_state_overrides( &self, - verification: &mut Verification, + verification: &Verification, query: &PriceQuery, trade: &TradeKind, - ) -> Result { - let mut overrides = alloy::primitives::map::AddressMap::default(); + ) -> Result> { + let mut requests: Vec = Vec::new(); + // Setup the funding contract override. Regardless of whether or not the + // contract has funds, it needs to exist in order to not revert + // simulations (Solidity reverts on attempts to call addresses without + // any code). + requests.push(AccountOverrideRequest::Code { + account: Self::SPARDOSE, + code: Spardose::Spardose::DEPLOYED_BYTECODE.clone(), + }); // Provide mocked balances if possible to the spardose to allow it to // give some balances to the trader in order to verify trades even for // owners without balances. Note that we use a separate account for @@ -460,47 +475,17 @@ impl TradeVerifier { &query.kind, )?, }; - match self - .balance_overrides - .state_override(BalanceOverrideRequest { - token: query.sell_token, - holder: Self::SPARDOSE, - amount: spardose_amount_with_buffer(needed), - }) - .await - { - Some((token, solver_balance_override)) => { - tracing::trace!(?solver_balance_override, "solver balance override enabled"); - overrides.insert(token, solver_balance_override); - - if verification.from.is_zero() { - verification.from = Address::random(); - tracing::debug!( - trader = ?verification.from, - "use random trader address with fake balances" - ); - } - } - _ => { - tracing::warn!( - sell_token = ?query.sell_token, - "could not set spardose balance override for sell token; trade \ - verification will rely on the trader's real balance" - ); - if verification.from.is_zero() { - anyhow::bail!("trader is zero address and balances can not be faked"); - } - } - } + requests.push(AccountOverrideRequest::Balance { + holder: Self::SPARDOSE, + token: query.sell_token, + amount: spardose_amount_with_buffer(needed), + }); // Set up mocked trader. - overrides.insert( - verification.from, - AccountOverride { - code: Some(Trader::Trader::DEPLOYED_BYTECODE.clone()), - ..Default::default() - }, - ); + requests.push(AccountOverrideRequest::Code { + account: verification.from, + code: Trader::Trader::DEPLOYED_BYTECODE.clone(), + }); // If the trader is a smart contract we also need to store its implementation // to proxy into it during the simulation. @@ -510,56 +495,30 @@ impl TradeVerifier { .await .context("failed to fetch trader code")?; if !trader_impl.0.is_empty() { - overrides.insert( - Self::TRADER_IMPL, - AccountOverride { - code: Some(trader_impl), - ..Default::default() - }, - ); + requests.push(AccountOverrideRequest::Code { + account: Self::TRADER_IMPL, + code: trader_impl, + }); } - // Setup the funding contract override. Regardless of whether or not the - // contract has funds, it needs to exist in order to not revert - // simulations (Solidity reverts on attempts to call addresses without - // any code). - overrides.insert( - Self::SPARDOSE, - AccountOverride { - code: Some(Spardose::Spardose::DEPLOYED_BYTECODE.clone()), - ..Default::default() - }, - ); - - // Set up mocked solver. - let solver_override = AccountOverride { - code: Some(Solver::Solver::DEPLOYED_BYTECODE.clone()), - // Allow solver simulations to proceed even if the real account holds no ETH. - // The number is obscenely large, but not max to avoid potential overflows. - // We had this set to eth(1), but some simulations require more than that on non-ETH - // netowrks e.g. polygon so it led to reverts. - balance: Some(U256::MAX / U256::from(2)), - ..Default::default() - }; + // Set up mocked solver with enough ETH to proceed even if the real account + // holds none. + let solver = trade.tx_origin().unwrap_or(trade.solver()); + requests.push(AccountOverrideRequest::Code { + account: solver, + code: Solver::Solver::DEPLOYED_BYTECODE.clone(), + }); + requests.push(AccountOverrideRequest::SufficientEthBalance(solver)); - // If the trade requires a special tx.origin we also need to fake the - // authenticator. - if trade - .tx_origin() - .is_some_and(|origin| origin != trade.solver()) + // If the trade requires a special tx.origin we also need to allow list + // it in the authenticator + if let Some(custom_origin) = trade.tx_origin() + && custom_origin != trade.solver() { - overrides.insert( - self.simulator.authenticator_address(), - AccountOverride { - code: Some(AnyoneAuthenticator::AnyoneAuthenticator::DEPLOYED_BYTECODE.clone()), - ..Default::default() - }, - ); + requests.push(AccountOverrideRequest::AuthenticateAsSolver(custom_origin)) } - overrides.insert(trade.tx_origin().unwrap_or(trade.solver()), solver_override); - - Ok(overrides) + Ok(requests) } /// Create interaction that sets up the trade right before transfering diff --git a/crates/simulator/src/simulation_builder.rs b/crates/simulator/src/simulation_builder.rs index 97c5f4bf2e..a82d18db4a 100644 --- a/crates/simulator/src/simulation_builder.rs +++ b/crates/simulator/src/simulation_builder.rs @@ -515,7 +515,7 @@ pub enum AccountOverrideRequest { /// Gives the address a huge amount of ETH. SufficientEthBalance(Address), /// Allowlists an address as a solver to let it settle orders. - AuthenticateAddress(Address), + AuthenticateAsSolver(Address), /// Computes necessary state overrides for the requested balance. Balance { holder: Address, diff --git a/crates/simulator/src/simulation_encoding.rs b/crates/simulator/src/simulation_encoding.rs index 9211c641df..5c5b3c725f 100644 --- a/crates/simulator/src/simulation_encoding.rs +++ b/crates/simulator/src/simulation_encoding.rs @@ -163,7 +163,7 @@ pub(crate) async fn encode( .push(AccountOverrideRequest::SufficientEthBalance(addr)); builder .account_override_requests - .push(AccountOverrideRequest::AuthenticateAddress(addr)); + .push(AccountOverrideRequest::AuthenticateAsSolver(addr)); addr } None => return Err(BuildError::NoSolver), @@ -234,7 +234,7 @@ async fn build_final_state_overrides( addr, AccountOverride::default().with_balance(U256::MAX / U256::from(2)), )), - AccountOverrideRequest::AuthenticateAddress(addr) => { + AccountOverrideRequest::AuthenticateAsSolver(addr) => { // GPv2AllowListAuthentication stores `mapping(address => bool) managers` // at storage slot 1. Solidity mapping key: keccak256(address_padded ++ // slot_padded). From 12dead3b6525ed73debf0091f172dfdcc83d0a10 Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Thu, 30 Apr 2026 14:55:20 +0000 Subject: [PATCH 35/44] best effort override resolution --- crates/simulator/src/simulation_builder.rs | 7 +- crates/simulator/src/simulation_encoding.rs | 116 ++++++++++---------- 2 files changed, 63 insertions(+), 60 deletions(-) diff --git a/crates/simulator/src/simulation_builder.rs b/crates/simulator/src/simulation_builder.rs index a82d18db4a..ddec6d0838 100644 --- a/crates/simulator/src/simulation_builder.rs +++ b/crates/simulator/src/simulation_builder.rs @@ -499,18 +499,15 @@ pub enum BuildError { MissingSellToken, #[error("buy token not found in token list")] MissingBuyToken, - #[error("could not override token balances to fund settlement contract")] - FailedToOverrideBalances, #[error("failed to query filled amount from settlement contract: {0}")] FilledAmountQuery(#[source] anyhow::Error), #[error("failed to parse app data: {0}")] AppDataParse(#[source] serde_json::Error), #[error("both wrappers and flashloans cannot be encoded in the same settlement")] FlashloanWrappersIncompatible, - #[error("conflicting state overrides for the same account: {0}")] - ConflictingStateOverrides(#[source] MergeConflict), } +#[derive(Debug)] pub enum AccountOverrideRequest { /// Gives the address a huge amount of ETH. SufficientEthBalance(Address), @@ -538,7 +535,7 @@ pub enum AccountOverrideRequest { /// Error returned when two [`AccountOverride`]s set the same field for the same /// address and cannot be merged. #[derive(Debug, thiserror::Error)] -pub enum MergeConflict { +pub(crate) enum MergeConflict { #[error("both overrides set the ETH balance")] Balance, #[error("both overrides set the nonce")] diff --git a/crates/simulator/src/simulation_encoding.rs b/crates/simulator/src/simulation_encoding.rs index 5c5b3c725f..82c2a983fd 100644 --- a/crates/simulator/src/simulation_encoding.rs +++ b/crates/simulator/src/simulation_encoding.rs @@ -29,7 +29,6 @@ use { alloy_sol_types::SolCall, balance_overrides::{BalanceOverrideRequest, BalanceOverriding}, model::order::OrderKind, - std::sync::Arc, }; pub(crate) async fn encode( @@ -170,10 +169,10 @@ pub(crate) async fn encode( }; let state_overrides = build_final_state_overrides( builder.account_override_requests, - Arc::clone(&builder.simulator.0.balance_overrides), + builder.simulator.0.balance_overrides.as_ref(), builder.simulator.0.authenticator, ) - .await?; + .await; Ok(EthCallInputs { request: TransactionRequest { @@ -219,72 +218,79 @@ async fn executed_amount( }) } -/// Resolves all [`AccountOverrideRequest`]s concurrently, merges them -/// and returns the final [`StateOverride`]. +/// Resolves all [`AccountOverrideRequest`]s concurrently on a best-effort +/// basis. Failures are logged and the corresponding override is skipped rather +/// than aborting the whole build. async fn build_final_state_overrides( requests: Vec, - balance_overrides: Arc, + balance_overrides: &dyn BalanceOverriding, authenticator: Address, -) -> Result { - let futures = requests.into_iter().map(|request| { - let balance_overrides = Arc::clone(&balance_overrides); - async move { - match request { - AccountOverrideRequest::SufficientEthBalance(addr) => Ok(( - addr, - AccountOverride::default().with_balance(U256::MAX / U256::from(2)), - )), - AccountOverrideRequest::AuthenticateAsSolver(addr) => { - // GPv2AllowListAuthentication stores `mapping(address => bool) managers` - // at storage slot 1. Solidity mapping key: keccak256(address_padded ++ - // slot_padded). - let mut buf = [0u8; 64]; - buf[12..32].copy_from_slice(addr.as_slice()); - buf[32..64].copy_from_slice(&U256::ONE.to_be_bytes::<32>()); - let slot = keccak256(buf); - Ok(( - authenticator, - AccountOverride::default() - .with_state_diff(std::iter::once((slot, B256::with_last_byte(1)))), - )) - } - AccountOverrideRequest::Balance { - holder, - token, - amount, - } => balance_overrides +) -> StateOverride { + let futures = requests.into_iter().map(|request| async move { + match request { + AccountOverrideRequest::SufficientEthBalance(addr) => Some(( + addr, + AccountOverride::default().with_balance(U256::MAX / U256::from(2)), + )), + AccountOverrideRequest::AuthenticateAsSolver(addr) => { + // GPv2AllowListAuthentication stores `mapping(address => bool) managers` + // at storage slot 1. Solidity mapping key: keccak256(address_padded ++ + // slot_padded). + let mut buf = [0u8; 64]; + buf[12..32].copy_from_slice(addr.as_slice()); + buf[32..64].copy_from_slice(&U256::ONE.to_be_bytes::<32>()); + let slot = keccak256(buf); + Some(( + authenticator, + AccountOverride::default() + .with_state_diff(std::iter::once((slot, B256::with_last_byte(1)))), + )) + } + AccountOverrideRequest::Balance { + holder, + token, + amount, + } => { + let result = balance_overrides .state_override(BalanceOverrideRequest { token, holder, amount, }) - .await - .ok_or(BuildError::FailedToOverrideBalances), - AccountOverrideRequest::BuyTokensForBuffers => { - unreachable!( - "replaced with specific Balance requests before state overrides get \ - computed" - ) + .await; + if result.is_none() { + tracing::warn!(%token, %holder, "failed to compute balance state override, skipping"); } - AccountOverrideRequest::Code { account, code } => Ok(( - account, - AccountOverride { - code: Some(code), - ..Default::default() - }, - )), - AccountOverrideRequest::Custom { account, state } => Ok((account, state)), + result } + AccountOverrideRequest::BuyTokensForBuffers => { + unreachable!( + "replaced with specific Balance requests before state overrides get \ + computed" + ) + } + AccountOverrideRequest::Code { account, code } => Some(( + account, + AccountOverride { + code: Some(code), + ..Default::default() + }, + )), + AccountOverrideRequest::Custom { account, state } => Some((account, state)), } }); - let resolved_overrides = futures::future::try_join_all(futures).await?; let mut state_overrides = StateOverride::default(); - for (address, account_override) in resolved_overrides { - apply_account_override(&mut state_overrides, address, account_override) - .map_err(BuildError::ConflictingStateOverrides)?; + for (address, account_override) in futures::future::join_all(futures) + .await + .into_iter() + .flatten() + { + if let Err(err) = apply_account_override(&mut state_overrides, address, account_override) { + tracing::warn!(?err, %address, "conflicting state overrides for address, skipping"); + } } - Ok(state_overrides) + state_overrides } /// Merges `new` into `existing` field by field. @@ -350,7 +356,7 @@ fn merge_account_override( /// /// If `address` already has an entry, the overrides are merged via /// [`merge_account_override`]. Returns an error on conflict. -pub fn apply_account_override( +pub(crate) fn apply_account_override( overrides: &mut StateOverride, address: Address, new: AccountOverride, From aad3997db1603dfc2526211c59b6e94c11ac3827 Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Thu, 30 Apr 2026 15:12:01 +0000 Subject: [PATCH 36/44] Remove customize functionality --- crates/simulator/src/simulation_builder.rs | 12 +----------- crates/simulator/src/simulation_encoding.rs | 5 +---- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/crates/simulator/src/simulation_builder.rs b/crates/simulator/src/simulation_builder.rs index ddec6d0838..5eef88a7ec 100644 --- a/crates/simulator/src/simulation_builder.rs +++ b/crates/simulator/src/simulation_builder.rs @@ -252,19 +252,9 @@ impl SimulationBuilder { /// Finishes the simulation struct based on the configuration thus far. pub async fn build(self) -> Result { - self.build_with_modifications(|_| {}).await - } - - /// Same as `build()` but allows the caller to alter the simulation - /// before it gets finalized. This should only be used for very specific - /// setups. - pub async fn build_with_modifications( - self, - customize: impl FnOnce(&mut EncodedSettlement), - ) -> Result { // Forward to a helper function to split the boring repetitive builder // code from the non-trivial code that actually does the encoding. - crate::simulation_encoding::encode(self, customize).await + crate::simulation_encoding::encode(self).await } } diff --git a/crates/simulator/src/simulation_encoding.rs b/crates/simulator/src/simulation_encoding.rs index 82c2a983fd..24b53442b4 100644 --- a/crates/simulator/src/simulation_encoding.rs +++ b/crates/simulator/src/simulation_encoding.rs @@ -31,10 +31,7 @@ use { model::order::OrderKind, }; -pub(crate) async fn encode( - mut builder: SimulationBuilder, - customize: impl FnOnce(&mut EncodedSettlement), -) -> Result { +pub(crate) async fn encode(mut builder: SimulationBuilder) -> Result { let order = builder.order.as_ref().ok_or(BuildError::NoOrder)?; let block = match builder.block { From 56b020bfed03189a8f1ff21dff0006290c7040b1 Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Thu, 30 Apr 2026 15:20:10 +0000 Subject: [PATCH 37/44] Support multiple orders --- crates/orderbook/src/orderbook.rs | 54 ++++---- .../src/trade_verifier/mod.rs | 126 +++++++---------- crates/simulator/src/simulation_builder.rs | 24 +--- crates/simulator/src/simulation_encoding.rs | 129 ++++++++++-------- 4 files changed, 158 insertions(+), 175 deletions(-) diff --git a/crates/orderbook/src/orderbook.rs b/crates/orderbook/src/orderbook.rs index afb38f4bbd..ed29cdb970 100644 --- a/crates/orderbook/src/orderbook.rs +++ b/crates/orderbook/src/orderbook.rs @@ -639,14 +639,12 @@ impl Orderbook { let sim = order_simulator .new_simulation_builder() - .add_order( - simulation_builder::Order::new(order.data) - .with_signature(order.metadata.owner, order.signature) - .fill_at( - simulation_builder::ExecutionAmount::Remaining, - simulation_builder::PriceEncoding::LimitPrice, - ), - ) + .add_orders([simulation_builder::Order::new(order.data) + .with_signature(order.metadata.owner, order.signature) + .fill_at( + simulation_builder::ExecutionAmount::Remaining, + simulation_builder::PriceEncoding::LimitPrice, + )]) .parameters_from_app_data(&full_app_data) .context("failed to parse app data")? .at_block( @@ -685,27 +683,25 @@ impl Orderbook { let sim = order_simulator .new_simulation_builder() - .add_order( - simulation_builder::Order::new(OrderData { - sell_token: request.sell_token, - buy_token: request.buy_token, - sell_amount: request.sell_amount.into(), - buy_amount: request.buy_amount, - kind: request.kind, - receiver: request.receiver, - sell_token_balance: request.sell_token_balance, - buy_token_balance: request.buy_token_balance, - fee_amount: request.fee_amount, - valid_to: request.valid_to, - app_data: AppDataHash(app_data_hash.into()), - partially_fillable: request.partially_fillable, - }) - .with_signature(request.owner, request.signature) - .fill_at( - simulation_builder::ExecutionAmount::Full, - simulation_builder::PriceEncoding::LimitPrice, - ), - ) + .add_orders([simulation_builder::Order::new(OrderData { + sell_token: request.sell_token, + buy_token: request.buy_token, + sell_amount: request.sell_amount.into(), + buy_amount: request.buy_amount, + kind: request.kind, + receiver: request.receiver, + sell_token_balance: request.sell_token_balance, + buy_token_balance: request.buy_token_balance, + fee_amount: request.fee_amount, + valid_to: request.valid_to, + app_data: AppDataHash(app_data_hash.into()), + partially_fillable: request.partially_fillable, + }) + .with_signature(request.owner, request.signature) + .fill_at( + simulation_builder::ExecutionAmount::Full, + simulation_builder::PriceEncoding::LimitPrice, + )]) .parameters_from_app_data(&request.app_data) .context("failed to parse app data")? .at_block( diff --git a/crates/price-estimation/src/trade_verifier/mod.rs b/crates/price-estimation/src/trade_verifier/mod.rs index 5c6c5c14c6..b6a14e9e21 100644 --- a/crates/price-estimation/src/trade_verifier/mod.rs +++ b/crates/price-estimation/src/trade_verifier/mod.rs @@ -34,7 +34,6 @@ use { nonzero::NonZeroU256, }, simulator::{ - encoding::{EncodedTrade, encode_trade}, simulation_builder::{ self as sim_builder, AccountOverrideRequest, @@ -43,7 +42,7 @@ use { SettlementSimulator, Solver as SimSolver, }, - tenderly::{self}, + tenderly, }, std::{ collections::{HashMap, HashSet}, @@ -214,20 +213,13 @@ impl TradeVerifier { .chain(map_interactions_data(verification.post_interactions.iter())) .collect(); - let jit_trades = match trade { - TradeKind::Regular(t) => { - encode_jit_orders(&t.jit_orders, &tokens, &self.simulator.domain_separator())? - } - _ => vec![], - }; - // Set limit amounts to always pass the settlement check so the actual // out_amount can be measured via the storeBalance interactions. let (fake_sell_amount, fake_buy_amount) = match query.kind { OrderKind::Sell => (sell_amount.get(), U256::ZERO), OrderKind::Buy => (sell_amount.get().max(U256::from(u128::MAX)), buy_amount), }; - let fake_order = OrderData { + let fake_order = sim_builder::Order::new(OrderData { sell_token: query.sell_token, sell_amount: fake_sell_amount, buy_token: query.buy_token, @@ -240,30 +232,65 @@ impl TradeVerifier { partially_fillable: false, sell_token_balance: verification.sell_token_source, buy_token_balance: verification.buy_token_destination, + }) + .with_signature( + verification.from, + Signature::default_with(SigningScheme::Eip1271), + ) + .fill_at( + ExecutionAmount::Full, + PriceEncoding::Custom { + tokens: tokens.clone(), + clearing_prices, + }, + ); + + let jit_orders: Vec = match trade { + TradeKind::Regular(t) => t + .jit_orders + .iter() + .map(|jit_order| { + let order_data = OrderData { + sell_token: jit_order.sell_token, + buy_token: jit_order.buy_token, + receiver: Some(jit_order.receiver), + sell_amount: jit_order.sell_amount, + buy_amount: jit_order.buy_amount, + valid_to: jit_order.valid_to, + app_data: jit_order.app_data, + fee_amount: U256::ZERO, + kind: match &jit_order.side { + Side::Buy => OrderKind::Buy, + Side::Sell => OrderKind::Sell, + }, + partially_fillable: jit_order.partially_fillable, + sell_token_balance: jit_order.sell_token_source, + buy_token_balance: jit_order.buy_token_destination, + }; + let (owner, signature) = recover_jit_order_owner( + jit_order, + &order_data, + &self.simulator.domain_separator(), + )?; + Ok(sim_builder::Order::new(order_data) + .with_signature(owner, signature) + .fill_at( + ExecutionAmount::Explicit(jit_order.executed_amount), + PriceEncoding::LimitPrice, + )) + }) + .collect::>()?, + _ => vec![], }; let mut builder = self .simulator .new_simulation_builder() - .add_order( - sim_builder::Order::new(fake_order) - .with_signature( - verification.from, - Signature::default_with(SigningScheme::Eip1271), - ) - .fill_at( - ExecutionAmount::Full, - PriceEncoding::Custom { - tokens: tokens.clone(), - clearing_prices, - }, - ), - ) + .add_orders(std::iter::once(fake_order).chain(jit_orders)) .from_solver(SimSolver::Real(solver_address)) .with_pre_interactions(pre_interactions) .with_main_interactions(main_interactions) - .with_post_interactions(post_interactions) - .add_extra_trades(jit_trades); + .with_post_interactions(post_interactions); for req in override_requests { builder = builder.with_override(req); @@ -754,53 +781,6 @@ pub struct PriceQuery { pub in_amount: NonZeroU256, } -pub fn encode_jit_orders( - jit_orders: &[dto::JitOrder], - tokens: &[Address], - domain_separator: &DomainSeparator, -) -> Result> { - jit_orders - .iter() - .map(|jit_order| { - let order_data = OrderData { - sell_token: jit_order.sell_token, - buy_token: jit_order.buy_token, - receiver: Some(jit_order.receiver), - sell_amount: jit_order.sell_amount, - buy_amount: jit_order.buy_amount, - valid_to: jit_order.valid_to, - app_data: jit_order.app_data, - fee_amount: U256::ZERO, - kind: match &jit_order.side { - Side::Buy => OrderKind::Buy, - Side::Sell => OrderKind::Sell, - }, - partially_fillable: jit_order.partially_fillable, - sell_token_balance: jit_order.sell_token_source, - buy_token_balance: jit_order.buy_token_destination, - }; - let (owner, signature) = - recover_jit_order_owner(jit_order, &order_data, domain_separator)?; - - Ok(encode_trade( - &order_data, - &signature, - owner, - // the tokens set length is small so the linear search is acceptable - tokens - .iter() - .position(|token| *token == jit_order.sell_token) - .context("missing jit order sell token index")?, - tokens - .iter() - .position(|token| *token == jit_order.buy_token) - .context("missing jit order buy token index")?, - jit_order.executed_amount, - )) - }) - .collect::>>() -} - /// Recovers the owner and signature from a `JitOrder`. fn recover_jit_order_owner( jit_order: &dto::JitOrder, diff --git a/crates/simulator/src/simulation_builder.rs b/crates/simulator/src/simulation_builder.rs index 5eef88a7ec..f94f481e16 100644 --- a/crates/simulator/src/simulation_builder.rs +++ b/crates/simulator/src/simulation_builder.rs @@ -1,8 +1,5 @@ use { - crate::{ - encoding::{EncodedSettlement, EncodedTrade, WrapperCall}, - tenderly::dto::StateObject, - }, + crate::{encoding::WrapperCall, tenderly::dto::StateObject}, alloy_primitives::{Address, B256, Bytes, TxKind, U256}, alloy_provider::{DynProvider, Provider}, alloy_rpc_types::{ @@ -91,7 +88,7 @@ impl SettlementSimulator { pub fn new_simulation_builder(&self) -> SimulationBuilder { SimulationBuilder { simulator: self.clone(), - order: None, + orders: vec![], pre_interactions: vec![], main_interactions: vec![], post_interactions: vec![], @@ -99,7 +96,6 @@ impl SettlementSimulator { solver: None, auction_id: None, account_override_requests: vec![], - extra_trades: vec![], block: Block::Latest, } } @@ -121,7 +117,7 @@ pub enum Block { /// /// Call [`SimulationBuilder::build`] when done to produce a [`SettlementCall`]. pub struct SimulationBuilder { - pub(crate) order: Option, + pub(crate) orders: Vec, pub(crate) pre_interactions: Vec, pub(crate) main_interactions: Vec, pub(crate) post_interactions: Vec, @@ -130,15 +126,12 @@ pub struct SimulationBuilder { pub(crate) auction_id: Option, pub(crate) simulator: SettlementSimulator, pub(crate) account_override_requests: Vec, - pub(crate) extra_trades: Vec, pub(crate) block: Block, } impl SimulationBuilder { - // TODO: support multiple orders to support use case of encoding solutions - // in the driver and the trade verification (requires JIT orders) - pub fn add_order(mut self, order: Order) -> Self { - self.order = Some(order); + pub fn add_orders(mut self, orders: impl IntoIterator) -> Self { + self.orders.extend(orders); self } @@ -243,13 +236,6 @@ impl SimulationBuilder { self } - /// Appends pre-encoded trades (e.g. JIT orders) to the settlement. - /// These are appended after the primary order's trade entry. - pub fn add_extra_trades(mut self, trades: Vec) -> Self { - self.extra_trades.extend(trades); - self - } - /// Finishes the simulation struct based on the configuration thus far. pub async fn build(self) -> Result { // Forward to a helper function to split the boring repetitive builder diff --git a/crates/simulator/src/simulation_encoding.rs b/crates/simulator/src/simulation_encoding.rs index 24b53442b4..f6cee66c51 100644 --- a/crates/simulator/src/simulation_encoding.rs +++ b/crates/simulator/src/simulation_encoding.rs @@ -28,26 +28,38 @@ use { }, alloy_sol_types::SolCall, balance_overrides::{BalanceOverrideRequest, BalanceOverriding}, - model::order::OrderKind, + model::{interaction::InteractionData, order::OrderKind}, }; pub(crate) async fn encode(mut builder: SimulationBuilder) -> Result { - let order = builder.order.as_ref().ok_or(BuildError::NoOrder)?; + if builder.orders.is_empty() { + return Err(BuildError::NoOrder); + } let block = match builder.block { Block::Latest => builder.simulator.0.current_block.borrow().number, Block::Number(n) => n, }; - let executed_amount = executed_amount(&builder, order, block).await?; + let executed_amounts = futures::future::try_join_all( + builder + .orders + .iter() + .map(|o| executed_amount(&builder, o, block)), + ) + .await?; + // The first order determines the settlement-level token list and clearing + // prices. Subsequent orders (e.g. JIT orders) must have their tokens + // present in that list. // At limit price: price[sell_token] = buy_amount, price[buy_token] = // sell_amount. This makes sell_amount * price[sell] / price[buy] = // buy_amount exactly. - let (tokens, clearing_prices) = match &order.price_encoding { + let first = &builder.orders[0]; + let (tokens, clearing_prices) = match &first.price_encoding { PriceEncoding::LimitPrice => ( - vec![order.data.sell_token, order.data.buy_token], - vec![order.data.buy_amount, order.data.sell_amount], + vec![first.data.sell_token, first.data.buy_token], + vec![first.data.buy_amount, first.data.sell_amount], ), PriceEncoding::Custom { tokens, @@ -55,68 +67,77 @@ pub(crate) async fn encode(mut builder: SimulationBuilder) -> Result (tokens.clone(), clearing_prices.clone()), }; - let sell_token_index = tokens - .iter() - .position(|t| *t == order.data.sell_token) - .ok_or(BuildError::MissingSellToken)?; - let buy_token_index = tokens - .iter() - .position(|t| *t == order.data.buy_token) - .ok_or(BuildError::MissingBuyToken)?; + // Replace BuyTokensForBuffers placeholders using the first order's data. + // Must happen before clearing_prices is moved into EncodedSettlement. + { + let first_exec = executed_amounts[0]; + let sell_idx = tokens + .iter() + .position(|t| *t == first.data.sell_token) + .ok_or(BuildError::MissingSellToken)?; + let buy_idx = tokens + .iter() + .position(|t| *t == first.data.buy_token) + .ok_or(BuildError::MissingBuyToken)?; + for request in &mut builder.account_override_requests { + if matches!(request, AccountOverrideRequest::BuyTokensForBuffers) { + let amount = match first.data.kind { + OrderKind::Sell => clearing_prices[sell_idx] + .saturating_mul(first_exec) + .checked_div(clearing_prices[buy_idx]) + .unwrap_or(U256::MAX), + OrderKind::Buy => first_exec, + } + // give 1 wei extra to avoid issues with rounding divisions + .saturating_add(U256::ONE); - // Replace BuyTokensForBuffers placeholders with concrete Balance requests - // now that the required amounts are known. Must happen before clearing_prices - // is moved into EncodedSettlement. - for request in &mut builder.account_override_requests { - if matches!(request, AccountOverrideRequest::BuyTokensForBuffers) { - let amount = match order.data.kind { - OrderKind::Sell => clearing_prices[sell_token_index] - .saturating_mul(executed_amount) - .checked_div(clearing_prices[buy_token_index]) - .unwrap_or(U256::MAX), - OrderKind::Buy => executed_amount, + *request = AccountOverrideRequest::Balance { + holder: *builder.simulator.0.settlement.address(), + token: first.data.buy_token, + amount, + }; } - // give 1 wei extra to avoid issues with rounding divisions - .saturating_add(U256::ONE); - - *request = AccountOverrideRequest::Balance { - holder: *builder.simulator.0.settlement.address(), - token: order.data.buy_token, - amount, - }; } } - let trade = encode_trade( - &order.data, - &order.signature, - order.owner, - sell_token_index, - buy_token_index, - executed_amount, - ); - - let order_pre = &order.pre_interactions; - let order_post = &order.post_interactions; - - let mut trades = vec![trade]; - trades.extend(builder.extra_trades); + // Encode every order as a trade, then collect all their interactions. + let mut trades = Vec::with_capacity(builder.orders.len()); + let mut all_order_pre: Vec = vec![]; + let mut all_order_post: Vec = vec![]; + for (order, exec) in builder.orders.iter().zip(&executed_amounts) { + let sell_idx = tokens + .iter() + .position(|t| *t == order.data.sell_token) + .ok_or(BuildError::MissingSellToken)?; + let buy_idx = tokens + .iter() + .position(|t| *t == order.data.buy_token) + .ok_or(BuildError::MissingBuyToken)?; + trades.push(encode_trade( + &order.data, + &order.signature, + order.owner, + sell_idx, + buy_idx, + *exec, + )); + all_order_pre.extend_from_slice(&order.pre_interactions); + all_order_post.extend_from_slice(&order.post_interactions); + } - let mut settlement = EncodedSettlement { + let settlement = EncodedSettlement { tokens, clearing_prices, trades, interactions: Interactions { - // order's pre-hooks run before any additional pre-interactions - pre: encode_interactions(order_pre.iter().chain(&builder.pre_interactions)), + // order pre-hooks run before any additional pre-interactions + pre: encode_interactions(all_order_pre.iter().chain(&builder.pre_interactions)), main: encode_interactions(&builder.main_interactions), - // additional post-interactions run before the order's post-hooks - post: encode_interactions(builder.post_interactions.iter().chain(order_post)), + // additional post-interactions run before order post-hooks + post: encode_interactions(builder.post_interactions.iter().chain(&all_order_post)), }, }; - customize(&mut settlement); - let settle_calldata = { let mut bytes = settlement.into_settle_call().to_vec(); if let Some(id) = builder.auction_id { From 4dd3628ba203e33f2dc003498145f587ea40fc8b Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Thu, 30 Apr 2026 15:31:03 +0000 Subject: [PATCH 38/44] nicer API --- crates/orderbook/src/orderbook.rs | 4 ++-- .../src/trade_verifier/mod.rs | 15 +++++--------- crates/simulator/src/simulation_builder.rs | 20 ++++++++++--------- crates/simulator/src/simulation_encoding.rs | 2 +- 4 files changed, 19 insertions(+), 22 deletions(-) diff --git a/crates/orderbook/src/orderbook.rs b/crates/orderbook/src/orderbook.rs index ed29cdb970..16e6a1802a 100644 --- a/crates/orderbook/src/orderbook.rs +++ b/crates/orderbook/src/orderbook.rs @@ -652,7 +652,7 @@ impl Orderbook { .map(simulation_builder::Block::Number) .unwrap_or(simulation_builder::Block::Latest), ) - .with_override(simulation_builder::AccountOverrideRequest::BuyTokensForBuffers) + .with_overrides([simulation_builder::AccountOverrideRequest::BuyTokensForBuffers]) .from_solver(simulation_builder::Solver::Fake(None)) .build() .await @@ -710,7 +710,7 @@ impl Orderbook { .map(simulation_builder::Block::Number) .unwrap_or(simulation_builder::Block::Latest), ) - .with_override(simulation_builder::AccountOverrideRequest::BuyTokensForBuffers) + .with_overrides([simulation_builder::AccountOverrideRequest::BuyTokensForBuffers]) .from_solver(simulation_builder::Solver::Fake(None)) .build() .await diff --git a/crates/price-estimation/src/trade_verifier/mod.rs b/crates/price-estimation/src/trade_verifier/mod.rs index b6a14e9e21..82e7632e7d 100644 --- a/crates/price-estimation/src/trade_verifier/mod.rs +++ b/crates/price-estimation/src/trade_verifier/mod.rs @@ -40,7 +40,7 @@ use { ExecutionAmount, PriceEncoding, SettlementSimulator, - Solver as SimSolver, + Solver as SimulationSolver, }, tenderly, }, @@ -283,20 +283,15 @@ impl TradeVerifier { _ => vec![], }; - let mut builder = self + let eth_call_inputs = self .simulator .new_simulation_builder() .add_orders(std::iter::once(fake_order).chain(jit_orders)) - .from_solver(SimSolver::Real(solver_address)) + .from_solver(SimulationSolver::OriginUnaltered(solver_address)) .with_pre_interactions(pre_interactions) .with_main_interactions(main_interactions) - .with_post_interactions(post_interactions); - - for req in override_requests { - builder = builder.with_override(req); - } - - let eth_call_inputs = builder + .with_post_interactions(post_interactions) + .with_overrides(override_requests) .build() .await .map_err(|e| Error::SimulationFailed(anyhow::anyhow!("{e}")))?; diff --git a/crates/simulator/src/simulation_builder.rs b/crates/simulator/src/simulation_builder.rs index f94f481e16..475f74c9b7 100644 --- a/crates/simulator/src/simulation_builder.rs +++ b/crates/simulator/src/simulation_builder.rs @@ -227,12 +227,13 @@ impl SimulationBuilder { Ok(self) } - /// Queues an [`AccountOverrideRequest`] to be resolved and applied during - /// [`build`](Self::build). Multiple requests may target the same address; - /// non-conflicting fields are merged and conflicts produce - /// [`BuildError::ConflictingStateOverrides`]. - pub fn with_override(mut self, request: AccountOverrideRequest) -> Self { - self.account_override_requests.push(request); + /// Queues [`AccountOverrideRequest`]s to be resolved and applied during + /// [`build`](Self::build). Multiple requests may target the same address. + pub fn with_overrides( + mut self, + requests: impl IntoIterator, + ) -> Self { + self.account_override_requests.extend(requests); self } @@ -248,9 +249,10 @@ pub enum Solver { /// Simulation assumes this is an actual solver so no state overrides will /// be applied to allow list it explicitly. /// If you need a very specific solver setup for your simulation consider - /// using this and explicitly add the necessary state overrides yourself - /// with `Simulation::build_with_modifications()`. - Real(Address), + /// using this and explicitly adding the necessary + /// [`AccountOverrideRequest`]s using with + /// [`SimulationBuilder::with_overrides()`]. + OriginUnaltered(Address), /// A fake solver for simulation. Uses the provided address or generates a /// random one. The simulation builder will automatically set the required /// state overrides to give it enough ETH and allow list it as a solver. diff --git a/crates/simulator/src/simulation_encoding.rs b/crates/simulator/src/simulation_encoding.rs index f6cee66c51..1eb78cd76d 100644 --- a/crates/simulator/src/simulation_encoding.rs +++ b/crates/simulator/src/simulation_encoding.rs @@ -172,7 +172,7 @@ pub(crate) async fn encode(mut builder: SimulationBuilder) -> Result addr, + Some(Solver::OriginUnaltered(addr)) => addr, Some(Solver::Fake(opt)) => { let addr = opt.unwrap_or_else(Address::random); builder From a0c3bccd108d79f40fffd5b02f13e6164ecd35f1 Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Thu, 30 Apr 2026 15:34:23 +0000 Subject: [PATCH 39/44] nits --- crates/simulator/src/simulation_builder.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/simulator/src/simulation_builder.rs b/crates/simulator/src/simulation_builder.rs index 475f74c9b7..a568acfe56 100644 --- a/crates/simulator/src/simulation_builder.rs +++ b/crates/simulator/src/simulation_builder.rs @@ -423,7 +423,6 @@ impl EthCallInputs { block_number: Some(self.block + 1), network_id: self.simulator.0.chain_id.to_string(), from: self.request.from.unwrap_or_default(), - // TODO: error handling to: match &self.request.to.ok_or(ConversionError::MissingTo)? { TxKind::Create => Default::default(), TxKind::Call(to) => *to, @@ -436,7 +435,6 @@ impl EthCallInputs { .map(|bytes| bytes.to_vec()) .unwrap_or_default(), gas: self.request.gas, - gas_price: None, // use tenderly default for now value: self.request.value, simulation_type: Some(crate::tenderly::dto::SimulationType::Full), state_objects: Some( @@ -453,8 +451,10 @@ impl EthCallInputs { ), access_list: self.request.access_list.as_ref().map(Into::into), save: Some(true), + gas_price: None, // use tenderly default for now save_if_fails: Some(true), - ..Default::default() + transaction_index: None, + generate_access_list: None, }) } } From 1de44d917e5810b5f57efb1a085df4cf37efd45c Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Thu, 30 Apr 2026 15:43:48 +0000 Subject: [PATCH 40/44] api improvements --- crates/simulator/src/simulation_builder.rs | 35 +++++++++++++++------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/crates/simulator/src/simulation_builder.rs b/crates/simulator/src/simulation_builder.rs index a568acfe56..694f61caf6 100644 --- a/crates/simulator/src/simulation_builder.rs +++ b/crates/simulator/src/simulation_builder.rs @@ -135,18 +135,27 @@ impl SimulationBuilder { self } - pub fn with_pre_interactions(mut self, interactions: Vec) -> Self { - self.pre_interactions = interactions; + pub fn with_pre_interactions( + mut self, + interactions: impl IntoIterator, + ) -> Self { + self.pre_interactions = interactions.into_iter().collect(); self } - pub fn with_main_interactions(mut self, interactions: Vec) -> Self { - self.main_interactions = interactions; + pub fn with_main_interactions( + mut self, + interactions: impl IntoIterator, + ) -> Self { + self.main_interactions = interactions.into_iter().collect(); self } - pub fn with_post_interactions(mut self, interactions: Vec) -> Self { - self.post_interactions = interactions; + pub fn with_post_interactions( + mut self, + interactions: impl IntoIterator, + ) -> Self { + self.post_interactions = interactions.into_iter().collect(); self } @@ -338,13 +347,19 @@ impl Order { self } - pub fn with_pre_interactions(mut self, interactions: Vec) -> Self { - self.pre_interactions = interactions; + pub fn with_pre_interactions( + mut self, + interactions: impl IntoIterator, + ) -> Self { + self.pre_interactions = interactions.into_iter().collect(); self } - pub fn with_post_interactions(mut self, interactions: Vec) -> Self { - self.post_interactions = interactions; + pub fn with_post_interactions( + mut self, + interactions: impl IntoIterator, + ) -> Self { + self.post_interactions = interactions.into_iter().collect(); self } From 2afaf88a85d06449086b6d06ed2972739e8c5ac9 Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Thu, 30 Apr 2026 16:07:28 +0000 Subject: [PATCH 41/44] 2 price vector entries per order --- .../src/trade_verifier/mod.rs | 41 ++++--- crates/simulator/src/simulation_builder.rs | 18 ++- crates/simulator/src/simulation_encoding.rs | 110 +++++++++--------- 3 files changed, 85 insertions(+), 84 deletions(-) diff --git a/crates/price-estimation/src/trade_verifier/mod.rs b/crates/price-estimation/src/trade_verifier/mod.rs index 82e7632e7d..121893fd78 100644 --- a/crates/price-estimation/src/trade_verifier/mod.rs +++ b/crates/price-estimation/src/trade_verifier/mod.rs @@ -136,20 +136,29 @@ impl TradeVerifier { // the simulation to pass. Otherwise just use the solver address. let solver_address = trade.tx_origin().unwrap_or(trade.solver()); - let (tokens, clearing_prices) = match trade { - TradeKind::Legacy(_) => { - let tokens = vec![query.sell_token, query.buy_token]; - let prices = match query.kind { - OrderKind::Sell => { - vec![*out_amount, query.in_amount.get()] - } - OrderKind::Buy => { - vec![query.in_amount.get(), *out_amount] - } - }; - (tokens, prices) - } - TradeKind::Regular(trade) => trade.clearing_prices.iter().unzip(), + // `tokens` is passed to `Solver::swap` so it can measure balance changes; + // it is independent of the settlement's token/price vectors. + let tokens: Vec
= match trade { + TradeKind::Legacy(_) => vec![query.sell_token, query.buy_token], + TradeKind::Regular(trade) => trade.clearing_prices.keys().copied().collect(), + }; + let (fake_sell_price, fake_buy_price) = match trade { + TradeKind::Legacy(_) => match query.kind { + OrderKind::Sell => (*out_amount, query.in_amount.get()), + OrderKind::Buy => (query.in_amount.get(), *out_amount), + }, + TradeKind::Regular(trade) => ( + trade + .clearing_prices + .get(&query.sell_token) + .copied() + .unwrap_or(U256::ONE), + trade + .clearing_prices + .get(&query.buy_token) + .copied() + .unwrap_or(U256::ONE), + ), }; let (sell_amount, buy_amount) = match query.kind { @@ -240,8 +249,8 @@ impl TradeVerifier { .fill_at( ExecutionAmount::Full, PriceEncoding::Custom { - tokens: tokens.clone(), - clearing_prices, + sell_price: fake_sell_price, + buy_price: fake_buy_price, }, ); diff --git a/crates/simulator/src/simulation_builder.rs b/crates/simulator/src/simulation_builder.rs index 694f61caf6..fa2fb5c032 100644 --- a/crates/simulator/src/simulation_builder.rs +++ b/crates/simulator/src/simulation_builder.rs @@ -237,7 +237,10 @@ impl SimulationBuilder { } /// Queues [`AccountOverrideRequest`]s to be resolved and applied during - /// [`build`](Self::build). Multiple requests may target the same address. + /// [`build`](Self::build). Multiple requests may target the same address + /// and will be applied on a best-effort basis (failure to compute balance + /// overrides or conflicting state overrides will get logged but do not + /// lead to an error). pub fn with_overrides( mut self, requests: impl IntoIterator, @@ -288,14 +291,11 @@ pub enum PriceEncoding { /// Sets `price[sell_token] = buy_amount` and `price[buy_token] = /// sell_amount`, exactly satisfying the order's limit with no surplus. LimitPrice, - /// Explicit token list and matching clearing prices. Use this when the - /// clearing prices differ from the order's limit — e.g. in trade + /// Explicit clearing prices for the order's sell and buy token. Use this + /// when the prices differ from the order's limit — e.g. in trade /// verification where the order amounts are set to always pass the limit /// check and the solver's quoted prices are supplied separately. - Custom { - tokens: Vec
, - clearing_prices: Vec, - }, + Custom { sell_price: U256, buy_price: U256 }, } /// A simulator-specific order that bundles the data needed to encode a trade. @@ -488,10 +488,6 @@ pub enum BuildError { NoOrder, #[error("no solver was set")] NoSolver, - #[error("sell token not found in token list")] - MissingSellToken, - #[error("buy token not found in token list")] - MissingBuyToken, #[error("failed to query filled amount from settlement contract: {0}")] FilledAmountQuery(#[source] anyhow::Error), #[error("failed to parse app data: {0}")] diff --git a/crates/simulator/src/simulation_encoding.rs b/crates/simulator/src/simulation_encoding.rs index 1eb78cd76d..8ca6b67b7a 100644 --- a/crates/simulator/src/simulation_encoding.rs +++ b/crates/simulator/src/simulation_encoding.rs @@ -49,76 +49,72 @@ pub(crate) async fn encode(mut builder: SimulationBuilder) -> Result ( - vec![first.data.sell_token, first.data.buy_token], - vec![first.data.buy_amount, first.data.sell_amount], - ), - PriceEncoding::Custom { - tokens, - clearing_prices, - } => (tokens.clone(), clearing_prices.clone()), - }; + // Each order occupies exactly 2 consecutive slots in the token/price + // vectors: [2*i] = sell_token, [2*i+1] = buy_token. + // This lets every order be encoded independently without requiring a shared + // global token list. + let n = builder.orders.len(); + let mut tokens = Vec::with_capacity(n * 2); + let mut clearing_prices = Vec::with_capacity(n * 2); + for order in &builder.orders { + let (sell_price, buy_price) = match &order.price_encoding { + PriceEncoding::LimitPrice => (order.data.buy_amount, order.data.sell_amount), + PriceEncoding::Custom { + sell_price, + buy_price, + } => (*sell_price, *buy_price), + }; + tokens.push(order.data.sell_token); + tokens.push(order.data.buy_token); + clearing_prices.push(sell_price); + clearing_prices.push(buy_price); + } - // Replace BuyTokensForBuffers placeholders using the first order's data. - // Must happen before clearing_prices is moved into EncodedSettlement. + // Expand any BuyTokensForBuffers request into one Balance override per + // order, then remove all BuyTokensForBuffers entries so duplicates are + // impossible. + if builder + .account_override_requests + .iter() + .any(|r| matches!(r, AccountOverrideRequest::BuyTokensForBuffers)) { - let first_exec = executed_amounts[0]; - let sell_idx = tokens - .iter() - .position(|t| *t == first.data.sell_token) - .ok_or(BuildError::MissingSellToken)?; - let buy_idx = tokens - .iter() - .position(|t| *t == first.data.buy_token) - .ok_or(BuildError::MissingBuyToken)?; - for request in &mut builder.account_override_requests { - if matches!(request, AccountOverrideRequest::BuyTokensForBuffers) { - let amount = match first.data.kind { - OrderKind::Sell => clearing_prices[sell_idx] - .saturating_mul(first_exec) - .checked_div(clearing_prices[buy_idx]) - .unwrap_or(U256::MAX), - OrderKind::Buy => first_exec, - } - // give 1 wei extra to avoid issues with rounding divisions - .saturating_add(U256::ONE); - - *request = AccountOverrideRequest::Balance { - holder: *builder.simulator.0.settlement.address(), - token: first.data.buy_token, - amount, - }; + builder + .account_override_requests + .retain(|r| !matches!(r, AccountOverrideRequest::BuyTokensForBuffers)); + let settlement = *builder.simulator.0.settlement.address(); + for (i, (order, &exec)) in builder.orders.iter().zip(&executed_amounts).enumerate() { + let sell_price = clearing_prices[2 * i]; + let buy_price = clearing_prices[2 * i + 1]; + let amount = match order.data.kind { + OrderKind::Sell => sell_price + .saturating_mul(exec) + .checked_div(buy_price) + .unwrap_or(U256::MAX), + OrderKind::Buy => exec, } + // give 1 wei extra to avoid issues with rounding divisions + .saturating_add(U256::ONE); + builder + .account_override_requests + .push(AccountOverrideRequest::Balance { + holder: settlement, + token: order.data.buy_token, + amount, + }); } } // Encode every order as a trade, then collect all their interactions. - let mut trades = Vec::with_capacity(builder.orders.len()); + let mut trades = Vec::with_capacity(n); let mut all_order_pre: Vec = vec![]; let mut all_order_post: Vec = vec![]; - for (order, exec) in builder.orders.iter().zip(&executed_amounts) { - let sell_idx = tokens - .iter() - .position(|t| *t == order.data.sell_token) - .ok_or(BuildError::MissingSellToken)?; - let buy_idx = tokens - .iter() - .position(|t| *t == order.data.buy_token) - .ok_or(BuildError::MissingBuyToken)?; + for (i, (order, exec)) in builder.orders.iter().zip(&executed_amounts).enumerate() { trades.push(encode_trade( &order.data, &order.signature, order.owner, - sell_idx, - buy_idx, + 2 * i, + 2 * i + 1, *exec, )); all_order_pre.extend_from_slice(&order.pre_interactions); From 439d719271a8c4209a2fb116b47807b51e582014 Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Thu, 30 Apr 2026 16:22:29 +0000 Subject: [PATCH 42/44] move gas_limit into settlement simulator --- crates/e2e/tests/e2e/quote_verification.rs | 2 +- crates/orderbook/src/run.rs | 5 +++-- crates/price-estimation/src/factory.rs | 2 +- crates/price-estimation/src/trade_verifier/mod.rs | 5 +---- crates/simulator/src/simulation_builder.rs | 7 +++++++ crates/simulator/src/simulation_encoding.rs | 3 ++- 6 files changed, 15 insertions(+), 9 deletions(-) diff --git a/crates/e2e/tests/e2e/quote_verification.rs b/crates/e2e/tests/e2e/quote_verification.rs index 606f141bd9..89eaf9f071 100644 --- a/crates/e2e/tests/e2e/quote_verification.rs +++ b/crates/e2e/tests/e2e/quote_verification.rs @@ -199,6 +199,7 @@ async fn test_bypass_verification_for_rfq_quotes(web3: Web3) { Default::default(), Default::default(), *onchain.contracts().weth.address(), + gas_limit, balance_overrides, block_stream.clone(), None, @@ -208,7 +209,6 @@ async fn test_bypass_verification_for_rfq_quotes(web3: Web3) { let verifier = TradeVerifier::new( simulator, - gas_limit, None, Arc::new(web3.clone()), BigDecimal::zero(), diff --git a/crates/orderbook/src/run.rs b/crates/orderbook/src/run.rs index 08b3c67d55..70c1797bb5 100644 --- a/crates/orderbook/src/run.rs +++ b/crates/orderbook/src/run.rs @@ -410,7 +410,7 @@ pub async fn run(config: Configuration) { ipfs, )); - let order_simulator2 = if let Some(config) = config.order_simulation { + let order_simulator = if let Some(config) = config.order_simulation { let tenderly: Option> = config.tenderly.as_ref().map(|tenderly_config| { Arc::new(simulator::tenderly::TenderlyApi::new( @@ -425,6 +425,7 @@ pub async fn run(config: Configuration) { Default::default(), hooks_trampoline_address, *native_token.address(), + config.gas_limit.saturating_to(), balance_overrider.clone(), current_block_stream.clone(), tenderly, @@ -444,7 +445,7 @@ pub async fn run(config: Configuration) { order_validator.clone(), app_data.clone(), config.active_order_competition_threshold, - order_simulator2, + order_simulator, )); check_database_connection(orderbook.as_ref()).await; diff --git a/crates/price-estimation/src/factory.rs b/crates/price-estimation/src/factory.rs index 4de8a55d71..05c90731e7 100644 --- a/crates/price-estimation/src/factory.rs +++ b/crates/price-estimation/src/factory.rs @@ -113,6 +113,7 @@ impl<'a> PriceEstimatorFactory<'a> { Default::default(), Default::default(), network.native_token, + args.max_gas_per_tx, balance_overrides, network.block_stream.clone(), tenderly.clone(), @@ -121,7 +122,6 @@ impl<'a> PriceEstimatorFactory<'a> { let verifier = TradeVerifier::new( simulator, - args.max_gas_per_tx, tenderly, components.code_fetcher.clone(), args.quote_inaccuracy_limit.clone(), diff --git a/crates/price-estimation/src/trade_verifier/mod.rs b/crates/price-estimation/src/trade_verifier/mod.rs index 121893fd78..7bb29d2409 100644 --- a/crates/price-estimation/src/trade_verifier/mod.rs +++ b/crates/price-estimation/src/trade_verifier/mod.rs @@ -71,7 +71,6 @@ pub trait TradeVerifying: Send + Sync + 'static { pub struct TradeVerifier { tenderly: Option>, simulator: SettlementSimulator, - gas_limit: u64, code_fetcher: Arc, quote_inaccuracy_limit: BigRational, tokens_without_verification: HashSet
, @@ -86,7 +85,6 @@ impl TradeVerifier { #[expect(clippy::too_many_arguments)] pub fn new( simulator: SettlementSimulator, - gas_limit: u64, tenderly: Option>, code_fetcher: Arc, quote_inaccuracy_limit: BigDecimal, @@ -102,7 +100,6 @@ impl TradeVerifier { Self { tenderly, simulator, - gas_limit, code_fetcher, quote_inaccuracy_limit: big_decimal_to_big_rational("e_inaccuracy_limit), tokens_without_verification, @@ -328,7 +325,7 @@ impl TradeVerifier { calldata, ) .from(solver_address) - .gas(self.gas_limit); + .gas(self.simulator.max_gas_limit()); let tx = swap_call.clone().into_transaction_request(); let result = swap_call diff --git a/crates/simulator/src/simulation_builder.rs b/crates/simulator/src/simulation_builder.rs index fa2fb5c032..d2bf6c7fc4 100644 --- a/crates/simulator/src/simulation_builder.rs +++ b/crates/simulator/src/simulation_builder.rs @@ -32,6 +32,7 @@ pub(crate) struct Inner { pub(crate) flash_loan_router: Address, pub(crate) hooks_trampoline: Address, pub(crate) native_token: Address, + pub(crate) max_gas_limit: u64, pub(crate) balance_overrides: Arc, pub(crate) provider: DynProvider, pub(crate) domain_separator: DomainSeparator, @@ -46,6 +47,7 @@ impl SettlementSimulator { flash_loan_router: Address, hooks_trampoline: Address, native_token: Address, + max_gas_limit: u64, balance_overrides: Arc, current_block: CurrentBlockWatcher, tenderly: Option>, @@ -60,6 +62,7 @@ impl SettlementSimulator { flash_loan_router, hooks_trampoline, native_token, + max_gas_limit, balance_overrides, provider, domain_separator, @@ -73,6 +76,10 @@ impl SettlementSimulator { self.0.native_token } + pub fn max_gas_limit(&self) -> u64 { + self.0.max_gas_limit + } + pub fn provider(&self) -> DynProvider { self.0.provider.clone() } diff --git a/crates/simulator/src/simulation_encoding.rs b/crates/simulator/src/simulation_encoding.rs index 8ca6b67b7a..1dc8a04981 100644 --- a/crates/simulator/src/simulation_encoding.rs +++ b/crates/simulator/src/simulation_encoding.rs @@ -193,6 +193,7 @@ pub(crate) async fn encode(mut builder: SimulationBuilder) -> Result Date: Thu, 30 Apr 2026 16:37:41 +0000 Subject: [PATCH 43/44] sort dependencies --- crates/simulator/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/simulator/Cargo.toml b/crates/simulator/Cargo.toml index 1cb1960dfe..5c41b774e0 100644 --- a/crates/simulator/Cargo.toml +++ b/crates/simulator/Cargo.toml @@ -16,7 +16,6 @@ alloy-sol-types = { workspace = true } alloy-transport = { workspace = true } anyhow = { workspace = true } app-data = { workspace = true } -futures = { workspace = true } async-trait = { workspace = true } balance-overrides = { workspace = true } cached = { workspace = true } @@ -27,6 +26,7 @@ contracts = { workspace = true } derive_more = { workspace = true } eth-domain-types = { workspace = true } ethrpc = { workspace = true } +futures = { workspace = true } gas-price-estimation = { workspace = true } hex-literal = { workspace = true } http-client = { workspace = true } From 543d466fa8b2b772736031bce7eb3103b4d67b2e Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Thu, 30 Apr 2026 16:41:26 +0000 Subject: [PATCH 44/44] fix lints --- crates/price-estimation/src/trade_verifier/mod.rs | 1 - crates/simulator/src/simulation_builder.rs | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/price-estimation/src/trade_verifier/mod.rs b/crates/price-estimation/src/trade_verifier/mod.rs index 7bb29d2409..ea4274328e 100644 --- a/crates/price-estimation/src/trade_verifier/mod.rs +++ b/crates/price-estimation/src/trade_verifier/mod.rs @@ -82,7 +82,6 @@ impl TradeVerifier { const SPARDOSE: Address = address!("0000000000000000000000000000000000020000"); const TRADER_IMPL: Address = address!("0000000000000000000000000000000000010000"); - #[expect(clippy::too_many_arguments)] pub fn new( simulator: SettlementSimulator, tenderly: Option>, diff --git a/crates/simulator/src/simulation_builder.rs b/crates/simulator/src/simulation_builder.rs index d2bf6c7fc4..38ad3fef16 100644 --- a/crates/simulator/src/simulation_builder.rs +++ b/crates/simulator/src/simulation_builder.rs @@ -42,6 +42,7 @@ pub(crate) struct Inner { } impl SettlementSimulator { + #[expect(clippy::too_many_arguments)] pub async fn new( settlement: contracts::GPv2Settlement::Instance, flash_loan_router: Address,