From a54a1a43b99494b3b0ebc503f006fcb7b7d51c22 Mon Sep 17 00:00:00 2001 From: Dom Navara Date: Mon, 8 Jun 2026 14:57:01 +0200 Subject: [PATCH 1/2] Fix SSV pull issue --- crates/common/src/interop/ssv/utils.rs | 28 +++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/crates/common/src/interop/ssv/utils.rs b/crates/common/src/interop/ssv/utils.rs index cfe1f812..272cf271 100644 --- a/crates/common/src/interop/ssv/utils.rs +++ b/crates/common/src/interop/ssv/utils.rs @@ -17,8 +17,13 @@ pub async fn request_ssv_pubkeys_from_ssv_node( http_timeout: Duration, ) -> eyre::Result { let client = reqwest::ClientBuilder::new().timeout(http_timeout).build()?; + // The SSV node API expects operator IDs as numeric (uint64) values. Serializing the + // U256 directly emits a JSON string, which the node rejects with a 400, so narrow it + // to a u64 first. + let operator_id = u64::try_from(node_operator_id) + .map_err(|e| eyre::eyre!("SSV node operator ID does not fit in u64: {e}"))?; let body = json!({ - "operators": [node_operator_id] + "operators": [operator_id] }); let response = client.get(url).json(&body).send().await.map_err(|e| { if e.is_timeout() { @@ -51,3 +56,24 @@ pub async fn request_ssv_pubkeys_from_public_api( serde_json::from_slice::(&body_bytes) .wrap_err("failed to parse SSV response") } + +#[cfg(test)] +mod tests { + use alloy::primitives::U256; + use serde_json::json; + + #[test] + fn ssv_node_request_serializes_operator_as_number() { + let node_operator_id = U256::from(100u64); + + // Regression guard: serializing the U256 directly emits a (hex) JSON string, which + // the SSV node rejects ("cannot unmarshal string into ... uint64"). + let stringy = serde_json::to_string(&json!({ "operators": [node_operator_id] })).unwrap(); + assert_eq!(stringy, r#"{"operators":["0x64"]}"#); + + // The fix narrows to u64 so the operator ID is emitted as a numeric value. + let operator_id = u64::try_from(node_operator_id).unwrap(); + let numeric = serde_json::to_string(&json!({ "operators": [operator_id] })).unwrap(); + assert_eq!(numeric, r#"{"operators":[100]}"#); + } +} From af69694d10596afee8e951d6f119ebb4668b898a Mon Sep 17 00:00:00 2001 From: Jason Vranek Date: Tue, 9 Jun 2026 10:04:31 -0700 Subject: [PATCH 2/2] just fmt --- crates/common/src/interop/ssv/utils.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/crates/common/src/interop/ssv/utils.rs b/crates/common/src/interop/ssv/utils.rs index 272cf271..add840c5 100644 --- a/crates/common/src/interop/ssv/utils.rs +++ b/crates/common/src/interop/ssv/utils.rs @@ -17,9 +17,9 @@ pub async fn request_ssv_pubkeys_from_ssv_node( http_timeout: Duration, ) -> eyre::Result { let client = reqwest::ClientBuilder::new().timeout(http_timeout).build()?; - // The SSV node API expects operator IDs as numeric (uint64) values. Serializing the - // U256 directly emits a JSON string, which the node rejects with a 400, so narrow it - // to a u64 first. + // The SSV node API expects operator IDs as numeric (uint64) values. Serializing + // the U256 directly emits a JSON string, which the node rejects with a 400, + // so narrow it to a u64 first. let operator_id = u64::try_from(node_operator_id) .map_err(|e| eyre::eyre!("SSV node operator ID does not fit in u64: {e}"))?; let body = json!({ @@ -66,8 +66,9 @@ mod tests { fn ssv_node_request_serializes_operator_as_number() { let node_operator_id = U256::from(100u64); - // Regression guard: serializing the U256 directly emits a (hex) JSON string, which - // the SSV node rejects ("cannot unmarshal string into ... uint64"). + // Regression guard: serializing the U256 directly emits a (hex) JSON string, + // which the SSV node rejects ("cannot unmarshal string into ... + // uint64"). let stringy = serde_json::to_string(&json!({ "operators": [node_operator_id] })).unwrap(); assert_eq!(stringy, r#"{"operators":["0x64"]}"#);