Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
e4c083b
Clean up usage of ethcontract exports
jmg-duarte Dec 15, 2025
1dd3d2a
lints
jmg-duarte Dec 15, 2025
12eab6f
fix driver test issue
jmg-duarte Dec 15, 2025
693126a
comment
jmg-duarte Dec 15, 2025
736b9e6
Migrate RPC calls into alloy
jmg-duarte Dec 15, 2025
a23adf2
fix compilation
jmg-duarte Dec 15, 2025
e690706
fix lint
jmg-duarte Dec 16, 2025
d34d600
Migrate the AccessList to alloy
jmg-duarte Dec 16, 2025
187b2d0
Merge branch 'main' into jmgd/alloy/rpc
jmg-duarte Dec 16, 2025
10c9735
wip
jmg-duarte Dec 17, 2025
6fb3f16
Merge branch 'main' into jmgd/alloy/rpc
jmg-duarte Dec 17, 2025
525129e
fix wrong migrations
jmg-duarte Dec 17, 2025
394c9e7
Merge branch 'jmgd/alloy/rpc' into jmgd/alloy/signer
jmg-duarte Dec 17, 2025
616697c
match signature
jmg-duarte Dec 17, 2025
3e84f43
clippy
jmg-duarte Dec 17, 2025
c147604
Merge branch 'jmgd/alloy/rpc' into jmgd/alloy/signer
jmg-duarte Dec 17, 2025
8a2a26a
Merge branch 'main' into jmgd/alloy/signer
jmg-duarte Dec 17, 2025
a19d78f
Merge branch 'main' into jmgd/alloy/rpc
jmg-duarte Dec 17, 2025
4761f68
Merge branch 'jmgd/alloy/rpc' into jmgd/alloy/signer
jmg-duarte Dec 17, 2025
663b732
Migrate TestAccount to alloy
jmg-duarte Dec 17, 2025
db595fa
migrate solver accounts to alloy
jmg-duarte Dec 18, 2025
fdacd7f
Merge branch 'jmgd/alloy/solver-account' into jmgd/alloy/accesslist
jmg-duarte Dec 18, 2025
2634602
wip
jmg-duarte Dec 18, 2025
106e1f7
Merge branch 'main' into jmgd/alloy/rpc
jmg-duarte Dec 18, 2025
8e3d7fa
Merge branch 'jmgd/alloy/rpc' into jmgd/alloy/signer
jmg-duarte Dec 18, 2025
461519b
Merge branch 'jmgd/alloy/signer' into jmgd/alloy/account
jmg-duarte Dec 18, 2025
0e6210e
migrate solver accounts to alloy
jmg-duarte Dec 18, 2025
0d5c47d
lints
jmg-duarte Dec 19, 2025
2e1ce01
Merge branch 'main' into jmgd/alloy/solver-account
jmg-duarte Dec 19, 2025
ab57d1b
Fix nonce
jmg-duarte Dec 19, 2025
fdb2ce8
address comment
jmg-duarte Dec 19, 2025
4c4319c
Merge branch 'jmgd/alloy/solver-account' into jmgd/alloy/accesslist
jmg-duarte Dec 19, 2025
6b9d8bd
fix bad local merge
jmg-duarte Dec 19, 2025
b5cf6c0
Merge branch 'main' into jmgd/alloy/accesslist
jmg-duarte Dec 19, 2025
b1d3c0c
fix merge
jmg-duarte Dec 19, 2025
a2ae8ee
address comment
jmg-duarte Dec 19, 2025
f7a1e70
address comment
jmg-duarte Dec 22, 2025
384be5e
fix comp
jmg-duarte Dec 22, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/driver/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ name = "driver"
path = "src/main.rs"

