@@ -9,6 +9,9 @@ use std::{
99
1010use anyhow:: anyhow;
1111use indexer_query:: escrow_account:: { self , EscrowAccountQuery } ;
12+ use indexer_query:: escrow_account_v2:: {
13+ self as escrow_account_v2, EscrowAccountQuery as EscrowAccountQueryV2 ,
14+ } ;
1215use thegraph_core:: alloy:: primitives:: { Address , U256 } ;
1316use thiserror:: Error ;
1417use tokio:: sync:: watch:: Receiver ;
@@ -112,13 +115,76 @@ pub async fn escrow_accounts_v2(
112115 . await
113116}
114117
115- // TODO implement escrow accounts v2 query
116118async fn get_escrow_accounts_v2 (
117- _escrow_subgraph : & ' static SubgraphClient ,
118- _indexer_address : Address ,
119- _reject_thawing_signers : bool ,
119+ escrow_subgraph : & ' static SubgraphClient ,
120+ indexer_address : Address ,
121+ reject_thawing_signers : bool ,
120122) -> anyhow:: Result < EscrowAccounts > {
121- Ok ( EscrowAccounts :: new ( HashMap :: new ( ) , HashMap :: new ( ) ) )
123+ // V2 TAP receipts use different field names (payer/service_provider) but the underlying
124+ // escrow account model is identical to V1. Both V1 and V2 receipts reference the same
125+ // sender addresses and the same escrow relationships.
126+ //
127+ // The separation of V1/V2 escrow account watchers allows for potential future differences
128+ // in escrow models, but currently both query the same subgraph data with identical logic.
129+ //
130+ // V2 receipt flow:
131+ // 1. V2 receipt contains payer address (equivalent to V1 sender)
132+ // 2. Receipt is signed by a signer authorized by the payer
133+ // 3. Escrow accounts map: signer -> payer (sender) -> balance
134+ // 4. Service provider (indexer) receives payments from payer's escrow
135+
136+ let response = escrow_subgraph
137+ . query :: < EscrowAccountQueryV2 , _ > ( escrow_account_v2:: Variables {
138+ indexer : format ! ( "{:x?}" , indexer_address) ,
139+ thaw_end_timestamp : if reject_thawing_signers {
140+ U256 :: ZERO . to_string ( )
141+ } else {
142+ U256 :: MAX . to_string ( )
143+ } ,
144+ } )
145+ . await ?;
146+
147+ let response = response?;
148+
149+ tracing:: trace!( "V2 Escrow accounts response: {:?}" , response) ;
150+
151+ let senders_balances: HashMap < Address , U256 > = response
152+ . escrow_accounts
153+ . iter ( )
154+ . map ( |account| {
155+ let balance = U256 :: checked_sub (
156+ U256 :: from_str ( & account. balance ) ?,
157+ U256 :: from_str ( & account. total_amount_thawing ) ?,
158+ )
159+ . unwrap_or_else ( || {
160+ tracing:: warn!(
161+ "Balance minus total amount thawing underflowed for V2 account {}. \
162+ Setting balance to 0, no V2 queries will be served for this payer.",
163+ account. payer. id
164+ ) ;
165+ U256 :: from ( 0 )
166+ } ) ;
167+
168+ Ok ( ( Address :: from_str ( & account. payer . id ) ?, balance) )
169+ } )
170+ . collect :: < Result < HashMap < _ , _ > , anyhow:: Error > > ( ) ?;
171+
172+ let senders_to_signers = response
173+ . escrow_accounts
174+ . into_iter ( )
175+ . map ( |account| {
176+ let payer = Address :: from_str ( & account. payer . id ) ?;
177+ let signers = account
178+ . payer
179+ . signers
180+ . iter ( )
181+ . map ( |signer| Address :: from_str ( & signer. id ) )
182+ . collect :: < Result < Vec < _ > , _ > > ( ) ?;
183+ Ok ( ( payer, signers) )
184+ } )
185+ . collect :: < Result < HashMap < _ , _ > , anyhow:: Error > > ( ) ?;
186+
187+ Ok ( EscrowAccounts :: new ( senders_balances, senders_to_signers) )
122188}
123189
124190async fn get_escrow_accounts_v1 (
@@ -262,4 +328,54 @@ mod tests {
262328 )
263329 ) ;
264330 }
331+
332+ #[ test( tokio:: test) ]
333+ async fn test_current_accounts_v2 ( ) {
334+ // Set up a mock escrow subgraph for V2 with payer fields
335+ let mock_server = MockServer :: start ( ) . await ;
336+ let escrow_subgraph = Box :: leak ( Box :: new (
337+ SubgraphClient :: new (
338+ reqwest:: Client :: new ( ) ,
339+ None ,
340+ DeploymentDetails :: for_query_url ( & format ! (
341+ "{}/subgraphs/id/{}" ,
342+ & mock_server. uri( ) ,
343+ test_assets:: ESCROW_SUBGRAPH_DEPLOYMENT
344+ ) )
345+ . unwrap ( ) ,
346+ )
347+ . await ,
348+ ) ) ;
349+
350+ let mock = Mock :: given ( method ( "POST" ) )
351+ . and ( path ( format ! (
352+ "/subgraphs/id/{}" ,
353+ test_assets:: ESCROW_SUBGRAPH_DEPLOYMENT
354+ ) ) )
355+ . respond_with (
356+ ResponseTemplate :: new ( 200 )
357+ . set_body_raw ( test_assets:: ESCROW_QUERY_RESPONSE_V2 , "application/json" ) ,
358+ ) ;
359+ mock_server. register ( mock) . await ;
360+
361+ // Test V2 escrow accounts watcher
362+ let mut accounts = escrow_accounts_v2 (
363+ escrow_subgraph,
364+ test_assets:: INDEXER_ADDRESS ,
365+ Duration :: from_secs ( 60 ) ,
366+ true ,
367+ )
368+ . await
369+ . unwrap ( ) ;
370+ accounts. changed ( ) . await . unwrap ( ) ;
371+
372+ // V2 should produce identical results to V1 since they query the same data
373+ assert_eq ! (
374+ accounts. borrow( ) . clone( ) ,
375+ EscrowAccounts :: new(
376+ ESCROW_ACCOUNTS_BALANCES . to_owned( ) ,
377+ ESCROW_ACCOUNTS_SENDERS_TO_SIGNERS . to_owned( ) ,
378+ )
379+ ) ;
380+ }
265381}
0 commit comments