11use {
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 ) ]
5454pub struct SolutionVerifier {
55- vault : BalancerV2Vault ,
55+ vault : BalancerV2Vault :: Instance ,
5656 batch_router : BalancerV3BatchRouter :: Instance ,
5757}
5858
5959impl 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
0 commit comments