Skip to content

Commit

Permalink
Settlement Simulation Works Without Internalizing Interactions (#843)
Browse files Browse the repository at this point in the history
* Settlement Simulation Works Without Internalizing Interactions
  • Loading branch information
sunce86 authored Dec 5, 2022
1 parent da53a3d commit f41fe81
Show file tree
Hide file tree
Showing 19 changed files with 150 additions and 67 deletions.
7 changes: 6 additions & 1 deletion crates/driver/src/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use {
shared::{
current_block::{into_stream, BlockInfo, CurrentBlockStream},
ethrpc::Web3,
http_solver::model::InternalizationStrategy,
},
solver::{
driver::submit_settlement,
Expand Down Expand Up @@ -117,7 +118,11 @@ impl Driver {
let fake_solver = Arc::new(CommitRevealSolverAdapter::from(self.solver.clone()));
let simulation_details = self
.settlement_rater
.simulate_settlements(vec![(fake_solver, settlement)], gas_price)
.simulate_settlements(
vec![(fake_solver, settlement)],
gas_price,
InternalizationStrategy::SkipInternalizableInteraction,
)
.await?
.pop()
.context("simulation returned no results")?;
Expand Down
13 changes: 13 additions & 0 deletions crates/shared/src/http_solver/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,8 @@ pub struct TransactionWithError {
pub struct SimulatedTransaction {
/// The simulation was done on top of all transactions from the given block number
pub block_number: u64,
/// Is transaction simulated with internalized interactions or without
pub internalization: InternalizationStrategy,
/// Which storage the settlement tries to access. Contains `None` if some error happened while
/// estimating the access list.
pub access_list: Option<AccessList>,
Expand All @@ -422,6 +424,15 @@ pub struct SimulatedTransaction {
pub data: Vec<u8>,
}

/// Whether or not internalizable interactions should be encoded as calldata
#[derive(Debug, Copy, Clone, Serialize)]
pub enum InternalizationStrategy {
#[serde(rename = "Disabled")]
EncodeAllInteractions,
#[serde(rename = "Enabled")]
SkipInternalizableInteraction,
}

#[cfg(test)]
mod tests {
use std::str::FromStr;
Expand Down Expand Up @@ -1007,6 +1018,7 @@ mod tests {
from: H160::from_str("0x9008D19f58AAbD9eD0D60971565AA8510560ab41").unwrap(),
to: H160::from_str("0x9008D19f58AAbD9eD0D60971565AA8510560ab41").unwrap(),
data: vec![19, 250, 73],
internalization: InternalizationStrategy::SkipInternalizableInteraction,
})
.unwrap(),
json!({
Expand All @@ -1020,6 +1032,7 @@ mod tests {
"data": "0x13fa49",
"from": "0x9008d19f58aabd9ed0d60971565aa8510560ab41",
"to": "0x9008d19f58aabd9ed0d60971565aa8510560ab41",
"internalization": "Enabled",
}),
);
}
Expand Down
13 changes: 10 additions & 3 deletions crates/solver/src/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,12 @@ use model::{
use num::{rational::Ratio, BigInt, BigRational, ToPrimitive};
use primitive_types::{H160, U256};
use shared::{
code_fetching::CodeFetching, current_block::CurrentBlockStream, ethrpc::Web3,
http_solver::model::SolverRunError, recent_block_cache::Block, tenderly_api::TenderlyApi,
code_fetching::CodeFetching,
current_block::CurrentBlockStream,
ethrpc::Web3,
http_solver::model::{InternalizationStrategy, SolverRunError},
recent_block_cache::Block,
tenderly_api::TenderlyApi,
};
use std::{
sync::Arc,
Expand Down Expand Up @@ -354,7 +358,10 @@ impl Driver {
})
.collect(),
call_data: settlement_simulation::call_data(
rated_settlement.settlement.clone().into(),
rated_settlement
.settlement
.clone()
.encode(InternalizationStrategy::SkipInternalizableInteraction), // rating is done with internalizations
),
})
.collect(),
Expand Down
6 changes: 5 additions & 1 deletion crates/solver/src/driver_logger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,13 @@ impl DriverLogger {
let simulations = simulate_and_error_with_tenderly_link(
errors.iter().map(|simulation_with_error| {
let simulation = &simulation_with_error.simulation;
let settlement = simulation
.settlement
.clone()
.encode(simulation.transaction.internalization);
(
simulation.solver.account().clone(),
simulation.settlement.clone(),
settlement,
simulation.transaction.access_list.clone(),
)
}),
Expand Down
6 changes: 2 additions & 4 deletions crates/solver/src/liquidity/balancer_v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,17 +187,15 @@ impl SettlementHandler {
#[cfg(test)]
mod tests {
use super::*;
use crate::{
interactions::allowances::{Approval, MockAllowanceManaging},
settlement::InternalizationStrategy,
};
use crate::interactions::allowances::{Approval, MockAllowanceManaging};
use maplit::{hashmap, hashset};
use mockall::predicate::*;
use model::TokenPair;
use num::BigRational;
use primitive_types::H160;
use shared::{
dummy_contract,
http_solver::model::InternalizationStrategy,
interaction::Interaction,
sources::balancer_v2::pool_fetching::{
AmplificationParameter, CommonPoolState, FetchedBalancerPools,
Expand Down
7 changes: 5 additions & 2 deletions crates/solver/src/liquidity/zeroex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,9 +208,12 @@ impl SettlementHandling<LimitOrder> for OrderSettlementHandler {
#[cfg(test)]
pub mod tests {
use super::*;
use crate::{interactions::allowances::Approval, settlement::InternalizationStrategy};
use crate::interactions::allowances::Approval;
use maplit::hashmap;
use shared::{interaction::Interaction, zeroex_api::OrderMetadata};
use shared::{
http_solver::model::InternalizationStrategy, interaction::Interaction,
zeroex_api::OrderMetadata,
};

fn get_relevant_pairs(token_a: H160, token_b: H160) -> HashSet<TokenPair> {
let base_tokens = Arc::new(BaseTokens::new(H160::zero(), &[]));
Expand Down
19 changes: 7 additions & 12 deletions crates/solver/src/settlement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ pub mod external_prices;
mod settlement_encoder;

use self::external_prices::ExternalPrices;
pub use self::settlement_encoder::{
verify_executed_amount, InternalizationStrategy, PricedTrade, SettlementEncoder,
};
pub use self::settlement_encoder::{verify_executed_amount, PricedTrade, SettlementEncoder};
use crate::{
encoding::{self, EncodedSettlement, EncodedTrade},
liquidity::Settleable,
Expand All @@ -14,7 +12,7 @@ use itertools::Itertools;
use model::order::{Order, OrderKind};
use num::{rational::Ratio, BigInt, BigRational, One, Signed, Zero};
use primitive_types::{H160, U256};
use shared::conversions::U256Ext as _;
use shared::{conversions::U256Ext as _, http_solver::model::InternalizationStrategy};
use std::{
collections::{HashMap, HashSet},
ops::{Mul, Sub},
Expand Down Expand Up @@ -433,13 +431,9 @@ impl Settlement {
Revertable::NoRisk
}
}
}

impl From<Settlement> for EncodedSettlement {
fn from(settlement: Settlement) -> Self {
settlement
.encoder
.finish(InternalizationStrategy::SkipInternalizableInteraction)
pub fn encode(self, internalization_strategy: InternalizationStrategy) -> EncodedSettlement {
self.encoder.finish(internalization_strategy)
}
}

Expand Down Expand Up @@ -1439,7 +1433,7 @@ pub mod tests {
let sell_token = H160([1; 20]);
let buy_token = H160([2; 20]);

let settlement = EncodedSettlement::from(test_settlement(
let settlement = test_settlement(
hashmap! {
sell_token => 100_000_u128.into(),
buy_token => 100_000_u128.into(),
Expand All @@ -1466,7 +1460,8 @@ pub mod tests {
executed_amount: 100_000_u128.into(),
scaled_unsubsidized_fee: 1_000_u128.into(),
}],
));
)
.encode(InternalizationStrategy::SkipInternalizableInteraction);

// Note that for limit order **both** the uniform clearing price of the
// buy token as well as the executed clearing price accounting for fees
Expand Down
10 changes: 3 additions & 7 deletions crates/solver/src/settlement/settlement_encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ use model::{
use num::{BigRational, One};
use number_conversions::big_rational_to_u256;
use primitive_types::{H160, U256};
use shared::{conversions::U256Ext, interaction::Interaction};
use shared::{
conversions::U256Ext, http_solver::model::InternalizationStrategy, interaction::Interaction,
};
use std::{
collections::{hash_map::Entry, HashMap, HashSet},
iter,
Expand Down Expand Up @@ -89,12 +91,6 @@ pub struct PricedTrade<'a> {
pub buy_token_price: U256,
}

/// Whether or not internalizable interactions should be encoded as calldata
pub enum InternalizationStrategy {
EncodeAllInteractions,
SkipInternalizableInteraction,
}

impl SettlementEncoder {
/// Creates a new settlement encoder with the specified prices.
///
Expand Down
7 changes: 6 additions & 1 deletion crates/solver/src/settlement_post_processing/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ use gas_estimation::GasPrice1559;
use optimize_buffer_usage::optimize_buffer_usage;
use optimize_unwrapping::optimize_unwrapping;
use primitive_types::H160;
use shared::{ethrpc::Web3, token_list::AutoUpdatingTokenList};
use shared::{
ethrpc::Web3, http_solver::model::InternalizationStrategy, token_list::AutoUpdatingTokenList,
};

/// Determines whether a settlement would be executed successfully.
#[cfg_attr(test, mockall::automock)]
Expand All @@ -24,11 +26,13 @@ pub struct SettlementSimulator {
settlement_contract: GPv2Settlement,
gas_price: GasPrice1559,
solver_account: Account,
internalization: InternalizationStrategy,
}

#[async_trait::async_trait]
impl SettlementSimulating for SettlementSimulator {
async fn settlement_would_succeed(&self, settlement: Settlement) -> bool {
let settlement = settlement.encode(self.internalization);
let result = simulate_and_estimate_gas_at_current_block(
std::iter::once((self.solver_account.clone(), settlement, None)),
&self.settlement_contract,
Expand Down Expand Up @@ -93,6 +97,7 @@ impl PostProcessing for PostProcessingPipeline {
settlement_contract: self.settlement_contract.clone(),
gas_price,
solver_account,
internalization: InternalizationStrategy::SkipInternalizableInteraction,
};

let optimized_solution = optimize_buffer_usage(
Expand Down
10 changes: 4 additions & 6 deletions crates/solver/src/settlement_ranker.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
use crate::{
driver::solver_settlements::{self, retain_mature_settlements, RatedSettlement},
metrics::{SolverMetrics, SolverRunOutcome, SolverSimulationOutcome},
settlement::{
external_prices::ExternalPrices, InternalizationStrategy, PriceCheckTokens, Settlement,
},
settlement::{external_prices::ExternalPrices, PriceCheckTokens, Settlement},
settlement_rater::{RatedSolverSettlement, SettlementRating},
settlement_simulation::call_data,
solver::{SimulationWithError, Solver},
Expand All @@ -15,7 +13,8 @@ use model::auction::AuctionId;
use num::{rational::Ratio, BigInt, BigRational, CheckedDiv, FromPrimitive};
use rand::prelude::SliceRandom;
use shared::http_solver::model::{
AuctionResult, SolverRejectionReason, SolverRunError, TransactionWithError,
AuctionResult, InternalizationStrategy, SolverRejectionReason, SolverRunError,
TransactionWithError,
};
use std::{cmp::Ordering, sync::Arc, time::Duration};

Expand Down Expand Up @@ -145,9 +144,8 @@ impl SettlementRanker {
"0x{}",
hex::encode(call_data(
settlement
.encoder
.clone()
.finish(InternalizationStrategy::EncodeAllInteractions)
.encode(InternalizationStrategy::EncodeAllInteractions)
)),
);

Expand Down
50 changes: 44 additions & 6 deletions crates/solver/src/settlement_rater.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ use gas_estimation::GasPrice1559;
use itertools::{Either, Itertools};
use num::BigRational;
use primitive_types::U256;
use shared::{code_fetching::CodeFetching, ethrpc::Web3, http_solver::model::SimulatedTransaction};
use shared::{
code_fetching::CodeFetching,
ethrpc::Web3,
http_solver::model::{InternalizationStrategy, SimulatedTransaction},
};
use std::{borrow::Borrow, sync::Arc};
use web3::types::AccessList;

Expand Down Expand Up @@ -44,6 +48,7 @@ pub trait SettlementRating: Send + Sync {
&self,
settlements: Vec<SolverSettlement>,
gas_price: GasPrice1559,
internalization: InternalizationStrategy,
) -> Result<Vec<SimulationWithResult>>;
}

Expand All @@ -59,6 +64,7 @@ impl SettlementRater {
&self,
solver_settlements: Vec<(Arc<dyn Solver>, Settlement)>,
gas_price: GasPrice1559,
internalization: InternalizationStrategy,
) -> Vec<SettlementWithSolver> {
join_all(
solver_settlements
Expand All @@ -67,7 +73,7 @@ impl SettlementRater {
let tx = settle_method(
gas_price,
&self.settlement_contract,
settlement.clone(),
settlement.clone().encode(internalization),
solver.account().clone(),
)
.tx;
Expand All @@ -94,14 +100,17 @@ impl SettlementRating for SettlementRater {
&self,
settlements: Vec<(Arc<dyn Solver>, Settlement)>,
gas_price: GasPrice1559,
internalization: InternalizationStrategy,
) -> Result<Vec<SimulationWithResult>> {
let settlements = self.append_access_lists(settlements, gas_price).await;
let settlements = self
.append_access_lists(settlements, gas_price, internalization)
.await;
let block_number = self.web3.eth().block_number().await?.as_u64();
let simulations = simulate_and_estimate_gas_at_current_block(
settlements.iter().map(|(solver, settlement, access_list)| {
(
solver.account().clone(),
settlement.clone(),
settlement.clone().encode(internalization),
access_list.clone(),
)
}),
Expand All @@ -118,11 +127,12 @@ impl SettlementRating for SettlementRater {
|((solver, settlement, access_list), simulation_result)| SimulationWithResult {
simulation: Simulation {
transaction: SimulatedTransaction {
internalization,
access_list,
block_number,
to: self.settlement_contract.address(),
from: solver.account().address(),
data: call_data(settlement.clone().into()),
data: call_data(settlement.clone().encode(internalization)),
},
settlement,
solver,
Expand All @@ -140,7 +150,34 @@ impl SettlementRating for SettlementRater {
prices: &ExternalPrices,
gas_price: GasPrice1559,
) -> Result<(Vec<RatedSolverSettlement>, Vec<SimulationWithError>)> {
let simulations = self.simulate_settlements(settlements, gas_price).await?;
// first simulate settlements without internalizations to make sure they pass
let simulations = self
.simulate_settlements(
settlements,
gas_price,
InternalizationStrategy::EncodeAllInteractions,
)
.await?;

// split simulations into succeeded and failed groups, then do the rating only for succeeded settlements
let (settlements, simulations_failed): (Vec<_>, Vec<_>) = simulations
.into_iter()
.partition_map(|simulation| match simulation.gas_estimate {
Ok(_) => Either::Left((
simulation.simulation.solver,
simulation.simulation.settlement,
)),
Err(_) => Either::Right(simulation),
});

// since rating is done with internalizations, repeat the simulations for previously succeeded simulations
let mut simulations = self
.simulate_settlements(
settlements,
gas_price,
InternalizationStrategy::SkipInternalizableInteraction,
)
.await?;

let gas_price =
BigRational::from_float(gas_price.effective_gas_price()).expect("Invalid gas price.");
Expand All @@ -160,6 +197,7 @@ impl SettlementRating for SettlementRater {
}
};

simulations.extend(simulations_failed);
Ok((simulations.into_iter().enumerate()).partition_map(
|(
i,
Expand Down
Loading

0 comments on commit f41fe81

Please sign in to comment.