Skip to content

Commit 840422b

Browse files
feat: Batch RPC calls for execution simulation
Motivation: doing one simulation at a time is too slow. We want to batch per block to increase performance so that we can do more simulations in the same time. Changes: - Overwrites are now scoped for the entire block - Simulations can be marked as: - Reverted - if the output from the RPC call is an error - Failed - if there was a problem in preparing/processing the simulation data. For example, if the balance/allowance storage slots could not be found. - Success - if everything went perfectly (as it should) Took 6 hours 46 minutes Took 13 seconds
1 parent 1fa18c9 commit 840422b

File tree

10 files changed

+593
-318
lines changed

10 files changed

+593
-318
lines changed

Cargo.lock

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ tycho-ethereum = { version = ">=0.91.0", features = ["onchain_data"] }
4040
tycho-execution = ">=0.129.1"
4141

4242
# EVM dependencies
43-
alloy = { version = "1.0.30", features = ["providers", "signer-local", "rpc-types-eth"] }
43+
alloy = { version = "1.0.30", features = ["providers", "signer-local", "rpc-types-eth", "provider-debug-api"] }
4444

4545
[dependencies]
4646
# Serialization/Deserialization
@@ -112,8 +112,8 @@ approx = "0.5.1"
112112
rstest = "0.23.0"
113113
rstest_reuse = "0.7.0"
114114
tracing-subscriber = { workspace = true, default-features = false, features = [
115-
"env-filter",
116-
"fmt",
115+
"env-filter",
116+
"fmt",
117117
] }
118118
tempfile = "3.13.0"
119119

@@ -132,7 +132,7 @@ tracing-appender = "0.2.3"
132132
default = ["evm", "rfq"]
133133
network_tests = []
134134
evm = [
135-
"dep:foundry-config", "dep:foundry-evm", "dep:revm", "dep:revm-inspectors", "dep:alloy",
135+
"dep:foundry-config", "dep:foundry-evm", "dep:revm", "dep:revm-inspectors", "dep:alloy",
136136
]
137137
rfq = ["dep:reqwest", "dep:async-trait", "dep:tokio-tungstenite", "dep:async-stream", "dep:http", "dep:prost"]
138138

tycho-integration-test/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ version = "0.1.0"
44
edition = "2021"
55

66
[dependencies]
7-
alloy = { workspace = true, features = ["rpc-types-eth", "sol-types"] }
7+
alloy = { workspace = true, features = ["rpc-types-eth", "sol-types", "provider-debug-api"] }
88
alloy-chains = "0.2.6"
99
alloy-rpc-types-trace = "1.0.38"
1010
clap = { version = "4.5.48", features = ["derive"] }

tycho-integration-test/src/execution/encoding.rs

Lines changed: 105 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
use std::{str::FromStr, sync::Arc};
1+
use std::{collections::HashMap, str::FromStr, sync::Arc};
22

