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