Skip to content

Commit 00545f7

Browse files
committed
Migrate BalancerV2Vault to alloy (cowprotocol#3793)
1 parent c2a17cd commit 00545f7

File tree

25 files changed

+280
-311
lines changed

25 files changed

+280
-311
lines changed

crates/autopilot/src/run.rs

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use {
2626
},
2727
chain::Chain,
2828
clap::Parser,
29-
contracts::{BalancerV2Vault, IUniswapV3Factory},
29+
contracts::{IUniswapV3Factory, alloy::BalancerV2Vault},
3030
ethcontract::{BlockNumber, H160, common::DeploymentInformation, errors::DeployError},
3131
ethrpc::{
3232
Web3,
@@ -232,22 +232,21 @@ pub async fn run(args: Arguments, shutdown_controller: ShutdownController) {
232232
.instrument(info_span!("vault_relayer_call"))
233233
.await
234234
.expect("Couldn't get vault relayer address");
235-
let vault = match args.shared.balancer_v2_vault_address {
236-
Some(address) => Some(contracts::BalancerV2Vault::with_deployment_info(
237-
&web3, address, None,
238-
)),
239-
None => match BalancerV2Vault::deployed(&web3)
240-
.instrument(info_span!("balancerV2vault_deployed"))
241-
.await
242-
{
243-
Ok(contract) => Some(contract),
244-
Err(DeployError::NotFound(_)) => {
245-
tracing::warn!("balancer contracts are not deployed on this network");
246-
None
247-
}
248-
Err(err) => panic!("failed to get balancer vault contract: {err}"),
249-
},
250-
};
235+
236+
let vault_address = args.shared.balancer_v2_vault_address.or_else(|| {
237+
let chain_id = chain.id();
238+
let addr = BalancerV2Vault::deployment_address(&chain_id);
239+
if addr.is_none() {
240+
tracing::warn!(
241+
chain_id,
242+
"balancer contracts are not deployed on this network"
243+
);
244+
}
245+
addr
246+
});
247+
let vault =
248+
vault_address.map(|address| BalancerV2Vault::Instance::new(address, web3.alloy.clone()));
249+
251250
let uniswapv3_factory = match IUniswapV3Factory::deployed(&web3)
252251
.instrument(info_span!("uniswapv3_deployed"))
253252
.await
@@ -275,7 +274,7 @@ pub async fn run(args: Arguments, shutdown_controller: ShutdownController) {
275274
eth.contracts().settlement().clone(),
276275
eth.contracts().balances().clone(),
277276
vault_relayer,
278-
vault.as_ref().map(|contract| contract.address()),
277+
vault_address.map(IntoLegacy::into_legacy),
279278
balance_overrider,
280279
),
281280
eth.current_block().clone(),

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,10 @@ impl Solver {
147147
(Some(vault_addr), Some(batch_router_addr), Some(ref node_url)) => {
148148
let web3 =
149149
ethrpc::web3(Default::default(), Default::default(), node_url, "verifier");
150-
let vault = contracts::BalancerV2Vault::at(&web3, vault_addr.0);
150+
let vault = contracts::alloy::BalancerV2Vault::Instance::new(
151+
vault_addr.0.into_alloy(),
152+
web3.alloy.clone(),
153+
);
151154
let batch_router = contracts::alloy::BalancerV3BatchRouter::Instance::new(
152155
batch_router_addr.0.into_alloy(),
153156
web3.alloy,

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

Lines changed: 70 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
use {
22
alloy::primitives,
3-
contracts::{
4-
BalancerV2Vault,
5-
alloy::BalancerV3BatchRouter::{
3+
contracts::alloy::{
4+
BalancerV2Vault::{self, IVault},
5+
BalancerV3BatchRouter::{
66
self,
77
IBatchRouter::{SwapPathExactAmountIn, SwapPathStep},
88
},
99
},
10-
ethcontract::{Address, Bytes, H160, U256},
10+
ethcontract::{Address, H160, U256},
1111
ethrpc::alloy::conversions::{IntoAlloy, IntoLegacy},
1212
serde::{Deserialize, Serialize},
1313
};
@@ -52,12 +52,15 @@ pub enum PoolVersion {
5252

5353
#[derive(Clone)]
5454
pub struct SolutionVerifier {
55-
vault: BalancerV2Vault,
55+
vault: BalancerV2Vault::Instance,
5656
batch_router: BalancerV3BatchRouter::Instance,
5757
}
5858

5959
impl SolutionVerifier {
60-
pub fn new(vault: BalancerV2Vault, batch_router: BalancerV3BatchRouter::Instance) -> Self {
60+
pub fn new(
61+
vault: BalancerV2Vault::Instance,
62+
batch_router: BalancerV3BatchRouter::Instance,
63+
) -> Self {
6164
Self {
6265
vault,
6366
batch_router,
@@ -216,6 +219,7 @@ impl SolutionVerifier {
216219
}
217220

218221
/// Quote V2 swap via Vault.queryBatchSwap
222+
/// This uses a static call (eth_call) to query the expected output amount.
219223
async fn quote_v2_swap(
220224
&self,
221225
balancer_pool_id: &str,
@@ -237,35 +241,36 @@ impl SolutionVerifier {
237241
let mut pool_id = [0u8; 32];
238242
pool_id.copy_from_slice(&pool_id_bytes);
239243

240-
// Build assets array: [token_in, token_out]
241-
let assets = vec![input_token, output_token];
242-
243-
// Create BatchSwapStep
244-
let swap = self.vault.methods().query_batch_swap(
245-
0u8.into(), // SwapKind.GIVEN_IN
246-
vec![(
247-
Bytes(pool_id),
248-
0u64.into(), // assetInIndex
249-
1u64.into(), // assetOutIndex
250-
input_amount,
251-
Bytes(vec![]), // empty userData
252-
)],
244+
// Build assets array using alloy types
245+
let assets = vec![input_token.into_alloy(), output_token.into_alloy()];
246+
247+
// Create BatchSwapStep using alloy types
248+
let swap_step = IVault::BatchSwapStep {
249+
poolId: primitives::FixedBytes::from(pool_id),
250+
assetInIndex: primitives::U256::from(0u64),
251+
assetOutIndex: primitives::U256::from(1u64),
252+
amount: input_amount.into_alloy(),
253+
userData: primitives::Bytes::new(),
254+
};
255+
256+
// Create FundManagement struct
257+
let funds = IVault::FundManagement {
258+
sender: primitives::Address::ZERO,
259+
fromInternalBalance: false,
260+
recipient: primitives::Address::ZERO,
261+
toInternalBalance: false,
262+
};
263+
264+
// Build the call - .call() automatically makes it a static call (eth_call)
265+
let call_builder = self.vault.queryBatchSwap(
266+
0u8, // SwapKind.GIVEN_IN
267+
vec![swap_step.clone()],
253268
assets.clone(),
254-
(
255-
H160::zero(), // sender (not needed for query)
256-
false, // fromInternalBalance
257-
H160::zero(), // recipient (not needed for query)
258-
false, // toInternalBalance
259-
),
269+
funds,
260270
);
261271

262272
// Capture contract call details for debugging
263-
let calldata = swap
264-
.tx
265-
.data
266-
.clone()
267-
.map(|d| format!("0x{}", hex::encode(d.0)))
268-
.unwrap_or_else(|| "0x".to_string());
273+
let calldata = format!("0x{}", hex::encode(call_builder.calldata()));
269274

270275
let decoded_params = serde_json::json!({
271276
"kind": "GIVEN_IN (0)",
@@ -277,8 +282,8 @@ impl SolutionVerifier {
277282
"userData": "0x"
278283
}],
279284
"assets": vec![
280-
format!("{:?}", assets[0]),
281-
format!("{:?}", assets[1])
285+
format!("{:?}", input_token),
286+
format!("{:?}", output_token)
282287
],
283288
"funds": {
284289
"sender": "0x0000000000000000000000000000000000000000",
@@ -289,34 +294,47 @@ impl SolutionVerifier {
289294
});
290295

291296
let call_details = ContractCallDetails {
292-
contract_address: format!("{:?}", self.vault.address()),
297+
contract_address: format!("{:?}", self.vault.address().into_legacy()),
293298
contract_name: "BalancerV2Vault".to_string(),
294299
function_name: "queryBatchSwap".to_string(),
295300
calldata,
296301
decoded_params,
297302
};
298303

299-
// Call the query (static call)
300-
let deltas = swap.call().await?;
304+
// Execute the static call
305+
let result = call_builder.call().await;
301306

302-
// Parse output: assetDeltas[1] represents net token flow for output token
303-
// In Balancer V2:
304-
// - Positive delta = tokens going INTO vault (user sends)
305-
// - Negative delta = tokens coming OUT of vault (user receives)
306-
// For the output token in a swap, we expect a NEGATIVE delta
307-
if deltas.len() < 2 {
308-
return Err("Invalid deltas returned from queryBatchSwap".into());
309-
}
307+
match result {
308+
Ok(deltas) => {
309+
// Parse output: assetDeltas[1] represents net token flow for output token
310+
// In Balancer V2:
311+
// - Positive delta = tokens going INTO vault (user sends)
312+
// - Negative delta = tokens coming OUT of vault (user receives)
313+
// For the output token in a swap, we expect a NEGATIVE delta
314+
if deltas.len() < 2 {
315+
return Err("Invalid deltas returned from queryBatchSwap".into());
316+
}
310317

311-
let amount_out = if deltas[1].is_negative() {
312-
// Negative means tokens out - negate to get positive amount
313-
(-deltas[1]).into_raw()
314-
} else {
315-
// Positive means tokens in, which is wrong for output token
316-
return Err("Expected negative output delta (tokens out of vault)".into());
317-
};
318+
let delta_out = deltas[1];
319+
320+
// Check if the signed value is negative
321+
let amount_out = if delta_out.is_negative() {
322+
// Negative means tokens out - convert to unsigned by negating
323+
// For Signed types, we need to negate and convert to unsigned
324+
let abs_value = delta_out.abs();
325+
primitives::U256::from_limbs(abs_value.into_limbs())
326+
} else {
327+
// Positive means tokens in, which is wrong for output token
328+
return Err("Expected negative output delta (tokens out of vault)".into());
329+
};
318330

319-
Ok((amount_out.to_string(), call_details))
331+
Ok((amount_out.to_string(), call_details))
332+
}
333+
Err(e) => {
334+
// Return the error - call details will be saved separately in the JSON
335+
Err(format!("Query failed: {:?}", e).into())
336+
}
337+
}
320338
}
321339

322340
/// Quote V3 swap via Batch Router.querySwapExactIn

crates/contracts/build.rs

Lines changed: 0 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -31,86 +31,6 @@ fn main() {
3131
// - https://doc.rust-lang.org/cargo/reference/build-scripts.html#cargorerun-if-changedpath
3232
println!("cargo:rerun-if-changed=build.rs");
3333

34-
// Balancer addresses can be obtained from:
35-
// <https://github.com/balancer/balancer-subgraph-v2/blob/master/networks.yaml>
36-
generate_contract_with_config("BalancerV2Vault", |builder| {
37-
builder
38-
.contract_mod_override("balancer_v2_vault")
39-
.add_network(
40-
MAINNET,
41-
Network {
42-
address: addr("0xBA12222222228d8Ba445958a75a0704d566BF2C8"),
43-
// <https://etherscan.io/tx/0x28c44bb10d469cbd42accf97bd00b73eabbace138e9d44593e851231fbed1cb7>
44-
deployment_information: Some(DeploymentInformation::BlockNumber(12272146)),
45-
},
46-
)
47-
.add_network(
48-
GNOSIS,
49-
Network {
50-
address: addr("0xBA12222222228d8Ba445958a75a0704d566BF2C8"),
51-
// <https://gnosisscan.io/tx/0x21947751661e1b9197492f22779af1f5175b71dc7057869e5a8593141d40edf1>
52-
deployment_information: Some(DeploymentInformation::BlockNumber(24821598)),
53-
},
54-
)
55-
.add_network(
56-
SEPOLIA,
57-
Network {
58-
address: addr("0xBA12222222228d8Ba445958a75a0704d566BF2C8"),
59-
// <https://sepolia.etherscan.io/tx/0xb22509c6725dd69a975ecb96a0c594901eeee6a279cc66d9d5191022a7039ee6>
60-
deployment_information: Some(DeploymentInformation::BlockNumber(3418831)),
61-
},
62-
)
63-
.add_network(
64-
ARBITRUM_ONE,
65-
Network {
66-
address: addr("0xBA12222222228d8Ba445958a75a0704d566BF2C8"),
67-
// <https://arbiscan.io/tx/0xe2c3826bd7b15ef8d338038769fe6140a44f1957a36b0f27ab321ab6c68d5a8e>
68-
deployment_information: Some(DeploymentInformation::BlockNumber(222832)),
69-
},
70-
)
71-
.add_network(
72-
BASE,
73-
Network {
74-
address: addr("0xBA12222222228d8Ba445958a75a0704d566BF2C8"),
75-
// <https://basescan.org/tx/0x0dc2e3d436424f2f038774805116896d31828c0bf3795a6901337bdec4e0dff6>
76-
deployment_information: Some(DeploymentInformation::BlockNumber(1196036)),
77-
},
78-
)
79-
.add_network(
80-
AVALANCHE,
81-
Network {
82-
address: addr("0xBA12222222228d8Ba445958a75a0704d566BF2C8"),
83-
// <https://snowscan.xyz/tx/0xc49af0372feb032e0edbba6988410304566b1fd65546c01ced620ac3c934120f>
84-
deployment_information: Some(DeploymentInformation::BlockNumber(26386141)),
85-
},
86-
)
87-
.add_network(
88-
BNB,
89-
Network {
90-
address: addr("0xBA12222222228d8Ba445958a75a0704d566BF2C8"),
91-
// <https://bscscan.com/tx/0x1de8caa6c54ff9a25600e26d80865d84c9cc4d33c2b98611240529ee7de5cd74>
92-
deployment_information: Some(DeploymentInformation::BlockNumber(22691002)),
93-
},
94-
)
95-
.add_network(
96-
OPTIMISM,
97-
Network {
98-
address: addr("0xBA12222222228d8Ba445958a75a0704d566BF2C8"),
99-
// <https://optimistic.etherscan.io/tx/0xa03cb990595df9eed6c5db17a09468cab534aed5f5589a06c0bb3d19dd2f7ce9>
100-
deployment_information: Some(DeploymentInformation::BlockNumber(7003431)),
101-
},
102-
)
103-
.add_network(
104-
POLYGON,
105-
Network {
106-
address: addr("0xBA12222222228d8Ba445958a75a0704d566BF2C8"),
107-
// <https://polygonscan.com/tx/0x66f275a2ed102a5b679c0894ced62c4ebcb2a65336d086a916eb83bd1fe5c8d2>
108-
deployment_information: Some(DeploymentInformation::BlockNumber(15832990)),
109-
},
110-
)
111-
// Not available on Lens
112-
});
113-
11434
// Balancer V3 contracts
11535
generate_contract_with_config("BalancerV3Vault", |builder| {
11636
builder

crates/contracts/src/alloy.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,8 @@ crate::bindings!(
349349
}
350350
);
351351
crate::bindings!(
352+
// Balancer addresses can be obtained from:
353+
// <https://github.com/balancer/balancer-subgraph-v2/blob/master/networks.yaml>
352354
BalancerV2Vault,
353355
crate::deployments! {
354356
// <https://etherscan.io/tx/0x28c44bb10d469cbd42accf97bd00b73eabbace138e9d44593e851231fbed1cb7>

crates/contracts/src/lib.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ macro_rules! include_contracts {
4949
}
5050

5151
include_contracts! {
52-
BalancerV2Vault;
5352
BalancerV3Vault;
5453
BalancerV3WeightedPool;
5554
BalancerV3WeightedPoolFactory;
@@ -164,7 +163,6 @@ mod tests {
164163
] {
165164
assert_has_deployment_address!(GPv2Settlement for *network);
166165
assert_has_deployment_address!(WETH9 for *network);
167-
assert_has_deployment_address!(BalancerV2Vault for *network);
168166
assert!(
169167
alloy::BalancerV2NoProtocolFeeLiquidityBootstrappingPoolFactory::deployment_address(network).is_some()
170168
)
@@ -223,7 +221,6 @@ mod tests {
223221

224222
for network in &[MAINNET, GNOSIS, SEPOLIA, ARBITRUM_ONE] {
225223
assert_has_deployment_information!(GPv2Settlement for *network);
226-
assert_has_deployment_information!(BalancerV2Vault for *network);
227224
}
228225
assert!(alloy::BalancerV2WeightedPoolFactory::deployment_address(&MAINNET).is_some());
229226
for network in &[MAINNET, ARBITRUM_ONE] {

0 commit comments

Comments
 (0)