33
use alloy::{
44
rpc::types::{state::AccountOverride, Block, TransactionRequest},
55
sol_types::SolValue,
66
};
77
use miette::{miette, IntoDiagnostic, WrapErr};
88
use num_bigint::BigUint;
9-
use tracing::debug;
9+
use tracing::{debug, info};
1010
use tycho_common::{
1111
models::{token::Token, Chain},
1212
simulation::protocol_sim::ProtocolSim,
@@ -25,7 +25,12 @@ use tycho_simulation::{
2525
protocol::models::ProtocolComponent,
2626
};
2727

28-
use crate::{execution::tenderly::OverwriteMetadata, RPCTools};
28+
use crate::{
29+
execution::tenderly::{get_overwites_string, OverwriteMetadata},
30+
RPCTools,
31+
};
32+
33+
const USER_ADDR: &str = "0xf847a638E44186F3287ee9F8cAF73FF4d4B80784";
2934

3035
pub fn encode_swap(
3136
component: &ProtocolComponent,
@@ -72,8 +77,7 @@ fn create_solution(
7277
amount_in: BigUint,
7378
expected_amount_out: BigUint,
7479
) -> miette::Result<Solution> {
75-
let user_address =
76-
Bytes::from_str("0xf847a638E44186F3287ee9F8cAF73FF4d4B80784").into_diagnostic()?;
80+
let user_address = Bytes::from_str(USER_ADDR).into_diagnostic()?;
7781

7882
// Prepare data to encode. First we need to create a swap object
7983
let simple_swap =
@@ -168,121 +172,124 @@ fn encode_input(selector: &str, mut encoded_args: Vec<u8>) -> Vec<u8> {
168172
/// Returns both the overwrites and metadata for human-readable logging.
169173
pub(crate) async fn setup_user_overwrites(
170174
rpc_tools: &RPCTools,
171-
solution: &Solution,
172-
transaction: &Transaction,
175+
user_balances: HashMap<Bytes, BigUint>,
173176
block: &Block,
174-
user_address: Address,
175-
) -> miette::Result<(AddressHashMap<AccountOverride>, OverwriteMetadata)> {
177+
to_address: Bytes,
178+
) -> miette::Result<(AddressHashMap<AccountOverride>, Vec<Bytes>)> {
176179
let mut overwrites = AddressHashMap::default();
177180
let mut metadata = OverwriteMetadata::new();
178-
let token_address = Address::from_slice(&solution.given_token[..20]);
179-
// If given token is ETH, add the given amount + 10 ETH for gas
180-
if solution.given_token == Bytes::zero(20) {
181-
let eth_balance = biguint_to_u256(&solution.given_amount) +
182-
U256::from_str("10000000000000000000").unwrap(); // given_amount + 1 ETH for gas
183-
overwrites.insert(user_address, AccountOverride::default().with_balance(eth_balance));
184-
// if the given token is not ETH, do balance and allowance slots overwrites
185-
} else {
186-
let results = rpc_tools
187-
.evm_balance_slot_detector
188-
.detect_balance_slots(
189-
std::slice::from_ref(&solution.given_token),
190-
(**user_address).into(),
191-
(*block.header.hash).into(),
192-
)
193-
.await;
181+
let mut failed_tokens: Vec<Bytes> = Vec::new();
182+
let user_address = Address::from_str(USER_ADDR).into_diagnostic()?;
183+
let spender_address = Address::from_slice(&to_address[..20]);
184+
for (token_address, balance) in user_balances {
185+
// ETH
186+
if token_address == Bytes::zero(20) {
187+
overwrites.insert(
188+
user_address,
189+
AccountOverride::default().with_balance(biguint_to_u256(&balance)),
190+
);
191+
} else {
192+
let results = rpc_tools
193+
.evm_balance_slot_detector
194+
.detect_balance_slots(
195+
std::slice::from_ref(&token_address),
196+
(**user_address).into(),
197+
(*block.header.hash).into(),
198+
)
199+
.await;
194200

195-
let (balance_storage_addr, balance_slot) =
196-
if let Some(Ok((storage_addr, slot))) = results.get(&solution.given_token.clone()) {
197-
(storage_addr, slot)
198-
} else {
199-
return Err(miette!("Couldn't find balance storage slot for token {token_address}"));
200-
};
201+
let (balance_storage_addr, balance_slot) =
202+
if let Some(Ok((storage_addr, slot))) = results.get(&token_address) {
203+
(storage_addr, slot)
204+
} else {
205+
failed_tokens.push(token_address.clone());
206+
continue;
207+
};
201208

202-
let results = rpc_tools
203-
.evm_allowance_slot_detector
204-
.detect_allowance_slots(
205-
std::slice::from_ref(&solution.given_token),
206-
(**user_address).into(),
207-
transaction.to.clone(), // tycho router
208-
(*block.header.hash).into(),
209-
)
210-
.await;
209+
let results = rpc_tools
210+
.evm_allowance_slot_detector
211+
.detect_allowance_slots(
212+
std::slice::from_ref(&token_address),
213+
(**user_address).into(),
214+
to_address.clone(), // tycho router
215+
(*block.header.hash).into(),
216+
)
217+
.await;
211218

212-
let (allowance_storage_addr, allowance_slot) = if let Some(Ok((storage_addr, slot))) =
213-
results.get(&solution.given_token.clone())
214-
{
215-
(storage_addr, slot)
216-
} else {
217-
return Err(miette!("Couldn't find allowance storage slot for token {token_address}"));
218-
};
219+
let (allowance_storage_addr, allowance_slot) =
220+
if let Some(Ok((storage_addr, slot))) = results.get(&token_address.clone()) {
221+
(storage_addr, slot)
222+
} else {
223+
failed_tokens.push(token_address.clone());
224+
continue;
225+
};
219226

220-
// Use the exact given amount for balance and allowance (no buffer, no max)
221-
let token_balance = biguint_to_u256(&solution.given_amount);
222-
let token_allowance = biguint_to_u256(&solution.given_amount);
227+
// Use the exact given amount for balance and allowance (no buffer, no max)
228+
let token_balance = biguint_to_u256(&balance);
229+
let token_allowance = biguint_to_u256(&balance);
223230

224-
let balance_storage_address = Address::from_slice(&balance_storage_addr[..20]);
225-
let allowance_storage_address = Address::from_slice(&allowance_storage_addr[..20]);
231+
let balance_storage_address = Address::from_slice(&balance_storage_addr[..20]);
232+
let allowance_storage_address = Address::from_slice(&allowance_storage_addr[..20]);
226233

227-
let balance_slot_b256 = alloy::primitives::B256::from_slice(balance_slot);
228-
let allowance_slot_b256 = alloy::primitives::B256::from_slice(allowance_slot);
229-
let spender_address = Address::from_slice(&transaction.to[..20]);
234+
let balance_slot_b256 = alloy::primitives::B256::from_slice(balance_slot);
235+
let allowance_slot_b256 = alloy::primitives::B256::from_slice(allowance_slot);
230236

231-
debug!(
237+
debug!(
232238
"Setting token override for {token_address}: balance={}, allowance={}, balance_storage={}, allowance_storage={}",
233239
token_balance, token_allowance, balance_storage_address, allowance_storage_address
234240
);
235241

236-
// Add metadata for human-readable logging
237-
metadata.add_balance(balance_storage_address, user_address, balance_slot_b256);
238-
metadata.add_allowance(
239-
allowance_storage_address,
240-
user_address,
241-
spender_address,
242-
allowance_slot_b256,
243-
);
242+
// Add metadata for human-readable logging
243+
metadata.add_balance(balance_storage_address, user_address, balance_slot_b256);
244+
metadata.add_allowance(
245+
allowance_storage_address,
246+
user_address,
247+
spender_address,
248+
allowance_slot_b256,
249+
);
244250

245-
// Apply balance and allowance overrides
246-
// If both storage addresses are the same, combine them into one override
247-
if balance_storage_address == allowance_storage_address {
248-
overwrites.insert(
249-
balance_storage_address,
250-
AccountOverride::default().with_state_diff(vec![
251-
(
251+
// Apply balance and allowance overrides
252+
// If both storage addresses are the same, combine them into one override
253+
if balance_storage_address == allowance_storage_address {
254+
overwrites.insert(
255+
balance_storage_address,
256+
AccountOverride::default().with_state_diff(vec![
257+
(
258+
balance_slot_b256,
259+
alloy::primitives::B256::from_slice(&token_balance.to_be_bytes::<32>()),
260+
),
261+
(
262+
allowance_slot_b256,
263+
alloy::primitives::B256::from_slice(
264+
&token_allowance.to_be_bytes::<32>(),
265+
),
266+
),
267+
]),
268+
);
269+
} else {
270+
// Different storage addresses, apply separately
271+
overwrites.insert(
272+
balance_storage_address,
273+
AccountOverride::default().with_state_diff(vec![(
252274
balance_slot_b256,
253275
alloy::primitives::B256::from_slice(&token_balance.to_be_bytes::<32>()),
254-
),
255-
(
276+
)]),
277+
);
278+
overwrites.insert(
279+
allowance_storage_address,
280+
AccountOverride::default().with_state_diff(vec![(
256281
allowance_slot_b256,
257282
alloy::primitives::B256::from_slice(&token_allowance.to_be_bytes::<32>()),
258-
),
259-
]),
260-
);
261-
} else {
262-
// Different storage addresses, apply separately
263-
overwrites.insert(
264-
balance_storage_address,
265-
AccountOverride::default().with_state_diff(vec![(
266-
balance_slot_b256,
267-
alloy::primitives::B256::from_slice(&token_balance.to_be_bytes::<32>()),
268-
)]),
269-
);
270-
overwrites.insert(
271-
allowance_storage_address,
272-
AccountOverride::default().with_state_diff(vec![(
273-
allowance_slot_b256,
274-
alloy::primitives::B256::from_slice(&token_allowance.to_be_bytes::<32>()),
275-
)]),
276-
);
283+
)]),
284+
);
285+
}
277286
}
278-
279-
// Add 10 ETH for gas for non-ETH token swaps
280-
let eth_balance = U256::from_str("10000000000000000000").unwrap(); // 1 ETH for gas
281-
overwrites.insert(user_address, AccountOverride::default().with_balance(eth_balance));
282-
debug!("Setting ETH balance override for user {user_address}: {eth_balance} (for gas)");
283287
}
284288

285-
Ok((overwrites, metadata))
289+
let overwrites_string = get_overwites_string(&overwrites, Some(&metadata));
290+
info!("Block {} overwrites: {}", block.number(), overwrites_string);
291+
292+
Ok((overwrites, failed_tokens))
286293
}
287294

288295
pub(crate) fn swap_request(

0 commit comments

Comments
 (0)