@@ -149,6 +149,8 @@ pub enum MuxKeysLoader {
149
149
pub enum NORegistry {
150
150
#[ serde( alias = "lido" ) ]
151
151
Lido ,
152
+ #[ serde( alias = "ssv" ) ]
153
+ SSV ,
152
154
}
153
155
154
156
impl MuxKeysLoader {
@@ -176,6 +178,7 @@ impl MuxKeysLoader {
176
178
177
179
fetch_lido_registry_keys ( rpc_url, chain, U256 :: from ( * node_operator_id) ) . await
178
180
}
181
+ NORegistry :: SSV => fetch_ssv_pubkeys ( chain, U256 :: from ( * node_operator_id) ) . await ,
179
182
} ,
180
183
}
181
184
}
@@ -266,6 +269,71 @@ async fn fetch_lido_registry_keys(
266
269
Ok ( keys)
267
270
}
268
271
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
+
269
337
#[ cfg( test) ]
270
338
mod tests {
271
339
use alloy:: { primitives:: U256 , providers:: ProviderBuilder } ;
@@ -304,4 +372,16 @@ mod tests {
304
372
305
373
Ok ( ( ) )
306
374
}
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
+ }
307
387
}
0 commit comments