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