use serde::{Deserialize, Serialize};
use std::time::{SystemTime, UNIX_EPOCH};
use uuid::Uuid;
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct WalletNonce {
pub nonce: String,
pub address: String,
pub created_at: u64,
pub expires_at: u64,
}
impl WalletNonce {
pub fn new(address: String) -> Self {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs();
let nonce = format!(
"Sign this message to authenticate with Alou Pay:\n\nNonce: {}\nAddress: {}\nTimestamp: {}",
Uuid::new_v4(),
address,
now
);
Self {
nonce,
address: address.to_lowercase(),
created_at: now,
expires_at: now + 300, }
}
pub fn is_expired(&self) -> bool {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs();
now > self.expires_at
}
pub fn get_message(&self) -> &str {
&self.nonce
}
}
pub fn verify_signature(
_message: &str,
signature: &str,
expected_address: &str,
) -> Result<bool, String> {
if signature.len() < 132 {
return Err("Invalid signature format".to_string());
}
if !expected_address.starts_with("0x") {
return Err("Invalid address format".to_string());
}
Ok(true)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_nonce_creation() {
let address = "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb".to_string();
let nonce = WalletNonce::new(address.clone());
assert_eq!(nonce.address, address.to_lowercase());
assert!(!nonce.is_expired());
assert!(nonce.get_message().contains("Alou Pay"));
}
#[test]
fn test_nonce_expiration() {
let address = "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb".to_string();
let mut nonce = WalletNonce::new(address);
nonce.expires_at = 0;
assert!(nonce.is_expired());
}
}