@@ -18,7 +18,7 @@ use indexer_dips::{
1818 server:: { DipsServer , DipsServerContext } ,
1919 signers:: EscrowSignerValidator ,
2020} ;
21- use indexer_monitor:: { escrow_accounts_v1, DeploymentDetails , SubgraphClient } ;
21+ use indexer_monitor:: { escrow_accounts_v1, escrow_accounts_v2 , DeploymentDetails , SubgraphClient } ;
2222use release:: IndexerServiceRelease ;
2323use reqwest:: Url ;
2424use tap_core:: tap_eip712_domain;
@@ -78,12 +78,7 @@ pub async fn run() -> anyhow::Result<()> {
7878 )
7979 . await ;
8080
81- let escrow_subgraph = create_subgraph_client (
82- http_client. clone ( ) ,
83- & config. graph_node ,
84- & config. subgraphs . escrow . config ,
85- )
86- . await ;
81+ // V2 escrow accounts are in the network subgraph, not a separate escrow_v2 subgraph
8782
8883 // Establish Database connection necessary for serving indexer management
8984 // requests with defined schema
@@ -105,19 +100,133 @@ pub async fn run() -> anyhow::Result<()> {
105100 let indexer_address = config. indexer . indexer_address ;
106101 let ipfs_url = config. service . ipfs_url . clone ( ) ;
107102
108- let router = ServiceRouter :: builder ( )
109- . database ( database. clone ( ) )
110- . domain_separator ( domain_separator. clone ( ) )
111- . graph_node ( config. graph_node )
112- . http_client ( http_client)
113- . release ( release)
114- . indexer ( config. indexer )
115- . service ( config. service )
116- . blockchain ( config. blockchain )
117- . timestamp_buffer_secs ( config. tap . rav_request . timestamp_buffer_secs )
118- . network_subgraph ( network_subgraph, config. subgraphs . network )
119- . escrow_subgraph ( escrow_subgraph, config. subgraphs . escrow )
120- . build ( ) ;
103+ // Capture individual fields needed for DIPS before they get moved
104+ let escrow_v1_query_url_for_dips = config. subgraphs . escrow . config . query_url . clone ( ) ;
105+ // V2 escrow accounts are in the network subgraph
106+ let escrow_v2_query_url_for_dips = Some ( config. subgraphs . network . config . query_url . clone ( ) ) ;
107+
108+ // Determine if we should check for Horizon contracts and potentially enable hybrid mode:
109+ // - If horizon.enabled = false: Pure legacy mode, no Horizon detection
110+ // - If horizon.enabled = true: Check if Horizon contracts are active in the network
111+ let is_horizon_active = if config. horizon . enabled {
112+ tracing:: info!( "Horizon migration support enabled - checking if Horizon contracts are active in the network" ) ;
113+ match indexer_monitor:: is_horizon_active ( network_subgraph) . await {
114+ Ok ( active) => {
115+ if active {
116+ tracing:: info!( "Horizon contracts detected in network subgraph - enabling hybrid migration mode" ) ;
117+ tracing:: info!( "Mode: Accept new V2 receipts only, continue processing existing V1 receipts for RAVs" ) ;
118+ } else {
119+ tracing:: info!( "Horizon contracts not yet active in network subgraph - remaining in legacy mode" ) ;
120+ }
121+ active
122+ }
123+ Err ( e) => {
124+ tracing:: warn!(
125+ "Failed to detect Horizon contracts: {}. Remaining in legacy mode." ,
126+ e
127+ ) ;
128+ false
129+ }
130+ }
131+ } else {
132+ tracing:: info!(
133+ "Horizon migration support disabled in configuration - using pure legacy mode"
134+ ) ;
135+ false
136+ } ;
137+
138+ // Configure router with escrow watchers based on automatic Horizon detection
139+ let router = if is_horizon_active {
140+ tracing:: info!( "Horizon contracts detected - using Horizon migration mode: V2 receipts only, but processing existing V1 receipts" ) ;
141+
142+ // Create V1 escrow watcher for processing existing receipts
143+ let escrow_subgraph_v1 = create_subgraph_client (
144+ http_client. clone ( ) ,
145+ & config. graph_node ,
146+ & config. subgraphs . escrow . config ,
147+ )
148+ . await ;
149+
150+ let v1_watcher = indexer_monitor:: escrow_accounts_v1 (
151+ escrow_subgraph_v1,
152+ indexer_address,
153+ config. subgraphs . escrow . config . syncing_interval_secs ,
154+ true , // Reject thawing signers eagerly
155+ )
156+ . await
157+ . expect ( "Error creating escrow_accounts_v1 channel" ) ;
158+
159+ // Create V2 escrow watcher for new receipts (V2 escrow accounts are in the network subgraph)
160+ let v2_watcher = match indexer_monitor:: escrow_accounts_v2 (
161+ network_subgraph,
162+ indexer_address,
163+ config. subgraphs . network . config . syncing_interval_secs ,
164+ true , // Reject thawing signers eagerly
165+ )
166+ . await
167+ {
168+ Ok ( watcher) => {
169+ tracing:: info!( "V2 escrow accounts successfully initialized from network subgraph" ) ;
170+ watcher
171+ }
172+ Err ( e) => {
173+ tracing:: error!(
174+ "Failed to initialize V2 escrow accounts: {}. Service cannot continue." ,
175+ e
176+ ) ;
177+ std:: process:: exit ( 1 ) ;
178+ }
179+ } ;
180+
181+ ServiceRouter :: builder ( )
182+ . database ( database. clone ( ) )
183+ . domain_separator ( domain_separator. clone ( ) )
184+ . graph_node ( config. graph_node )
185+ . http_client ( http_client)
186+ . release ( release)
187+ . indexer ( config. indexer )
188+ . service ( config. service )
189+ . blockchain ( config. blockchain )
190+ . timestamp_buffer_secs ( config. tap . rav_request . timestamp_buffer_secs )
191+ . network_subgraph ( network_subgraph, config. subgraphs . network )
192+ . escrow_accounts_v1 ( v1_watcher)
193+ . escrow_accounts_v2 ( v2_watcher)
194+ . build ( )
195+ } else {
196+ tracing:: info!(
197+ "No Horizon contracts detected - using Legacy (V1) mode with escrow accounts v1 only"
198+ ) ;
199+ // Only create v1 watcher for legacy mode
200+ let escrow_subgraph_v1 = create_subgraph_client (
201+ http_client. clone ( ) ,
202+ & config. graph_node ,
203+ & config. subgraphs . escrow . config ,
204+ )
205+ . await ;
206+
207+ let v1_watcher = indexer_monitor:: escrow_accounts_v1 (
208+ escrow_subgraph_v1,
209+ indexer_address,
210+ config. subgraphs . escrow . config . syncing_interval_secs ,
211+ true , // Reject thawing signers eagerly
212+ )
213+ . await
214+ . expect ( "Error creating escrow_accounts_v1 channel" ) ;
215+
216+ ServiceRouter :: builder ( )
217+ . database ( database. clone ( ) )
218+ . domain_separator ( domain_separator. clone ( ) )
219+ . graph_node ( config. graph_node )
220+ . http_client ( http_client)
221+ . release ( release)
222+ . indexer ( config. indexer )
223+ . service ( config. service )
224+ . blockchain ( config. blockchain )
225+ . timestamp_buffer_secs ( config. tap . rav_request . timestamp_buffer_secs )
226+ . network_subgraph ( network_subgraph, config. subgraphs . network )
227+ . escrow_accounts_v1 ( v1_watcher)
228+ . build ( )
229+ } ;
121230
122231 serve_metrics ( config. metrics . get_socket_addr ( ) ) ;
123232
@@ -143,14 +252,64 @@ pub async fn run() -> anyhow::Result<()> {
143252 Arc :: new ( IpfsClient :: new ( ipfs_url. as_str ( ) ) . unwrap ( ) ) ;
144253
145254 // TODO: Try to re-use the same watcher for both DIPS and TAP
146- let watcher = escrow_accounts_v1 (
147- escrow_subgraph,
148- indexer_address,
149- Duration :: from_secs ( 500 ) ,
150- true ,
151- )
152- . await
153- . expect ( "Failed to create escrow accounts watcher" ) ;
255+ // DIPS is part of Horizon/v2, so use v2 escrow watcher when available
256+ let dips_http_client = reqwest:: Client :: builder ( )
257+ . timeout ( Duration :: from_secs ( 60 ) )
258+ . build ( )
259+ . expect ( "Failed to init HTTP client" ) ;
260+
261+ let escrow_subgraph_for_dips = if let Some ( ref escrow_v2_url) = escrow_v2_query_url_for_dips
262+ {
263+ tracing:: info!( "DIPS using v2 escrow subgraph" ) ;
264+ // Create subgraph client for v2
265+ Box :: leak ( Box :: new (
266+ SubgraphClient :: new (
267+ dips_http_client,
268+ None , // No local deployment
269+ DeploymentDetails :: for_query_url_with_token (
270+ escrow_v2_url. clone ( ) ,
271+ None , // No auth token
272+ ) ,
273+ )
274+ . await ,
275+ ) )
276+ } else {
277+ tracing:: info!( "DIPS falling back to v1 escrow subgraph" ) ;
278+ // Create subgraph client for v1
279+ Box :: leak ( Box :: new (
280+ SubgraphClient :: new (
281+ dips_http_client,
282+ None , // No local deployment
283+ DeploymentDetails :: for_query_url_with_token (
284+ escrow_v1_query_url_for_dips,
285+ None , // No auth token
286+ ) ,
287+ )
288+ . await ,
289+ ) )
290+ } ;
291+
292+ let watcher = if escrow_v2_query_url_for_dips. is_some ( ) {
293+ // Use v2 watcher for DIPS when v2 is available
294+ escrow_accounts_v2 (
295+ escrow_subgraph_for_dips,
296+ indexer_address,
297+ Duration :: from_secs ( 500 ) ,
298+ true ,
299+ )
300+ . await
301+ . expect ( "Failed to create escrow accounts v2 watcher for DIPS" )
302+ } else {
303+ // Fall back to v1 watcher
304+ escrow_accounts_v1 (
305+ escrow_subgraph_for_dips,
306+ indexer_address,
307+ Duration :: from_secs ( 500 ) ,
308+ true ,
309+ )
310+ . await
311+ . expect ( "Failed to create escrow accounts v1 watcher for DIPS" )
312+ } ;
154313
155314 let registry = NetworksRegistry :: from_latest_version ( ) . await . unwrap ( ) ;
156315
0 commit comments