@@ -149,6 +149,8 @@ pub enum MuxKeysLoader {
149149pub enum NORegistry {
150150 #[ serde( alias = "lido" ) ]
151151 Lido ,
152+ #[ serde( alias = "ssv" ) ]
153+ SSV ,
152154}
153155
154156impl MuxKeysLoader {
@@ -176,6 +178,7 @@ impl MuxKeysLoader {
176178
177179 fetch_lido_registry_keys ( rpc_url, chain, U256 :: from ( * node_operator_id) ) . await
178180 }
181+ NORegistry :: SSV => fetch_ssv_pubkeys ( chain, U256 :: from ( * node_operator_id) ) . await ,
179182 } ,
180183 }
181184 }
@@ -266,6 +269,71 @@ async fn fetch_lido_registry_keys(
266269 Ok ( keys)
267270}
268271
272+ async fn fetch_ssv_pubkeys (
273+ chain : Chain ,
274+ node_operator_id : U256 ,
275+ ) -> eyre:: Result < Vec < BlsPublicKey > > {
276+ const MAX_PER_PAGE : usize = 100 ;
277+
278+ let chain_name = match chain {
279+ Chain :: Mainnet => "mainnet" ,
280+ Chain :: Holesky => "holesky" ,
281+ _ => bail ! ( "SSV network is not supported for chain: {chain:?}" ) ,
282+ } ;
283+
284+ let client = reqwest:: Client :: new ( ) ;
285+ let mut pubkeys: Vec < BlsPublicKey > = vec ! [ ] ;
286+ let mut page = 1 ;
287+
288+ loop {
289+ let response = client
290+ . get ( format ! (
291+ "https://api.ssv.network/api/v4/{}/validators/in_operator/{}?perPage={}&page={}" ,
292+ chain_name, node_operator_id, MAX_PER_PAGE , page
293+ ) )
294+ . send ( )
295+ . await
296+ . map_err ( |e| eyre:: eyre!( "Error sending request to SSV network API: {e}" ) ) ?
297+ . json :: < SSVResponse > ( )
298+ . await ?;
299+
300+ pubkeys. extend ( response. validators . iter ( ) . map ( |v| v. pubkey ) . collect :: < Vec < BlsPublicKey > > ( ) ) ;
301+ page += 1 ;
302+
303+ if response. validators . len ( ) < MAX_PER_PAGE {
304+ ensure ! (
305+ pubkeys. len( ) == response. pagination. total,
306+ "expected {} keys, got {}" ,
307+ response. pagination. total,
308+ pubkeys. len( )
309+ ) ;
310+ break ;
311+ }
312+ }
313+
314+ let unique = pubkeys. iter ( ) . collect :: < HashSet < _ > > ( ) ;
315+ ensure ! ( unique. len( ) == pubkeys. len( ) , "found duplicate keys in registry" ) ;
316+
317+ Ok ( pubkeys)
318+ }
319+
320+ #[ derive( Deserialize ) ]
321+ struct SSVResponse {
322+ validators : Vec < SSVValidator > ,
323+ pagination : SSVPagination ,
324+ }
325+
326+ #[ derive( Deserialize ) ]
327+ struct SSVValidator {
328+ #[ serde( rename = "public_key" ) ]
329+ pubkey : BlsPublicKey ,
330+ }
331+
332+ #[ derive( Deserialize ) ]
333+ struct SSVPagination {
334+ total : usize ,
335+ }
336+
269337#[ cfg( test) ]
270338mod tests {
271339 use alloy:: { primitives:: U256 , providers:: ProviderBuilder } ;
@@ -304,4 +372,16 @@ mod tests {
304372
305373 Ok ( ( ) )
306374 }
375+
376+ #[ tokio:: test]
377+ async fn test_ssv_network_fetch ( ) -> eyre:: Result < ( ) > {
378+ let chain = Chain :: Holesky ;
379+ let node_operator_id = U256 :: from ( 200 ) ;
380+
381+ let pubkeys = fetch_ssv_pubkeys ( chain, node_operator_id) . await ?;
382+
383+ assert_eq ! ( pubkeys. len( ) , 3 ) ;
384+
385+ Ok ( ( ) )
386+ }
307387}
0 commit comments