Skip to content

Commit 59c3cb1

Browse files
committed
Migrate BalancerV3BatchRouter to alloy (cowprotocol#3775)
Migrates BalancerV3BatchRouter to alloy. Alloy, by default, wasn't able to parse the ABI JSON we currently have, so I had to update it a bit. To see the exact change, see [this](cowprotocol@d4db354#diff-6b43d7a940f1bbb3e887415e980543e0825ea48d1d5919797012f188f71b8551) commit after the formatting one. This would require changes in the gnosis/solvers repo, since the SC is used only there.
1 parent f4b59d5 commit 59c3cb1

File tree

29 files changed

+1185
-162
lines changed

29 files changed

+1185
-162
lines changed

Cargo.lock

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

crates/balancer-solver/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ name = "balancer-solver"
1313
path = "src/main.rs"
1414

1515
[dependencies]
16+
alloy = { workspace = true }
1617
axum = { workspace = true }
1718
bigdecimal = { workspace = true, features = ["serde"] }
1819
chain = { workspace = true }

crates/balancer-solver/src/domain/solver.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use {
1919
infra::metrics,
2020
},
2121
ethereum_types::U256,
22+
ethrpc::alloy::conversions::IntoAlloy,
2223
reqwest::Url,
2324
std::{cmp, collections::HashSet, sync::Arc},
2425
tracing::Instrument,
@@ -147,7 +148,10 @@ impl Solver {
147148
let web3 =
148149
ethrpc::web3(Default::default(), Default::default(), node_url, "verifier");
149150
let vault = contracts::BalancerV2Vault::at(&web3, vault_addr.0);
150-
let batch_router = contracts::BalancerV3BatchRouter::at(&web3, batch_router_addr.0);
151+
let batch_router = contracts::alloy::BalancerV3BatchRouter::Instance::new(
152+
batch_router_addr.0.into_alloy(),
153+
web3.alloy,
154+
);
151155
Some(crate::infra::solution_verifier::SolutionVerifier::new(
152156
vault,
153157
batch_router,

crates/balancer-solver/src/infra/solution_verifier.rs

Lines changed: 42 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
use {
2-
contracts::{BalancerV2Vault, BalancerV3BatchRouter},
3-
ethcontract::{Account, Address, Bytes, H160, U256},
2+
alloy::primitives,
3+
contracts::{
4+
BalancerV2Vault,
5+
alloy::BalancerV3BatchRouter::{
6+
self,
7+
IBatchRouter::{SwapPathExactAmountIn, SwapPathStep},
8+
},
9+
},
10+
ethcontract::{Address, Bytes, H160, U256},
11+
ethrpc::alloy::conversions::{IntoAlloy, IntoLegacy},
412
serde::{Deserialize, Serialize},
513
};
614

@@ -45,11 +53,11 @@ pub enum PoolVersion {
4553
#[derive(Clone)]
4654
pub struct SolutionVerifier {
4755
vault: BalancerV2Vault,
48-
batch_router: BalancerV3BatchRouter,
56+
batch_router: BalancerV3BatchRouter::Instance,
4957
}
5058

5159
impl SolutionVerifier {
52-
pub fn new(vault: BalancerV2Vault, batch_router: BalancerV3BatchRouter) -> Self {
60+
pub fn new(vault: BalancerV2Vault, batch_router: BalancerV3BatchRouter::Instance) -> Self {
5361
Self {
5462
vault,
5563
batch_router,
@@ -312,6 +320,7 @@ impl SolutionVerifier {
312320
}
313321

314322
/// Quote V3 swap via Batch Router.querySwapExactIn
323+
/// This uses a static call (eth_call) to query the expected output amount.
315324
async fn quote_v3_swap(
316325
&self,
317326
pool_address_str: &str,
@@ -322,37 +331,27 @@ impl SolutionVerifier {
322331
// Parse pool address from string
323332
let pool_address: H160 = pool_address_str.parse()?;
324333

325-
// Build SwapPathExactAmountIn
326-
let path = (
327-
input_token, // tokenIn
328-
vec![(
329-
pool_address, // pool
330-
output_token, // tokenOut
331-
false, // isBuffer
332-
)],
333-
input_amount, // exactAmountIn
334-
U256::zero(), // minAmountOut (no minimum for query)
335-
);
334+
// Build SwapPathExactAmountIn using alloy types
335+
let path = SwapPathExactAmountIn {
336+
tokenIn: input_token.into_alloy(),
337+
steps: vec![SwapPathStep {
338+
pool: pool_address.into_alloy(),
339+
tokenOut: output_token.into_alloy(),
340+
isBuffer: false,
341+
}],
342+
exactAmountIn: input_amount.into_alloy(),
343+
minAmountOut: primitives::U256::ZERO,
344+
};
336345

337-
// Call querySwapExactIn
338-
// IMPORTANT: Must set .from() to make this a proper staticcall
339-
let query = self
340-
.batch_router
341-
.methods()
342-
.query_swap_exact_in(
343-
vec![path.clone()],
344-
self.batch_router.address(), // sender (required for pools with hooks)
345-
Bytes(vec![]), // empty userData
346-
)
347-
.from(Account::Local(H160::zero(), None)); // Set from address for the eth_call
346+
// Build the call - .call() automatically makes it a static call (eth_call)
347+
let call_builder = self.batch_router.querySwapExactIn(
348+
vec![path.clone()],
349+
*self.batch_router.address(), // sender (required for pools with hooks)
350+
primitives::Bytes::new(), // empty userData
351+
);
348352

349353
// Capture contract call details for debugging
350-
let calldata = query
351-
.tx
352-
.data
353-
.clone()
354-
.map(|d| format!("0x{}", hex::encode(d.0)))
355-
.unwrap_or_else(|| "0x".to_string());
354+
let calldata = format!("0x{}", hex::encode(call_builder.calldata()));
356355

357356
let decoded_params = serde_json::json!({
358357
"paths": [{
@@ -365,23 +364,24 @@ impl SolutionVerifier {
365364
"exactAmountIn": input_amount.to_string(),
366365
"minAmountOut": "0"
367366
}],
368-
"sender": format!("{:?}", self.batch_router.address()),
369-
"userData": "0x",
370-
"from": "0x0000000000000000000000000000000000000000"
367+
"sender": format!("{:?}", self.batch_router.address().into_legacy()),
368+
"userData": "0x"
371369
});
372370

373371
let call_details = ContractCallDetails {
374-
contract_address: format!("{:?}", self.batch_router.address()),
372+
contract_address: format!("{:?}", self.batch_router.address().into_legacy()),
375373
contract_name: "BalancerV3BatchRouter".to_string(),
376374
function_name: "querySwapExactIn".to_string(),
377375
calldata,
378376
decoded_params,
379377
};
380378

381-
let result = query.call().await;
379+
// Execute the static call
380+
let result = call_builder.call().await;
382381

383382
match result {
384-
Ok((path_amounts_out, _tokens_out, _amounts_out)) => {
383+
Ok(return_data) => {
384+
let path_amounts_out = return_data.pathAmountsOut;
385385
// Get the first path's output amount
386386
if path_amounts_out.is_empty() {
387387
return Err("No output amounts returned from querySwapExactIn".into());
@@ -390,14 +390,14 @@ impl SolutionVerifier {
390390
}
391391
Err(e) => {
392392
// Return the error - call details will be saved separately in the JSON
393-
Err(Box::new(e))
393+
Err(format!("Query failed: {:?}", e).into())
394394
}
395395
}
396396
}
397397
}
398398

399399
fn create_v3_call_details(
400-
batch_router: &BalancerV3BatchRouter,
400+
batch_router: &BalancerV3BatchRouter::Instance,
401401
pool_address: &str,
402402
input_token: &Address,
403403
output_token: &Address,
@@ -414,12 +414,12 @@ fn create_v3_call_details(
414414
"exactAmountIn": input_amount.to_string(),
415415
"minAmountOut": "0"
416416
}],
417-
"sender": format!("{:?}", batch_router.address()),
417+
"sender": format!("{:?}", batch_router.address().into_legacy()),
418418
"userData": "0x"
419419
});
420420

421421
ContractCallDetails {
422-
contract_address: format!("{:?}", batch_router.address()),
422+
contract_address: format!("{:?}", batch_router.address().into_legacy()),
423423
contract_name: "BalancerV3BatchRouter".to_string(),
424424
function_name: "querySwapExactIn".to_string(),
425425
calldata: "(error - call details captured without execution)".to_string(),

crates/contracts/artifacts/BalancerV3BatchRouter.json

Lines changed: 998 additions & 1 deletion
Large diffs are not rendered by default.

crates/contracts/build.rs

Lines changed: 0 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -172,66 +172,6 @@ fn main() {
172172
},
173173
)
174174
});
175-
generate_contract_with_config("BalancerV3BatchRouter", |builder| {
176-
builder
177-
.add_network(
178-
MAINNET,
179-
Network {
180-
address: addr("0x136f1EFcC3f8f88516B9E94110D56FDBfB1778d1"),
181-
// <https://etherscan.io/tx/0x41cb8619fb92dd532eb09b0e81fd4ce1c6006a10924893f02909e36a317777f3>
182-
deployment_information: Some(DeploymentInformation::BlockNumber(21339510)),
183-
},
184-
)
185-
.add_network(
186-
GNOSIS,
187-
Network {
188-
address: addr("0xe2fa4e1d17725e72dcdAfe943Ecf45dF4B9E285b"),
189-
// <https://gnosisscan.io/tx/0xeafddbace9f445266f851ef1d92928e3d01a4622a1a6780b41ac52d5872f12ab>
190-
deployment_information: Some(DeploymentInformation::BlockNumber(37377506)),
191-
},
192-
)
193-
.add_network(
194-
SEPOLIA,
195-
Network {
196-
address: addr("0xC85b652685567C1B074e8c0D4389f83a2E458b1C"),
197-
// <https://sepolia.etherscan.io/tx/0x95ed8e1aaaa7bdc5881f3c8fc5a4914a66639bee52987c3a1ea88545083b0681>
198-
deployment_information: Some(DeploymentInformation::BlockNumber(7219301)),
199-
},
200-
)
201-
.add_network(
202-
ARBITRUM_ONE,
203-
Network {
204-
address: addr("0xaD89051bEd8d96f045E8912aE1672c6C0bF8a85E"),
205-
// <https://arbiscan.io/tx/0xa7968c6bc0775208ffece789c6e5d09b0eea5f2c3ed2806e9bd94fb0b978ff0f>
206-
deployment_information: Some(DeploymentInformation::BlockNumber(297828544)),
207-
},
208-
)
209-
.add_network(
210-
BASE,
211-
Network {
212-
address: addr("0x85a80afee867aDf27B50BdB7b76DA70f1E853062"),
213-
// <https://basescan.org/tx/0x47b81146714630ce50445bfa28872a36973acedf785317ca423498810ec8e76c>
214-
deployment_information: Some(DeploymentInformation::BlockNumber(25347205)),
215-
},
216-
)
217-
.add_network(
218-
AVALANCHE,
219-
Network {
220-
address: addr("0xc9b36096f5201ea332Db35d6D195774ea0D5988f"),
221-
// <https://snowscan.xyz/tx/0x3bfaba7135ee2d67d98f20ee1aa4c8b7e81e47be64223376f3086bab429ac806>
222-
deployment_information: Some(DeploymentInformation::BlockNumber(59965747)),
223-
},
224-
)
225-
.add_network(
226-
OPTIMISM,
227-
Network {
228-
address: addr("0xaD89051bEd8d96f045E8912aE1672c6C0bF8a85E"),
229-
// <https://optimistic.etherscan.io/tx/0xf370aab0d652f3e0f7c34e1a53e1afd98e86c487138300b0939d4e54b0088b67>
230-
deployment_information: Some(DeploymentInformation::BlockNumber(133969588)),
231-
},
232-
)
233-
// Not available on Lens
234-
});
235175
generate_contract_with_config("BalancerV3WeightedPoolFactory", |builder| {
236176
builder
237177
.add_network(

crates/contracts/src/alloy.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,26 @@ crate::bindings!(
371371
// Not available on Lens
372372
}
373373
);
374+
crate::bindings!(
375+
BalancerV3BatchRouter,
376+
crate::deployments! {
377+
// <https://etherscan.io/tx/0x41cb8619fb92dd532eb09b0e81fd4ce1c6006a10924893f02909e36a317777f3>
378+
MAINNET => (address!("0x136f1EFcC3f8f88516B9E94110D56FDBfB1778d1"), 21339510),
379+
// <https://gnosisscan.io/tx/0xeafddbace9f445266f851ef1d92928e3d01a4622a1a6780b41ac52d5872f12ab>
380+
GNOSIS => (address!("0xe2fa4e1d17725e72dcdAfe943Ecf45dF4B9E285b"), 37377506),
381+
// <https://sepolia.etherscan.io/tx/0x95ed8e1aaaa7bdc5881f3c8fc5a4914a66639bee52987c3a1ea88545083b0681>
382+
SEPOLIA => (address!("0xC85b652685567C1B074e8c0D4389f83a2E458b1C"), 7219301),
383+
// <https://arbiscan.io/tx/0xa7968c6bc0775208ffece789c6e5d09b0eea5f2c3ed2806e9bd94fb0b978ff0f>
384+
ARBITRUM_ONE => (address!("0xaD89051bEd8d96f045E8912aE1672c6C0bF8a85E"), 297828544),
385+
// <https://basescan.org/tx/0x47b81146714630ce50445bfa28872a36973acedf785317ca423498810ec8e76c>
386+
BASE => (address!("0x85a80afee867aDf27B50BdB7b76DA70f1E853062"), 25347205),
387+
// <https://snowscan.xyz/tx/0x3bfaba7135ee2d67d98f20ee1aa4c8b7e81e47be64223376f3086bab429ac806>
388+
AVALANCHE => (address!("0xc9b36096f5201ea332Db35d6D195774ea0D5988f"), 59965747),
389+
// <https://optimistic.etherscan.io/tx/0xf370aab0d652f3e0f7c34e1a53e1afd98e86c487138300b0939d4e54b0088b67>
390+
OPTIMISM => (address!("0xaD89051bEd8d96f045E8912aE1672c6C0bF8a85E"), 133969588),
391+
// Not available on Lens, Polygon, BNB
392+
}
393+
);
374394

375395
// UniV2
376396
crate::bindings!(

crates/contracts/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ macro_rules! include_contracts {
5151
include_contracts! {
5252
BalancerV2Authorizer;
5353
BalancerV2Vault;
54-
BalancerV3BatchRouter;
5554
BalancerV3Vault;
5655
BalancerV3WeightedPool;
5756
BalancerV3WeightedPoolFactory;

crates/driver/src/boundary/liquidity/balancer/v3/gyro_2clp.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use {
66
liquidity::{self, balancer},
77
},
88
},
9+
ethrpc::alloy::conversions::IntoLegacy,
910
shared::sources::balancer_v3::pool_fetching::Gyro2CLPPoolVersion,
1011
solver::liquidity::{BalancerV3Gyro2CLPOrder, balancer_v3},
1112
};
@@ -59,6 +60,7 @@ fn batch_router(pool: &BalancerV3Gyro2CLPOrder) -> eth::ContractAddress {
5960
.expect("downcast balancer settlement handler")
6061
.batch_router()
6162
.address()
63+
.into_legacy()
6264
.into()
6365
}
6466

crates/driver/src/boundary/liquidity/balancer/v3/gyro_e.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use {
66
liquidity::{self, balancer},
77
},
88
},
9+
ethrpc::alloy::conversions::IntoLegacy,
910
shared::sources::balancer_v3::pool_fetching::GyroEPoolVersion,
1011
solver::liquidity::{BalancerV3GyroEOrder, balancer_v3},
1112
};
@@ -78,6 +79,7 @@ fn batch_router(pool: &BalancerV3GyroEOrder) -> eth::ContractAddress {
7879
.expect("downcast balancer settlement handler")
7980
.batch_router()
8081
.address()
82+
.into_legacy()
8183
.into()
8284
}
8385

0 commit comments

Comments
 (0)