kobe_svm/
standard_wallet.rs1use alloc::string::String;
7
8use ed25519_dalek::{SigningKey, VerifyingKey};
9#[cfg(feature = "rand")]
10use rand_core::OsRng;
11use zeroize::Zeroizing;
12
13use crate::Error;
14
15#[derive(Debug)]
20pub struct StandardWallet {
21 signing_key: SigningKey,
23}
24
25impl StandardWallet {
26 #[cfg(feature = "rand")]
30 #[must_use]
31 pub fn generate() -> Self {
32 let signing_key = SigningKey::generate(&mut OsRng);
33 Self { signing_key }
34 }
35
36 #[must_use]
38 pub fn from_bytes(bytes: &[u8; 32]) -> Self {
39 let signing_key = SigningKey::from_bytes(bytes);
40 Self { signing_key }
41 }
42
43 pub fn from_hex(hex_key: &str) -> Result<Self, Error> {
49 let bytes = hex::decode(hex_key).map_err(|_| Error::InvalidHex)?;
50
51 if bytes.len() != 32 {
52 return Err(Error::Derivation(alloc::format!(
53 "expected 32 bytes, got {}",
54 bytes.len()
55 )));
56 }
57
58 let mut key_bytes = [0u8; 32];
59 key_bytes.copy_from_slice(&bytes);
60 Ok(Self::from_bytes(&key_bytes))
61 }
62
63 #[inline]
65 #[must_use]
66 pub fn address(&self) -> String {
67 let verifying_key: VerifyingKey = self.signing_key.verifying_key();
68 bs58::encode(verifying_key.as_bytes()).into_string()
69 }
70
71 #[inline]
73 #[must_use]
74 pub fn secret_bytes(&self) -> Zeroizing<[u8; 32]> {
75 Zeroizing::new(*self.signing_key.as_bytes())
76 }
77
78 #[inline]
80 #[must_use]
81 pub fn secret_hex(&self) -> Zeroizing<String> {
82 Zeroizing::new(hex::encode(self.signing_key.as_bytes()))
83 }
84
85 #[inline]
90 #[must_use]
91 pub fn keypair_base58(&self) -> Zeroizing<String> {
92 let verifying_key: VerifyingKey = self.signing_key.verifying_key();
93 let mut keypair_bytes = [0u8; 64];
94 keypair_bytes[..32].copy_from_slice(self.signing_key.as_bytes());
95 keypair_bytes[32..].copy_from_slice(verifying_key.as_bytes());
96 let encoded = bs58::encode(&keypair_bytes).into_string();
97 keypair_bytes.fill(0);
99 Zeroizing::new(encoded)
100 }
101
102 #[inline]
104 #[must_use]
105 pub fn pubkey_hex(&self) -> String {
106 let verifying_key: VerifyingKey = self.signing_key.verifying_key();
107 hex::encode(verifying_key.as_bytes())
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114
115 #[cfg(feature = "rand")]
116 #[test]
117 fn test_generate() {
118 let wallet = StandardWallet::generate();
119 let address = wallet.address();
120
121 assert!(address.len() >= 32 && address.len() <= 44);
123 }
124
125 #[test]
126 fn test_from_bytes() {
127 let key = [1u8; 32];
128 let wallet = StandardWallet::from_bytes(&key);
129 let address = wallet.address();
130
131 assert!(address.len() >= 32 && address.len() <= 44);
132 }
133
134 #[test]
135 fn test_from_hex() {
136 let hex_key = "0101010101010101010101010101010101010101010101010101010101010101";
137 let wallet = StandardWallet::from_hex(hex_key).unwrap();
138 let address = wallet.address();
139
140 assert!(address.len() >= 32 && address.len() <= 44);
141 }
142
143 #[test]
144 fn test_deterministic() {
145 let key = [42u8; 32];
146 let wallet1 = StandardWallet::from_bytes(&key);
147 let wallet2 = StandardWallet::from_bytes(&key);
148
149 assert_eq!(wallet1.address(), wallet2.address());
150 }
151}