asterdex_sdk/auth/
credentials.rs1use alloy_primitives::Address;
3use alloy_signer::SignerSync;
4use alloy_signer_local::PrivateKeySigner;
5use alloy_sol_types::Eip712Domain;
6use std::sync::atomic::{AtomicU64, Ordering};
7use std::time::{SystemTime, UNIX_EPOCH};
8
9use crate::rest::error::AsterDexError;
10
11pub struct Credentials {
14 pub user: Address,
15 pub signer: Address,
16 signer_wallet: PrivateKeySigner,
17 last_nonce: AtomicU64,
20 user_str: String,
22 signer_str: String,
24 domain: Eip712Domain,
26}
27
28impl Credentials {
29 pub fn new(
31 user: &str,
32 signer: &str,
33 private_key_hex: &str,
34 chain_id: u64,
35 ) -> Result<Self, AsterDexError> {
36 let key_hex = private_key_hex.trim_start_matches("0x");
38 let wallet = key_hex.parse::<PrivateKeySigner>().map_err(|e| {
39 AsterDexError::ConfigError {
40 message: format!("invalid signer private key: {e}"),
41 }
42 })?;
43 let user_addr = user.parse::<Address>().map_err(|e| {
44 AsterDexError::ConfigError {
45 message: format!("invalid user address: {e}"),
46 }
47 })?;
48 let signer_addr = signer.parse::<Address>().map_err(|e| {
49 AsterDexError::ConfigError {
50 message: format!("invalid signer address: {e}"),
51 }
52 })?;
53
54 let user_str = format!("{:?}", user_addr);
56 let signer_str = format!("{:?}", signer_addr);
57
58 let domain = crate::auth::eip712::get_domain(chain_id);
60
61 Ok(Self {
62 user: user_addr,
63 signer: signer_addr,
64 signer_wallet: wallet,
65 last_nonce: AtomicU64::new(0),
66 user_str,
67 signer_str,
68 domain,
69 })
70 }
71
72 pub fn from_env() -> Result<Self, AsterDexError> {
74 let user = std::env::var("ASTERDEX_USER").map_err(|_| AsterDexError::ConfigError {
75 message: "missing env var: ASTERDEX_USER".to_string(),
76 })?;
77 let signer =
78 std::env::var("ASTERDEX_SIGNER_ADDRESS").map_err(|_| AsterDexError::ConfigError {
79 message: "missing env var: ASTERDEX_SIGNER_ADDRESS".to_string(),
80 })?;
81 let key = std::env::var("ASTERDEX_SIGNER_PRIVATE_KEY").map_err(|_| {
82 AsterDexError::ConfigError {
83 message: "missing env var: ASTERDEX_SIGNER_PRIVATE_KEY".to_string(),
84 }
85 })?;
86 let chain_id = match std::env::var("ASTERDEX_CHAIN_ID") {
87 Ok(s) => s.parse::<u64>().map_err(|_| AsterDexError::ConfigError {
88 message: format!("ASTERDEX_CHAIN_ID is not a valid integer: {s}"),
89 })?,
90 Err(_) => 1666, };
92 Self::new(&user, &signer, &key, chain_id)
93 }
94
95 pub fn new_nonce(&self) -> u64 {
108 let base = SystemTime::now()
109 .duration_since(UNIX_EPOCH)
110 .map(|d| d.as_micros() as u64)
111 .unwrap_or(0);
112 loop {
113 let prev = self.last_nonce.load(Ordering::Relaxed);
114 let next = base.max(prev.saturating_add(1));
115 match self.last_nonce.compare_exchange(
116 prev,
117 next,
118 Ordering::AcqRel,
119 Ordering::Relaxed,
120 ) {
121 Ok(_) => return next,
122 Err(_) => continue,
123 }
124 }
125 }
126
127 pub fn sign_query(
136 &self,
137 params: &[(&str, &str)],
138 ) -> Result<String, AsterDexError> {
139 let nonce = self.new_nonce();
140 let nonce_str = nonce.to_string();
141
142 let capacity = params.len() * 64 + 256;
145 let mut param_string = String::with_capacity(capacity);
146
147 for (k, v) in params {
149 if !param_string.is_empty() {
150 param_string.push('&');
151 }
152 param_string.push_str(k);
153 param_string.push('=');
154 param_string.push_str(&urlencoding::encode(v));
155 }
156
157 if !param_string.is_empty() {
160 param_string.push('&');
161 }
162 param_string.push_str("nonce=");
163 param_string.push_str(&nonce_str);
164 param_string.push_str("&user=");
165 param_string.push_str(&self.user_str);
166 param_string.push_str("&signer=");
167 param_string.push_str(&self.signer_str);
168
169 let hash = crate::auth::eip712::signing_hash(¶m_string, &self.domain);
171 let sig = self.signer_wallet.sign_hash_sync(&hash).map_err(|e| {
172 AsterDexError::ConfigError {
173 message: format!("signing failed: {e}"),
174 }
175 })?;
176
177 let sig_str = format!("{}", sig);
179 let sig_hex = sig_str.strip_prefix("0x").unwrap_or(&sig_str);
180 param_string.push_str("&signature=");
181 param_string.push_str(sig_hex);
182
183 Ok(param_string)
184 }
185}