11use crate :: {
22 error:: { Error , TimeoutError } ,
3- prelude:: { ChainClient , LOG_TARGET } ,
3+ prelude:: { ChainClient , Config , LOG_TARGET } ,
44 prometheus,
55} ;
66use std:: {
@@ -11,6 +11,7 @@ use std::{
1111 time:: Duration ,
1212} ;
1313use subxt:: backend:: {
14+ chain_head:: { ChainHeadBackend , ChainHeadBackendBuilder } ,
1415 legacy:: LegacyBackend ,
1516 rpc:: reconnecting_rpc_client:: { ExponentialBackoff , RpcClient as ReconnectingRpcClient } ,
1617} ;
@@ -52,7 +53,7 @@ pub struct Client {
5253}
5354
5455impl Client {
55- /// Create a new client from a comma-separated list of RPC endpoints.
56+ /// Create a new client from a comma-separated list of RPC endpoints using ChainHeadBackend .
5657 ///
5758 /// The client will try each endpoint in sequence until one connects successfully.
5859 /// Multiple endpoints can be specified for failover:
@@ -72,7 +73,38 @@ impl Client {
7273 log:: info!( target: LOG_TARGET , "RPC endpoint pool: {} endpoint(s)" , endpoints. len( ) ) ;
7374 }
7475
75- let ( chain_api, connected_index) = Self :: connect_with_failover ( & endpoints, 0 ) . await ?;
76+ let ( chain_api, connected_index) =
77+ Self :: connect_with_failover ( & endpoints, 0 , false ) . await ?;
78+
79+ Ok ( Self {
80+ chain_api : Arc :: new ( RwLock :: new ( chain_api) ) ,
81+ endpoints : Arc :: new ( endpoints) ,
82+ current_endpoint_index : Arc :: new ( AtomicUsize :: new ( connected_index) ) ,
83+ reconnect_generation : Arc :: new ( AtomicUsize :: new ( 0 ) ) ,
84+ } )
85+ }
86+
87+ /// Create a new client from a comma-separated list of RPC endpoints using LegacyBackend.
88+ ///
89+ /// The client will try each endpoint in sequence until one connects successfully.
90+ /// Multiple endpoints can be specified for failover:
91+ /// "wss://rpc1.example.com,wss://rpc2.example.com"
92+ pub async fn new_with_legacy_backend ( uris : & str ) -> Result < Self , Error > {
93+ let endpoints: Vec < String > = uris
94+ . split ( ',' )
95+ . map ( |s| s. trim ( ) . to_string ( ) )
96+ . filter ( |s| !s. is_empty ( ) )
97+ . collect ( ) ;
98+
99+ if endpoints. is_empty ( ) {
100+ return Err ( Error :: Other ( "No RPC endpoints provided" . into ( ) ) ) ;
101+ }
102+
103+ if endpoints. len ( ) > 1 {
104+ log:: info!( target: LOG_TARGET , "RPC endpoint pool: {} endpoint(s)" , endpoints. len( ) ) ;
105+ }
106+
107+ let ( chain_api, connected_index) = Self :: connect_with_failover ( & endpoints, 0 , true ) . await ?;
76108
77109 Ok ( Self {
78110 chain_api : Arc :: new ( RwLock :: new ( chain_api) ) ,
@@ -95,6 +127,7 @@ impl Client {
95127 async fn connect_with_failover (
96128 endpoints : & [ String ] ,
97129 start_index : usize ,
130+ use_legacy : bool ,
98131 ) -> Result < ( ChainClient , usize ) , Error > {
99132 let mut last_error = None ;
100133 let total = endpoints. len ( ) ;
@@ -112,7 +145,7 @@ impl Client {
112145 "attempting to connect to {uri:?} (endpoint {endpoint_num}/{total}, attempt {attempt}/{max_attempts})"
113146 ) ;
114147
115- match Self :: try_connect ( uri) . await {
148+ match Self :: try_connect ( uri, use_legacy ) . await {
116149 Ok ( client) => {
117150 if total > 1 {
118151 log:: info!(
@@ -150,8 +183,8 @@ impl Client {
150183 }
151184
152185 /// Try to connect to a single endpoint with timeout.
153- async fn try_connect ( uri : & str ) -> Result < ChainClient , Error > {
154- let connect_future = async {
186+ async fn try_connect ( uri : & str , use_legacy : bool ) -> Result < ChainClient , Error > {
187+ let connect_future = async move {
155188 let reconnecting_rpc = ReconnectingRpcClient :: builder ( )
156189 . retry_policy (
157190 ExponentialBackoff :: from_millis ( 500 ) . max_delay ( Duration :: from_secs ( 10 ) ) . take ( 3 ) ,
@@ -160,11 +193,18 @@ impl Client {
160193 . await
161194 . map_err ( |e| Error :: Other ( format ! ( "Failed to connect: {e:?}" ) ) ) ?;
162195
163- let backend = LegacyBackend :: builder ( ) . build ( reconnecting_rpc. clone ( ) ) ;
164-
165- let chain_api = ChainClient :: from_backend ( Arc :: new ( backend) ) . await ?;
166-
167- log:: info!( target: LOG_TARGET , "Connected to {uri} with Legacy backend" ) ;
196+ let chain_api = if use_legacy {
197+ let backend = LegacyBackend :: builder ( ) . build ( reconnecting_rpc. clone ( ) ) ;
198+ let client = ChainClient :: from_backend ( Arc :: new ( backend) ) . await ?;
199+ log:: info!( target: LOG_TARGET , "Connected to {uri} with Legacy backend" ) ;
200+ client
201+ } else {
202+ let backend: ChainHeadBackend < Config > = ChainHeadBackendBuilder :: default ( )
203+ . build_with_background_driver ( reconnecting_rpc) ;
204+ let client = ChainClient :: from_backend ( Arc :: new ( backend) ) . await ?;
205+ log:: info!( target: LOG_TARGET , "Connected to {uri} with ChainHead backend" ) ;
206+ client
207+ } ;
168208
169209 Ok :: < ChainClient , Error > ( chain_api)
170210 } ;
@@ -216,7 +256,7 @@ impl Client {
216256
217257 // Establish new connection before acquiring write lock
218258 let ( new_client, connected_idx) =
219- Self :: connect_with_failover ( & self . endpoints , start_idx) . await ?;
259+ Self :: connect_with_failover ( & self . endpoints , start_idx, false ) . await ?;
220260
221261 // Acquire write lock and check if another task already reconnected
222262 let mut guard = self . chain_api . write ( ) . await ;
0 commit comments