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