@@ -21,8 +21,38 @@ pub async fn start(args: impl Iterator<Item = String>) {
2121 initialize_observability ( & args) ;
2222 observe:: metrics:: setup_registry ( None , None ) ;
2323 let config = Configuration :: from_path ( & args. config ) . expect ( "failed to load configuration" ) ;
24- tracing:: info!( "pool-indexer starting" ) ;
25- run ( config) . await ;
24+ if args. bootstrap_only {
25+ tracing:: info!( "pool-indexer bootstrap-only starting" ) ;
26+ bootstrap ( config) . await ;
27+ tracing:: info!( "pool-indexer bootstrap complete, exiting" ) ;
28+ } else {
29+ tracing:: info!( "pool-indexer starting" ) ;
30+ run ( config) . await ;
31+ }
32+ }
33+
34+ /// Runs the bootstrap phase (seed + catch-up to the finalized head) for every
35+ /// factory, then returns. Binds no HTTP ports — this is migration-style work
36+ /// meant to run as a K8s initContainer ahead of the serve container.
37+ ///
38+ /// Idempotent: each factory with an existing checkpoint is skipped (see
39+ /// [`bootstrap_factory`]), so re-running on an already-seeded DB is a fast
40+ /// no-op that never touches the subgraph. On return, a subsequent `run` finds
41+ /// the checkpoints present and flips `/startup` ready almost immediately.
42+ pub async fn bootstrap ( config : Configuration ) {
43+ let db = connect_db ( & config) . await ;
44+ let network = config. network ;
45+ let provider = build_provider_checked ( & network) . await ;
46+ let network = Arc :: new ( network) ;
47+
48+ for factory in network. factories . iter ( ) . copied ( ) {
49+ let indexer = UniswapV3Indexer :: new (
50+ provider. clone ( ) ,
51+ db. clone ( ) ,
52+ & network. indexer_config ( factory. address ) ,
53+ ) ;
54+ bootstrap_factory ( & db, & indexer, & network, & factory) . await ;
55+ }
2656}
2757
2858pub async fn run ( config : Configuration ) {
@@ -125,20 +155,7 @@ async fn run_network_indexer(db: PgPool, network: NetworkConfig, barrier: Arc<St
125155 "starting network indexer" ,
126156 ) ;
127157
128- let provider = build_provider ( & network) ;
129-
130- // Catch misconfigured RPC-vs-network pairings (e.g. chain_id=1 pointed
131- // at an Arbitrum node) before we index the wrong chain into the DB.
132- let actual_chain_id = provider
133- . get_chain_id ( )
134- . await
135- . expect ( "failed to fetch chain_id from RPC" ) ;
136- assert_eq ! (
137- actual_chain_id, network. chain_id,
138- "chain_id mismatch for network {}: config says {}, RPC reports {}" ,
139- network. name, network. chain_id, actual_chain_id,
140- ) ;
141-
158+ let provider = build_provider_checked ( & network) . await ;
142159 let network = Arc :: new ( network) ;
143160
144161 // One task per factory. Provider + DB pool are shared; checkpoints are
@@ -255,6 +272,23 @@ fn build_provider(network: &NetworkConfig) -> AlloyProvider {
255272 . clone ( )
256273}
257274
275+ /// Builds the RPC provider and asserts the node's chain_id matches config.
276+ /// Catches misconfigured RPC-vs-network pairings (e.g. chain_id=1 pointed at
277+ /// an Arbitrum node) before we index the wrong chain into the DB.
278+ async fn build_provider_checked ( network : & NetworkConfig ) -> AlloyProvider {
279+ let provider = build_provider ( network) ;
280+ let actual_chain_id = provider
281+ . get_chain_id ( )
282+ . await
283+ . expect ( "failed to fetch chain_id from RPC" ) ;
284+ assert_eq ! (
285+ actual_chain_id, network. chain_id,
286+ "chain_id mismatch for network {}: config says {}, RPC reports {}" ,
287+ network. name, network. chain_id, actual_chain_id,
288+ ) ;
289+ provider
290+ }
291+
258292async fn connect_db ( config : & Configuration ) -> sqlx:: PgPool {
259293 PgPoolOptions :: new ( )
260294 . max_connections ( config. database . max_connections . get ( ) )
0 commit comments