[dependencies]
alloy = { workspace = true, features = ["sol-types", "provider-txpool-api", "rand", "provider-anvil-api", "json-rpc"] }
alloy = { workspace = true, features = ["sol-types", "provider-txpool-api", "rand", "provider-anvil-api", "json-rpc", "rpc-types"] }
app-data = { workspace = true }
async-trait = { workspace = true }
axum = { workspace = true }
Expand Down
57 changes: 0 additions & 57 deletions crates/driver/src/domain/eth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,11 @@ use {
crate::util::{Bytes, conv::u256::U256Ext},
alloy::rpc::types::TransactionRequest,
derive_more::{From, Into},
ethrpc::alloy::conversions::{IntoAlloy, IntoLegacy},
itertools::Itertools,
solvers_dto::auction::FlashloanHint,
std::{
collections::{HashMap, HashSet},
ops::{Div, Mul, Sub},
},
web3::types::CallRequest,
};

pub mod allowance;
Expand Down Expand Up @@ -56,43 +53,6 @@ impl AccessList {
}
}

impl From<web3::types::AccessList> for AccessList {
fn from(value: web3::types::AccessList) -> Self {
Self(
value
.into_iter()
.map(|item| {
(
item.address.into_alloy(),
item.storage_keys
.into_iter()
.map(|key| key.into_alloy().into())
.collect(),
)
})
.collect(),
)
}
}

impl From<AccessList> for web3::types::AccessList {
fn from(value: AccessList) -> Self {
value
.0
.into_iter()
.sorted_by_key(|&(address, _)| address)
.map(|(address, storage_keys)| web3::types::AccessListItem {
address: address.into_legacy(),
storage_keys: storage_keys
.into_iter()
.sorted()
.map(|key| key.0.into_legacy())
.collect(),
})
.collect()
}
}

