Skip to main content

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            .map(|d| d.as_secs())
22            .unwrap_or(0)
23    });
24    let n = nonce.unwrap_or(0);
25
26    let address = signer.get_address().await?;
27
28    let sig = crate::signing::build_clob_eip712_signature(signer, chain_id as i64, ts, n)
29        .await
30        .map_err(|e| ClobError::Other(format!("failed to build eip712 signature: {}", e)))?;
31
32    let mut headers = Headers::new();
33    headers.insert("POLY_ADDRESS".to_string(), address);
34    headers.insert("POLY_SIGNATURE".to_string(), sig);
35    headers.insert("POLY_TIMESTAMP".to_string(), ts.to_string());
36    headers.insert("POLY_NONCE".to_string(), n.to_string());
37    Ok(headers)
38}
39
40/// Build L2 headers using HMAC signature (POLY API HMAC) along with API key/passphrase.
41pub async fn create_l2_headers<S: crate::signing::Eip712Signer + Send + Sync>(
42    signer: &S,
43    creds: &ApiKeyCreds,
44    method: &str,
45    request_path: &str,
46    body: Option<&str>,
47    timestamp: Option<u64>,
48) -> Result<Headers, ClobError> {
49    let ts = timestamp.unwrap_or_else(|| {
50        SystemTime::now()
51            .duration_since(UNIX_EPOCH)
52            .map(|d| d.as_secs())
53            .unwrap_or(0)
54    });
55    let address = signer.get_address().await?;
56
57    // creds.secret is expected to be base64 encoded; build HMAC signature.
58    let sig = match crate::signing::build_poly_hmac_signature(
59        &creds.secret,
60        ts,
61        method,
62        request_path,
63        body,
64    ) {
65        Ok(s) => s,
66        Err(e) => {
67            // If HMAC calculation fails (e.g., secret not base64), return an error to surface configuration problems.
68            return Err(ClobError::Other(format!(
69                "failed to build hmac signature: {}",
70                e
71            )));
72        }
73    };
74
75    // 调试输出(受环境变量控制),避免在生产泄露敏感信息。
76    // 仅在设置 CLOB_DEBUG_FULL 或 CLOB_DEBUG_RAW 时输出。
77    if std::env::var("CLOB_DEBUG_FULL").is_ok() || std::env::var("CLOB_DEBUG_RAW").is_ok() {
78        let preview_secret = if creds.secret.len() > 6 {
79            format!("{}***", &creds.secret[..6])
80        } else {
81            "***".to_string()
82        };
83        let payload_preview = {
84            let mut base = format!("{}{}{}", ts, method, request_path);
85            if let Some(b) = body {
86                base.push_str(b);
87            }
88            // 避免日志过长,只截取前后 200 字符
89            if base.len() > 420 {
90                format!("{} ... {}", &base[..200], &base[base.len() - 200..])
91            } else {
92                base
93            }
94        };
95        eprintln!(
96            "[L2 DEBUG] ts(sec)={} method={} path={} secret(b64-prefix)={} sig={} payload_part={} body_len={}",
97            ts,
98            method,
99            request_path,
100            preview_secret,
101            sig,
102            payload_preview,
103            body.map(|b| b.len()).unwrap_or(0)
104        );
105    }
106
107    let mut headers = Headers::new();
108    headers.insert("POLY_ADDRESS".to_string(), address);
109    headers.insert("POLY_SIGNATURE".to_string(), sig);
110    headers.insert("POLY_TIMESTAMP".to_string(), ts.to_string());
111    headers.insert("POLY_API_KEY".to_string(), creds.key.clone());
112    headers.insert("POLY_PASSPHRASE".to_string(), creds.passphrase.clone());
113    Ok(headers)
114}
115
116pub fn inject_builder_headers(
117    mut l2: Headers,
118    builder: &std::collections::HashMap<String, String>,
119) -> Headers {
120    for (k, v) in builder.iter() {
121        l2.insert(k.clone(), v.clone());
122    }
123    l2
124}