@@ -16,8 +16,13 @@ pub async fn request_ssv_pubkeys_from_ssv_node(
1616 http_timeout : Duration ,
1717) -> eyre:: Result < SSVNodeResponse > {
1818 let client = reqwest:: ClientBuilder :: new ( ) . timeout ( http_timeout) . build ( ) ?;
19+ // The SSV node API expects operator IDs as numeric (uint64) values. Serializing
20+ // the U256 directly emits a JSON string, which the node rejects with a 400,
21+ // so narrow it to a u64 first.
22+ let operator_id = u64:: try_from ( node_operator_id)
23+ . map_err ( |e| eyre:: eyre!( "SSV node operator ID does not fit in u64: {e}" ) ) ?;
1924 let body = json ! ( {
20- "operators" : [ node_operator_id ]
25+ "operators" : [ operator_id ]
2126 } ) ;
2227 let response = client. get ( url) . json ( & body) . send ( ) . await . map_err ( |e| {
2328 if e. is_timeout ( ) {
@@ -50,3 +55,25 @@ pub async fn request_ssv_pubkeys_from_public_api(
5055 serde_json:: from_slice :: < SSVPublicResponse > ( & body_bytes)
5156 . wrap_err ( "failed to parse SSV response" )
5257}
58+
59+ #[ cfg( test) ]
60+ mod tests {
61+ use alloy:: primitives:: U256 ;
62+ use serde_json:: json;
63+
64+ #[ test]
65+ fn ssv_node_request_serializes_operator_as_number ( ) {
66+ let node_operator_id = U256 :: from ( 100u64 ) ;
67+
68+ // Regression guard: serializing the U256 directly emits a (hex) JSON string,
69+ // which the SSV node rejects ("cannot unmarshal string into ...
70+ // uint64").
71+ let stringy = serde_json:: to_string ( & json ! ( { "operators" : [ node_operator_id] } ) ) . unwrap ( ) ;
72+ assert_eq ! ( stringy, r#"{"operators":["0x64"]}"# ) ;
73+
74+ // The fix narrows to u64 so the operator ID is emitted as a numeric value.
75+ let operator_id = u64:: try_from ( node_operator_id) . unwrap ( ) ;
76+ let numeric = serde_json:: to_string ( & json ! ( { "operators" : [ operator_id] } ) ) . unwrap ( ) ;
77+ assert_eq ! ( numeric, r#"{"operators":[100]}"# ) ;
78+ }
79+ }
0 commit comments