impl IntoIterator for AccessList {
type IntoIter = std::collections::hash_map::IntoIter<Address, HashSet<StorageKey>>;
type Item = (Address, HashSet<StorageKey>);
Expand Down Expand Up @@ -412,23 +372,6 @@ pub struct Tx {
pub access_list: AccessList,
}

impl From<Tx> for CallRequest {
fn from(value: Tx) -> Self {
Self {
from: Some(value.from.into_legacy()),
to: Some(value.to.into_legacy()),
gas: None,
gas_price: None,
value: Some(value.value.0.into_legacy()),
data: Some(value.input.into()),
transaction_type: None,
access_list: Some(value.access_list.into()),
max_fee_per_gas: None,
max_priority_fee_per_gas: None,
}
}
}

impl std::fmt::Debug for Tx {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Tx")
Expand Down
4 changes: 2 additions & 2 deletions crates/driver/src/domain/mempools.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ impl Mempools {
// The tx is simulated before submitting the solution to the competition, but a
// delay between that and the actual execution can cause the simulation to be
// invalid which doesn't make sense to submit to the mempool anymore.
if let Err(err) = self.ethereum.estimate_gas(tx).await {
if let Err(err) = self.ethereum.estimate_gas(tx.clone()).await {
if err.is_revert() {
tracing::info!(
?err,
Expand Down Expand Up @@ -215,7 +215,7 @@ impl Mempools {
});
}
// Check if transaction still simulates
if let Err(err) = self.ethereum.estimate_gas(tx).await {
if let Err(err) = self.ethereum.estimate_gas(tx.clone()).await {
if err.is_revert() {
tracing::info!(
settle_tx_hash = ?hash,
Expand Down
119 changes: 61 additions & 58 deletions crates/driver/src/infra/blockchain/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ use {
boundary,
domain::{eth, eth::U256},
},
alloy::{providers::Provider, rpc::types::TransactionReceipt, transports::TransportErrorKind},
chain::Chain,
ethcontract::{BlockNumber, errors::ExecutionError},
ethrpc::{
Web3,
alloy::conversions::{IntoAlloy, IntoLegacy},
block_stream::CurrentBlockWatcher,
alloy::{
network::TransactionBuilder,
providers::Provider,
rpc::types::{TransactionReceipt, TransactionRequest},
transports::TransportErrorKind,
},
anyhow::anyhow,
chain::Chain,
ethcontract::errors::ExecutionError,
ethrpc::{Web3, alloy::conversions::IntoAlloy, block_stream::CurrentBlockWatcher},
shared::{
account_balances::{BalanceSimulator, SimulationError},
price_estimation::trade_verifier::balance_overrides::{
Expand All @@ -22,7 +24,6 @@ use {
thiserror::Error,
tracing::{Level, instrument},
url::Url,
web3::{Transport, types::CallRequest},
};

pub mod contracts;
Expand Down Expand Up @@ -184,54 +185,53 @@ impl Ethereum {
#[instrument(skip_all)]
pub async fn create_access_list<T>(&self, tx: T) -> Result<eth::AccessList, Error>
where
CallRequest: From<T>,
T: Into<TransactionRequest>,
{
let mut tx: CallRequest = tx.into();
tx.gas = Some(self.inner.tx_gas_limit.into_legacy());
tx.gas_price = self
.simulation_gas_price()
.await
.map(IntoLegacy::into_legacy);

let json = self
.web3
.transport()
.execute(
"eth_createAccessList",
vec![serde_json::to_value(&tx).unwrap(), "pending".into()],
)
.await?;
if let Some(err) = json.get("error") {
return Err(Error::AccessList(err.to_owned()));
}
let access_list: web3::types::AccessList =
serde_json::from_value(json.get("accessList").unwrap().to_owned()).unwrap();
Ok(access_list.into())
let tx = tx.into();

let gas_limit = self.inner.tx_gas_limit.try_into().map_err(|err| {
Error::GasPrice(anyhow!("failed to convert gas_limit to u64: {err:?}"))
})?;
let tx = tx.with_gas_limit(gas_limit);
let tx = match self.simulation_gas_price().await {
Some(gas_price) => tx.with_gas_price(gas_price),
_ => tx,
};

let access_list = self.web3.alloy.create_access_list(&tx).pending().await?;

Ok(access_list
.ensure_ok()
.map_err(Error::AccessList)?
.access_list
.into())
}

/// Estimate gas used by a transaction.
pub async fn estimate_gas(&self, tx: &eth::Tx) -> Result<eth::Gas, Error> {
self.web3
.eth()
.estimate_gas(
web3::types::CallRequest {
from: Some(tx.from.into_legacy()),
to: Some(tx.to.into_legacy()),
value: Some(tx.value.0.into_legacy()),
data: Some(tx.input.clone().into()),
access_list: Some(tx.access_list.clone().into()),
gas_price: self
.simulation_gas_price()
.await
.map(IntoLegacy::into_legacy),
..Default::default()
},
Some(BlockNumber::Pending),
)
pub async fn estimate_gas(&self, tx: eth::Tx) -> Result<eth::Gas, Error> {
let tx = TransactionRequest::default()
.from(tx.from)
.to(tx.to)
.value(tx.value.0)
.input(tx.input.0.into())
.access_list(tx.access_list.into());

let tx = match self.simulation_gas_price().await {
Some(gas_price) => tx.with_gas_price(gas_price),
_ => tx,
};

let estimated_gas = self
.web3
.alloy
.estimate_gas(tx)
.pending()
.await
.map(IntoAlloy::into_alloy)
.map(Into::into)
.map_err(Into::into)
.map_err(anyhow::Error::from)
.map_err(Error::GasPrice)?
.into();

Ok(estimated_gas)
}

/// The gas price is determined based on the deadline by which the
Expand Down Expand Up @@ -296,20 +296,23 @@ impl Ethereum {
}

#[instrument(skip(self), ret(level = Level::DEBUG))]
pub(super) async fn simulation_gas_price(&self) -> Option<eth::U256> {
pub(super) async fn simulation_gas_price(&self) -> Option<u128> {
// Some nodes don't pick a reasonable default value when you don't specify a gas
// price and default to 0. Additionally some sneaky tokens have special code
// paths that detect that case to try to behave differently during simulations
// than they normally would. To not rely on the node picking a reasonable
// default value we estimate the current gas price upfront. But because it's
// extremely rare that tokens behave that way we are fine with falling back to
// the node specific fallback value instead of failing the whole call.
self.inner
.gas
.estimate()
.await
let gas_price = self.inner.gas.estimate().await.ok()?.effective().0.0;
u128::try_from(gas_price)
.inspect_err(|err| {
tracing::debug!(
?err,
"failed to convert gas estimate to u128, returning None"
);
})
.ok()
.map(|gas| gas.effective().0.0)
}

pub fn web3(&self) -> &Web3 {
Expand Down Expand Up @@ -341,7 +344,7 @@ pub enum Error {
#[error("gas price estimation error: {0}")]
GasPrice(boundary::Error),
#[error("access list estimation error: {0:?}")]
AccessList(serde_json::Value),
AccessList(String),
}

impl Error {
Expand Down
32 changes: 1 addition & 31 deletions crates/driver/src/infra/simulator/enso/dto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use {
crate::{domain::eth, util::serialize},
ethrpc::alloy::conversions::IntoAlloy,
alloy::rpc::types::AccessList,
serde::{Deserialize, Serialize},
serde_with::serde_as,
};
Expand All @@ -26,36 +26,6 @@ pub struct Request {
pub access_list: Option<AccessList>,
}

#[derive(Debug, Serialize)]
#[serde(transparent)]
pub struct AccessList(Vec<AccessListItem>);

impl From<eth::AccessList> for AccessList {
fn from(value: eth::AccessList) -> Self {
Self(
web3::types::AccessList::from(value)
.into_iter()
.map(|item| AccessListItem {
address: item.address.into_alloy(),
storage_keys: item
.storage_keys
.into_iter()
.map(IntoAlloy::into_alloy)
.collect(),
})
.collect(),
)
}
}

#[serde_as]
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AccessListItem {
pub address: eth::Address,
pub storage_keys: Vec<eth::B256>,
}

#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
Expand Down
2 changes: 1 addition & 1 deletion crates/driver/src/infra/simulator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ impl Simulator {
}
Inner::Ethereum => self
.eth
.estimate_gas(tx)
.estimate_gas(tx.clone())
.await
.map_err(with(tx.clone(), block))?,
Inner::Enso(enso) => enso
Expand Down
33 changes: 10 additions & 23 deletions crates/driver/src/infra/simulator/tenderly/dto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

use {
crate::{domain::eth, util::serialize},
ethrpc::alloy::conversions::{IntoAlloy, IntoLegacy},
itertools::Itertools,
serde::{Deserialize, Serialize},
serde_with::serde_as,
};
Expand Down Expand Up @@ -54,15 +52,11 @@ struct AccessListItem {
impl From<eth::AccessList> for AccessList {
fn from(value: eth::AccessList) -> Self {
Self(
web3::types::AccessList::from(value)
value
.into_iter()
.map(|item| AccessListItem {
address: item.address.into_alloy(),
storage_keys: item
.storage_keys
.into_iter()
.map(IntoAlloy::into_alloy)
.collect(),
.map(|(address, storage_keys)| AccessListItem {
address,
storage_keys: storage_keys.into_iter().map(|k| k.0).collect(),
})
.collect(),
)
Expand All @@ -71,19 +65,12 @@ impl From<eth::AccessList> for AccessList {

impl From<AccessList> for eth::AccessList {
fn from(value: AccessList) -> Self {
value
.0
.into_iter()
.map(|item| web3::types::AccessListItem {
address: item.address.into_legacy(),
storage_keys: item
.storage_keys
.into_iter()
.map(IntoLegacy::into_legacy)
.collect(),
})
.collect_vec()
.into()
Self::from_iter(
value
.0
.into_iter()
.map(|item| (item.address, item.storage_keys)),
)
}
}

Expand Down
2 changes: 1 addition & 1 deletion crates/solvers-dto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ edition = "2024"
license = "MIT OR Apache-2.0"

[dependencies]
alloy = {workspace = true}
alloy = {workspace = true, features = ["rpc"]}
app-data = { workspace = true }
bigdecimal = { workspace = true, features = ["serde"] }
bytes-hex = { workspace = true } # may get marked as unused but it's used with serde
Expand Down
Loading
Loading