newton-core 0.4.16

newton protocol core sdk
use serde::{Deserialize, Deserializer, Serialize};

// Custom deserializer to preserve large numbers as strings without precision loss
// This is critical for BLS/ECDSA private keys which are 77-digit numbers
fn deserialize_optional_string<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
where
    D: Deserializer<'de>,
{
    #[derive(Deserialize)]
    #[serde(untagged)]
    enum StringOrNumber {
        String(String),
        Number(f64),
        Int(i64),
        UInt(u64),
    }

    let value = Option::<StringOrNumber>::deserialize(deserializer)?;

    match value {
        None => Ok(None),
        Some(StringOrNumber::String(s)) => {
            // Already a string - preserve as-is
            Ok(Some(s))
        }
        Some(StringOrNumber::Number(n)) => {
            // Number type - this means precision was already lost upstream
            // Best we can do is convert to string, but this will have rounding errors
            Ok(Some(format!("{:.0}", n)))
        }
        Some(StringOrNumber::Int(n)) => Ok(Some(n.to_string())),
        Some(StringOrNumber::UInt(n)) => Ok(Some(n.to_string())),
    }
}

/// Configuration for a BLS key used by the operator.
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
pub struct BlsKey {
    /// Optional path to a keystore file containing the BLS private key.
    pub keystore_path: Option<String>,

    /// Optional password used to decrypt the BLS keystore file.
    pub keystore_password: Option<String>,

    #[serde(deserialize_with = "deserialize_optional_string")]
    /// Optional BLS private key represented as a string.
    pub private_key: Option<String>,
}

/// Configuration for an ECDSA key used by the AVS services.
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
pub struct EcdsaKey {
    /// Optional path to a keystore file containing the ECDSA private key.
    pub keystore_path: Option<String>,

    /// Optional password used to decrypt the ECDSA keystore file.
    pub keystore_password: Option<String>,

    #[serde(deserialize_with = "deserialize_optional_string")]
    /// Optional ECDSA private key represented as a string.
    pub private_key: Option<String>,
}