clob_client_rust/
headers.rs

1use crate::errors::ClobError;
2use crate::types::ApiKeyCreds;
3use std::collections::HashMap;
4use std::time::{SystemTime, UNIX_EPOCH};
5
6/// Type alias for header map
7pub type Headers = HashMap<String, String>;
8
9/// Build L1 headers using EIP-712 typed-data signing via the Eip712Signer trait.
10pub async fn create_l1_headers<S: crate::signing::Eip712Signer + Send + Sync>(
11    signer: &S,
12    chain_id: i32,
13    nonce: Option<u64>,
14    timestamp: Option<u64>,
15) -> Result<Headers, ClobError> {
16    // 与官方 TypeScript SDK 对齐:使用“秒”级时间戳 Math.floor(Date.now()/1000)
17    // 如果外部传入则直接使用;否则获取当前秒。
18    let ts = timestamp.unwrap_or_else(|| {
19        SystemTime::now()
20            .duration_since(UNIX_EPOCH)
21            .unwrap()
22            .as_secs()
23    });
24    let n = nonce.unwrap_or(0);
25
26    let address = signer.get_address().await?;
27
28    // Use EIP-712 signing helper implemented in `signing.rs`.
29    let sig = crate::signing::build_clob_eip712_signature(signer, chain_id as i64, ts, n)
30        .await
31        .map_err(|e| ClobError::Other(format!("failed to build eip712 signature: {}", e)))?;
32
33    let mut headers = Headers::new();
34    headers.insert("POLY_ADDRESS".to_string(), address);
35    headers.insert("POLY_SIGNATURE".to_string(), sig);
36    headers.insert("POLY_TIMESTAMP".to_string(), ts.to_string());
37    headers.insert("POLY_NONCE".to_string(), n.to_string());
38    Ok(headers)
39}
40
41/// Build L2 headers using HMAC signature (POLY API HMAC) along with API key/passphrase.
42pub async fn create_l2_headers<S: crate::signing::Eip712Signer + Send + Sync>(
43    signer: &S,
44    creds: &ApiKeyCreds,
45    method: &str,
46    request_path: &str,
47    body: Option<&str>,
48    timestamp: Option<u64>,
49) -> Result<Headers, ClobError> {
50    // 对齐 TS:秒级时间戳,避免与后端验证窗口不一致。
51    let ts = timestamp.unwrap_or_else(|| {
52        SystemTime::now()
53            .duration_since(UNIX_EPOCH)
54            .unwrap()
55            .as_secs()
56    });
57    let address = signer.get_address().await?;
58
59    // creds.secret is expected to be base64 encoded; build HMAC signature.
60    let sig = match crate::signing::build_poly_hmac_signature(
61        &creds.secret,
62        ts,
63        method,
64        request_path,
65        body,
66    ) {
67        Ok(s) => s,
68        Err(e) => {
69            // If HMAC calculation fails (e.g., secret not base64), return an error to surface configuration problems.
70            return Err(ClobError::Other(format!(
71                "failed to build hmac signature: {}",
72                e
73            )));
74        }
75    };
76
77    // 调试输出(受环境变量控制),避免在生产泄露敏感信息。
78    // 仅在设置 CLOB_DEBUG_FULL 或 CLOB_DEBUG_RAW 时输出。
79    if std::env::var("CLOB_DEBUG_FULL").is_ok() || std::env::var("CLOB_DEBUG_RAW").is_ok() {
80        let preview_secret = if creds.secret.len() > 6 {
81            format!("{}***", &creds.secret[..6])
82        } else {
83            "***".to_string()
84        };
85        let payload_preview = {
86            let mut base = format!("{}{}{}", ts, method, request_path);
87            if let Some(b) = body {
88                base.push_str(b);
89            }
90            // 避免日志过长,只截取前后 200 字符
91            if base.len() > 420 {
92                format!("{} ... {}", &base[..200], &base[base.len() - 200..])
93            } else {
94                base
95            }
96        };
97        eprintln!(
98            "[L2 DEBUG] ts(sec)={} method={} path={} secret(b64-prefix)={} sig={} payload_part={} body_len={}",
99            ts,
100            method,
101            request_path,
102            preview_secret,
103            sig,
104            payload_preview,
105            body.map(|b| b.len()).unwrap_or(0)
106        );
107    }
108
109    let mut headers = Headers::new();
110    headers.insert("POLY_ADDRESS".to_string(), address);
111    headers.insert("POLY_SIGNATURE".to_string(), sig);
112    headers.insert("POLY_TIMESTAMP".to_string(), ts.to_string());
113    headers.insert("POLY_API_KEY".to_string(), creds.key.clone());
114    headers.insert("POLY_PASSPHRASE".to_string(), creds.passphrase.clone());
115    Ok(headers)
116}
117
118pub fn inject_builder_headers(
119    mut l2: Headers,
120    builder: &std::collections::HashMap<String, String>,
121) -> Headers {
122    for (k, v) in builder.iter() {
123        l2.insert(k.clone(), v.clone());
124    }
125    l2
126}