nautilus_coinbase_intx/common/
credential.rs
1use base64::prelude::*;
17use ring::hmac;
18use ustr::Ustr;
19
20pub fn get_env_var(key: &str) -> anyhow::Result<String> {
26 match std::env::var(key) {
27 Ok(var) => Ok(var),
28 Err(_) => anyhow::bail!("environment variable '{key}' must be set"),
29 }
30}
31
32#[derive(Debug, Clone)]
36pub struct Credential {
37 pub api_key: Ustr,
38 pub api_passphrase: Ustr,
39 hmac_key: hmac::Key,
40}
41
42impl Credential {
43 #[must_use]
45 pub fn new(api_key: String, api_secret: String, api_passphrase: String) -> Self {
46 let decoded_secret = BASE64_STANDARD
47 .decode(api_secret)
48 .expect("Invalid base64 secret key");
49
50 Self {
51 api_key: api_key.into(),
52 api_passphrase: api_passphrase.into(),
53 hmac_key: hmac::Key::new(hmac::HMAC_SHA256, &decoded_secret),
54 }
55 }
56
57 pub fn sign(&self, timestamp: &str, method: &str, endpoint: &str, body: &str) -> String {
59 let request_path = match endpoint.find('?') {
61 Some(index) => &endpoint[..index],
62 None => endpoint,
63 };
64
65 let message = format!("{timestamp}{method}{request_path}{body}");
66 tracing::trace!("Signing message: {message}");
67 let signature = hmac::sign(&self.hmac_key, message.as_bytes());
68 BASE64_STANDARD.encode(signature)
69 }
70
71 pub fn sign_ws(&self, timestamp: &str) -> String {
72 let message = format!("{timestamp}{}CBINTLMD{}", self.api_key, self.api_passphrase);
73 tracing::trace!("Signing message: {message}");
74 let signature = hmac::sign(&self.hmac_key, message.as_bytes());
75 BASE64_STANDARD.encode(signature)
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use rstest::rstest;
82
83 use super::*;
84
85 const API_KEY: &str = "test_key_123";
86 const API_SECRET: &str = "dGVzdF9zZWNyZXRfYmFzZTY0"; const API_PASSPHRASE: &str = "test_pass";
88
89 #[rstest]
90 fn test_simple_get() {
91 let credential = Credential::new(
92 API_KEY.to_string(),
93 API_SECRET.to_string(),
94 API_PASSPHRASE.to_string(),
95 );
96 let timestamp = "1641890400"; let signature = credential.sign(timestamp, "GET", "/api/v1/fee-rate-tiers", "");
98
99 assert_eq!(signature, "h/9tnYzD/nsEbH1sV7dkB5uJ3Vygr4TjmOOxJNQB8ts=");
100 }
101}