ether_link/
wallet.rs

1use k256::{
2    ecdsa::SigningKey,
3    elliptic_curve::rand_core::OsRng,
4};
5use sha3::{Digest, Keccak256};
6
7pub struct Wallet {
8    pub address: String,
9    pub privkey: String,
10    pub pubkey_compressed: String,
11    pub pubkey_uncompressed: String,
12}
13
14impl Wallet {
15    // Generate a new Ethereum key pair
16    pub fn new() -> Self {
17        let private_key = SigningKey::random(&mut OsRng);
18        let privkey_hex = hex::encode(private_key.to_bytes());
19
20        let public_key = private_key.verifying_key();
21        // Compressed public key (starts with 0x02 or 0x03)
22        let pubkey_compressed = public_key.to_encoded_point(true).as_bytes().to_vec();
23        let pubkey_compressed_hex = hex::encode(&pubkey_compressed);
24        // Uncompressed public key (starts with 0x04)
25        let pubkey_uncompressed = public_key.to_encoded_point(false).as_bytes().to_vec();
26        let pubkey_uncompressed_hex = hex::encode(&pubkey_uncompressed);
27        
28        // Ethereum address (last 20 bytes of keccak256 of uncompressed key without prefix)
29        let hash = Keccak256::digest(&pubkey_uncompressed[1..]); // remove 0x04 prefix
30        let address = format!("0x{}", hex::encode(&hash[12..]));
31
32        Self {
33            address,
34            privkey: privkey_hex,
35            pubkey_compressed:pubkey_compressed_hex,
36            pubkey_uncompressed:pubkey_uncompressed_hex,
37        }
38    }
39    // Sign a message with the private key
40    pub async fn sign_message(&self, message: &str) -> Result<String, String> {
41        use ethers::core::types::Signature;
42        use ethers::signers::{LocalWallet, Signer};
43        use ethers::core::k256::SecretKey;
44        use std::time::{SystemTime, UNIX_EPOCH};
45        
46        // Convert hex private key to SecretKey
47        let secret_bytes = hex::decode(&self.privkey).map_err(|e| e.to_string())?;
48        let secret_key = SecretKey::from_slice(&secret_bytes).map_err(|e| e.to_string())?;
49        let wallet = LocalWallet::from(secret_key);
50        
51        let address = &self.address;
52        let timestamps = SystemTime::now()
53            .duration_since(UNIX_EPOCH)
54            .unwrap()
55            .as_secs();
56        let message_with_timestamp = format!("{}:{}", message, timestamps);
57        
58        // Verify the wallet address matches our derived address
59        let wallet_addr = format!("0x{}", hex::encode(wallet.address().as_bytes()));
60        debug_assert_eq!(wallet_addr.to_lowercase(), address.to_lowercase());
61
62        // Sign the hash with the private key
63        let signature: Signature = wallet
64            .sign_message(message_with_timestamp.clone())
65            .await
66            .map_err(|e| e.to_string())?;
67
68        // Return a JSON string containing both the message and signature for verification
69        let result = serde_json::json!({
70            "address": address,
71            "signed_message": message_with_timestamp,
72            "signature": format!("0x{}", hex::encode(signature.to_vec())),
73        });
74        Ok(result.to_string())
75    }
76}
77
78