11use  { 
22    crate :: { 
33        chain:: reader:: { BlockNumber ,  BlockStatus ,  EntropyReader } , 
4+         config:: Config , 
45        history:: History , 
56        state:: MonitoredHashChainState , 
67    } , 
@@ -76,13 +77,17 @@ pub struct ApiState {
7677pub  metrics :  Arc < ApiMetrics > , 
7778
7879    pub  explorer_metrics :  Arc < ExplorerMetrics > , 
80+ 
81+     /// Parsed configuration 
82+ pub  config :  Config , 
7983} 
8084
8185impl  ApiState  { 
8286    pub  async  fn  new ( 
8387        chains :  Arc < RwLock < HashMap < ChainId ,  ApiBlockChainState > > > , 
8488        metrics_registry :  Arc < RwLock < Registry > > , 
8589        history :  Arc < History > , 
90+         config :  Config , 
8691    )  -> ApiState  { 
8792        let  metrics = ApiMetrics  { 
8893            http_requests :  Family :: default ( ) , 
@@ -103,6 +108,7 @@ impl ApiState {
103108            explorer_metrics, 
104109            history, 
105110            metrics_registry, 
111+             config, 
106112        } 
107113    } 
108114} 
@@ -234,9 +240,10 @@ mod test {
234240        crate :: { 
235241            api:: { 
236242                self ,  ApiBlockChainState ,  ApiState ,  BinaryEncoding ,  Blob ,  BlockchainState , 
237-                 GetRandomValueResponse , 
243+                 ChainConfigSummary ,   GetRandomValueResponse , 
238244            } , 
239245            chain:: reader:: { mock:: MockEntropyReader ,  BlockStatus } , 
246+             config:: Config , 
240247            history:: History , 
241248            state:: { HashChainState ,  MonitoredHashChainState ,  PebbleHashChain } , 
242249        } , 
@@ -315,10 +322,40 @@ mod test {
315322            ApiBlockChainState :: Initialized ( avax_state) , 
316323        ) ; 
317324
325+         // Create a minimal config for testing 
326+         let  config = Config  { 
327+             chains :  HashMap :: new ( ) , 
328+             provider :  crate :: config:: ProviderConfig  { 
329+                 uri :  "http://localhost:8080/" . to_string ( ) , 
330+                 address :  PROVIDER , 
331+                 private_key :  crate :: config:: SecretString  { 
332+                     value :  Some ( "0xabcd" . to_string ( ) ) , 
333+                     file :  None , 
334+                 } , 
335+                 secret :  crate :: config:: SecretString  { 
336+                     value :  Some ( "abcd" . to_string ( ) ) , 
337+                     file :  None , 
338+                 } , 
339+                 chain_length :  100000 , 
340+                 chain_sample_interval :  10 , 
341+                 fee_manager :  None , 
342+             } , 
343+             keeper :  crate :: config:: KeeperConfig  { 
344+                 private_key :  crate :: config:: SecretString  { 
345+                     value :  Some ( "0xabcd" . to_string ( ) ) , 
346+                     file :  None , 
347+                 } , 
348+                 fee_manager_private_key :  None , 
349+                 other_keeper_addresses :  vec ! [ ] , 
350+                 replica_config :  None , 
351+             } , 
352+         } ; 
353+ 
318354        let  api_state = ApiState :: new ( 
319355            Arc :: new ( RwLock :: new ( chains) ) , 
320356            metrics_registry, 
321357            Arc :: new ( History :: new ( ) . await . unwrap ( ) ) , 
358+             config, 
322359        ) 
323360        . await ; 
324361
@@ -538,4 +575,183 @@ mod test {
538575        ) 
539576        . await ; 
540577    } 
578+ 
579+     #[ tokio:: test]  
580+     async  fn  test_chain_configs ( )  { 
581+         let  ( server,  _,  _)  = test_server ( ) . await ; 
582+ 
583+         // Test the chain configs endpoint 
584+         let  response = server. get ( "/v1/chains/configs" ) . await ; 
585+         response. assert_status ( StatusCode :: OK ) ; 
586+ 
587+         // Parse the response as JSON 
588+         let  configs:  Vec < ChainConfigSummary >  = response. json ( ) ; 
589+         
590+         // Verify the response structure - should be empty for test server 
591+         assert_eq ! ( configs. len( ) ,  0 ,  "Should return empty configs for test server" ) ; 
592+     } 
593+ 
594+     #[ tokio:: test]  
595+     async  fn  test_chain_configs_with_data ( )  { 
596+         // Create a test server with actual chain configurations 
597+         let  eth_read = Arc :: new ( MockEntropyReader :: with_requests ( 10 ,  & [ ] ) ) ; 
598+         let  avax_read = Arc :: new ( MockEntropyReader :: with_requests ( 10 ,  & [ ] ) ) ; 
599+ 
600+         let  eth_state = MonitoredHashChainState :: new ( 
601+             ETH_CHAIN . clone ( ) , 
602+             Default :: default ( ) , 
603+             "ethereum" . into ( ) , 
604+             PROVIDER , 
605+         ) ; 
606+ 
607+         let  eth_state = BlockchainState  { 
608+             id :  "ethereum" . into ( ) , 
609+             network_id :  1 , 
610+             state :  Arc :: new ( eth_state) , 
611+             contract :  eth_read. clone ( ) , 
612+             provider_address :  PROVIDER , 
613+             reveal_delay_blocks :  1 , 
614+             confirmed_block_status :  BlockStatus :: Latest , 
615+         } ; 
616+ 
617+         let  avax_state = MonitoredHashChainState :: new ( 
618+             AVAX_CHAIN . clone ( ) , 
619+             Default :: default ( ) , 
620+             "avalanche" . into ( ) , 
621+             PROVIDER , 
622+         ) ; 
623+ 
624+         let  avax_state = BlockchainState  { 
625+             id :  "avalanche" . into ( ) , 
626+             network_id :  43114 , 
627+             state :  Arc :: new ( avax_state) , 
628+             contract :  avax_read. clone ( ) , 
629+             provider_address :  PROVIDER , 
630+             reveal_delay_blocks :  2 , 
631+             confirmed_block_status :  BlockStatus :: Latest , 
632+         } ; 
633+ 
634+         let  mut  chains = HashMap :: new ( ) ; 
635+         chains. insert ( 
636+             "ethereum" . into ( ) , 
637+             ApiBlockChainState :: Initialized ( eth_state) , 
638+         ) ; 
639+         chains. insert ( 
640+             "avalanche" . into ( ) , 
641+             ApiBlockChainState :: Initialized ( avax_state) , 
642+         ) ; 
643+ 
644+         // Create a config with actual chain data 
645+         let  mut  config_chains = HashMap :: new ( ) ; 
646+         config_chains. insert ( 
647+             "ethereum" . to_string ( ) , 
648+             crate :: config:: EthereumConfig  { 
649+                 geth_rpc_addr :  "http://localhost:8545" . to_string ( ) , 
650+                 contract_addr :  Address :: from_low_u64_be ( 0x1234 ) , 
651+                 reveal_delay_blocks :  1 , 
652+                 confirmed_block_status :  BlockStatus :: Latest , 
653+                 backlog_range :  1000 , 
654+                 legacy_tx :  false , 
655+                 gas_limit :  500000 , 
656+                 priority_fee_multiplier_pct :  100 , 
657+                 escalation_policy :  crate :: config:: EscalationPolicyConfig :: default ( ) , 
658+                 min_profit_pct :  0 , 
659+                 target_profit_pct :  20 , 
660+                 max_profit_pct :  100 , 
661+                 min_keeper_balance :  100000000000000000 , 
662+                 fee :  1500000000000000 , 
663+                 sync_fee_only_on_register :  true , 
664+                 commitments :  None , 
665+                 max_num_hashes :  None , 
666+                 block_delays :  vec ! [ 5 ] , 
667+             } , 
668+         ) ; 
669+         config_chains. insert ( 
670+             "avalanche" . to_string ( ) , 
671+             crate :: config:: EthereumConfig  { 
672+                 geth_rpc_addr :  "http://localhost:9650" . to_string ( ) , 
673+                 contract_addr :  Address :: from_low_u64_be ( 0x5678 ) , 
674+                 reveal_delay_blocks :  2 , 
675+                 confirmed_block_status :  BlockStatus :: Latest , 
676+                 backlog_range :  1000 , 
677+                 legacy_tx :  false , 
678+                 gas_limit :  600000 , 
679+                 priority_fee_multiplier_pct :  100 , 
680+                 escalation_policy :  crate :: config:: EscalationPolicyConfig :: default ( ) , 
681+                 min_profit_pct :  0 , 
682+                 target_profit_pct :  20 , 
683+                 max_profit_pct :  100 , 
684+                 min_keeper_balance :  100000000000000000 , 
685+                 fee :  2000000000000000 , 
686+                 sync_fee_only_on_register :  true , 
687+                 commitments :  None , 
688+                 max_num_hashes :  None , 
689+                 block_delays :  vec ! [ 5 ] , 
690+             } , 
691+         ) ; 
692+ 
693+         let  config = Config  { 
694+             chains :  config_chains, 
695+             provider :  crate :: config:: ProviderConfig  { 
696+                 uri :  "http://localhost:8080/" . to_string ( ) , 
697+                 address :  PROVIDER , 
698+                 private_key :  crate :: config:: SecretString  { 
699+                     value :  Some ( "0xabcd" . to_string ( ) ) , 
700+                     file :  None , 
701+                 } , 
702+                 secret :  crate :: config:: SecretString  { 
703+                     value :  Some ( "abcd" . to_string ( ) ) , 
704+                     file :  None , 
705+                 } , 
706+                 chain_length :  100000 , 
707+                 chain_sample_interval :  10 , 
708+                 fee_manager :  None , 
709+             } , 
710+             keeper :  crate :: config:: KeeperConfig  { 
711+                 private_key :  crate :: config:: SecretString  { 
712+                     value :  Some ( "0xabcd" . to_string ( ) ) , 
713+                     file :  None , 
714+                 } , 
715+                 fee_manager_private_key :  None , 
716+                 other_keeper_addresses :  vec ! [ ] , 
717+                 replica_config :  None , 
718+             } , 
719+         } ; 
720+ 
721+         let  metrics_registry = Arc :: new ( RwLock :: new ( Registry :: default ( ) ) ) ; 
722+         let  api_state = ApiState :: new ( 
723+             Arc :: new ( RwLock :: new ( chains) ) , 
724+             metrics_registry, 
725+             Arc :: new ( History :: new ( ) . await . unwrap ( ) ) , 
726+             config, 
727+         ) 
728+         . await ; 
729+ 
730+         let  app = api:: routes ( api_state) ; 
731+         let  server = TestServer :: new ( app) . unwrap ( ) ; 
732+ 
733+         // Test the chain configs endpoint 
734+         let  response = server. get ( "/v1/chains/configs" ) . await ; 
735+         response. assert_status ( StatusCode :: OK ) ; 
736+ 
737+         // Parse the response as JSON 
738+         let  configs:  Vec < ChainConfigSummary >  = response. json ( ) ; 
739+         
740+         // Verify we have 2 chains 
741+         assert_eq ! ( configs. len( ) ,  2 ,  "Should return 2 chain configs" ) ; 
742+         
743+         // Find ethereum config 
744+         let  eth_config = configs. iter ( ) . find ( |c| c. name  == "ethereum" ) . expect ( "Ethereum config not found" ) ; 
745+         assert_eq ! ( eth_config. contract_addr,  "0x0000000000000000000000000000000000001234" ) ; 
746+         assert_eq ! ( eth_config. reveal_delay_blocks,  1 ) ; 
747+         assert_eq ! ( eth_config. gas_limit,  500000 ) ; 
748+         assert_eq ! ( eth_config. fee,  1500000000000000 ) ; 
749+         
750+         // Find avalanche config 
751+         let  avax_config = configs. iter ( ) . find ( |c| c. name  == "avalanche" ) . expect ( "Avalanche config not found" ) ; 
752+         assert_eq ! ( avax_config. contract_addr,  "0x0000000000000000000000000000000000005678" ) ; 
753+         assert_eq ! ( avax_config. reveal_delay_blocks,  2 ) ; 
754+         assert_eq ! ( avax_config. gas_limit,  600000 ) ; 
755+         assert_eq ! ( avax_config. fee,  2000000000000000 ) ; 
756+     } 
541757} 
0 commit comments