chainstream_sdk/
wallet_auth.rs1use async_trait::async_trait;
9use rand::Rng;
10use std::time::{SystemTime, UNIX_EPOCH};
11
12const SIGNATURE_PREFIX: &str = "chainstream";
13
14#[async_trait]
16pub trait WalletSigner: Send + Sync {
17 fn chain(&self) -> &str;
19 fn address(&self) -> &str;
21 async fn sign_message(&self, message: &str) -> Result<String, Box<dyn std::error::Error + Send + Sync>>;
23}
24
25pub fn build_sign_message(chain: &str, address: &str, timestamp: &str, nonce: &str) -> String {
27 format!("{SIGNATURE_PREFIX}:{chain}:{address}:{timestamp}:{nonce}")
28}
29
30fn generate_nonce() -> String {
31 let bytes: [u8; 16] = rand::thread_rng().gen();
32 hex::encode(bytes)
33}
34
35pub struct WalletAuthHeaders {
37 pub address: String,
38 pub chain: String,
39 pub signature: String,
40 pub timestamp: String,
41 pub nonce: String,
42}
43
44pub async fn create_wallet_auth_headers(
46 signer: &dyn WalletSigner,
47) -> Result<WalletAuthHeaders, Box<dyn std::error::Error + Send + Sync>> {
48 let timestamp = SystemTime::now()
49 .duration_since(UNIX_EPOCH)?
50 .as_secs()
51 .to_string();
52 let nonce = generate_nonce();
53 let message = build_sign_message(signer.chain(), signer.address(), ×tamp, &nonce);
54 let signature = signer.sign_message(&message).await?;
55
56 Ok(WalletAuthHeaders {
57 address: signer.address().to_string(),
58 chain: signer.chain().to_string(),
59 signature,
60 timestamp,
61 nonce,
62 })
63}
64
65pub struct WalletAuthMiddleware {
67 signer: Box<dyn WalletSigner>,
68}
69
70impl WalletAuthMiddleware {
71 pub fn new(signer: Box<dyn WalletSigner>) -> Self {
72 Self { signer }
73 }
74}
75
76#[async_trait]
77impl reqwest_middleware::Middleware for WalletAuthMiddleware {
78 async fn handle(
79 &self,
80 mut req: reqwest::Request,
81 extensions: &mut http::Extensions,
82 next: reqwest_middleware::Next<'_>,
83 ) -> reqwest_middleware::Result<reqwest::Response> {
84 let headers = create_wallet_auth_headers(self.signer.as_ref())
85 .await
86 .map_err(|e| reqwest_middleware::Error::Middleware(anyhow::anyhow!("{e}")))?;
87
88 let h = req.headers_mut();
89 h.insert("X-Wallet-Address", headers.address.parse().unwrap());
90 h.insert("X-Wallet-Chain", headers.chain.parse().unwrap());
91 h.insert("X-Wallet-Signature", headers.signature.parse().unwrap());
92 h.insert("X-Wallet-Timestamp", headers.timestamp.parse().unwrap());
93 h.insert("X-Wallet-Nonce", headers.nonce.parse().unwrap());
94
95 next.run(req, extensions).await
96 }
97}
98
99pub struct ApiKeyAuthMiddleware {
101 api_key: String,
102}
103
104impl ApiKeyAuthMiddleware {
105 pub fn new(api_key: impl Into<String>) -> Self {
106 Self {
107 api_key: api_key.into(),
108 }
109 }
110}
111
112#[async_trait]
113impl reqwest_middleware::Middleware for ApiKeyAuthMiddleware {
114 async fn handle(
115 &self,
116 mut req: reqwest::Request,
117 extensions: &mut http::Extensions,
118 next: reqwest_middleware::Next<'_>,
119 ) -> reqwest_middleware::Result<reqwest::Response> {
120 req.headers_mut()
121 .insert("X-API-KEY", self.api_key.parse().unwrap());
122 next.run(req, extensions).await
123 }
124}