tronz_primitives/
address.rs1use core::{fmt, str::FromStr};
8
9use alloy_primitives::keccak256;
10use k256::ecdsa::VerifyingKey;
11use serde::{Deserialize, Deserializer, Serialize, Serializer};
12
13use crate::error::AddressError;
14
15pub const ADDRESS_PREFIX: u8 = 0x41;
17
18pub const ADDRESS_LEN: usize = 21;
20
21pub const EVM_ADDRESS_LEN: usize = 20;
23
24#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
26pub struct Address([u8; ADDRESS_LEN]);
27
28impl Address {
29 pub const ZERO: Self = {
31 let mut bytes = [0u8; ADDRESS_LEN];
32 bytes[0] = ADDRESS_PREFIX;
33 Self(bytes)
34 };
35
36 pub fn from_bytes(bytes: [u8; ADDRESS_LEN]) -> Result<Self, AddressError> {
38 if bytes[0] != ADDRESS_PREFIX {
39 return Err(AddressError::BadPrefix(bytes[0]));
40 }
41 Ok(Self(bytes))
42 }
43
44 pub fn from_slice(slice: &[u8]) -> Result<Self, AddressError> {
46 let bytes: [u8; ADDRESS_LEN] = slice
47 .try_into()
48 .map_err(|_| AddressError::BadLength { expected: ADDRESS_LEN, got: slice.len() })?;
49 Self::from_bytes(bytes)
50 }
51
52 pub fn from_evm_bytes(evm: [u8; EVM_ADDRESS_LEN]) -> Self {
54 let mut bytes = [0u8; ADDRESS_LEN];
55 bytes[0] = ADDRESS_PREFIX;
56 bytes[1..].copy_from_slice(&evm);
57 Self(bytes)
58 }
59
60 pub fn from_public_key(key: &VerifyingKey) -> Self {
64 let point = key.to_encoded_point(false);
65 let hash = keccak256(&point.as_bytes()[1..]);
68 let mut evm = [0u8; EVM_ADDRESS_LEN];
69 evm.copy_from_slice(&hash[12..]);
70 Self::from_evm_bytes(evm)
71 }
72
73 pub fn from_base58(s: &str) -> Result<Self, AddressError> {
75 let decoded = bs58::decode(s).with_check(None).into_vec()?;
76 Self::from_slice(&decoded)
77 }
78
79 pub fn from_hex(s: &str) -> Result<Self, AddressError> {
83 let s = s.strip_prefix("0x").unwrap_or(s);
84 let bytes = hex::decode(s)?;
85 Self::from_slice(&bytes)
86 }
87
88 pub fn as_bytes(&self) -> &[u8; ADDRESS_LEN] {
90 &self.0
91 }
92
93 pub fn as_evm_bytes(&self) -> &[u8; EVM_ADDRESS_LEN] {
96 self.0[1..].try_into().expect("address body is always 20 bytes")
97 }
98
99 pub fn to_base58(&self) -> String {
101 bs58::encode(&self.0).with_check().into_string()
102 }
103
104 pub fn to_hex(&self) -> String {
106 hex::encode(self.0)
107 }
108}
109
110impl TryFrom<[u8; ADDRESS_LEN]> for Address {
111 type Error = AddressError;
112
113 fn try_from(bytes: [u8; ADDRESS_LEN]) -> Result<Self, Self::Error> {
114 Self::from_bytes(bytes)
115 }
116}
117
118impl TryFrom<&[u8]> for Address {
119 type Error = AddressError;
120
121 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
122 Self::from_slice(bytes)
123 }
124}
125
126impl From<Address> for [u8; ADDRESS_LEN] {
127 fn from(address: Address) -> Self {
128 address.0
129 }
130}
131
132impl From<&Address> for [u8; ADDRESS_LEN] {
133 fn from(address: &Address) -> Self {
134 address.0
135 }
136}
137
138impl AsRef<[u8]> for Address {
139 fn as_ref(&self) -> &[u8] {
140 &self.0
141 }
142}
143
144impl Default for Address {
145 fn default() -> Self {
146 Self::ZERO
147 }
148}
149
150impl fmt::Display for Address {
151 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152 f.write_str(&self.to_base58())
153 }
154}
155
156impl fmt::Debug for Address {
157 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158 write!(f, "Address({})", self.to_base58())
159 }
160}
161
162impl FromStr for Address {
163 type Err = AddressError;
164
165 fn from_str(s: &str) -> Result<Self, Self::Err> {
169 let hexish = s.strip_prefix("0x").unwrap_or(s);
170 let looks_hex =
171 hexish.len() == ADDRESS_LEN * 2 && hexish.bytes().all(|b| b.is_ascii_hexdigit());
172 if looks_hex { Self::from_hex(s) } else { Self::from_base58(s) }
173 }
174}
175
176impl From<Address> for alloy_primitives::Address {
179 fn from(a: Address) -> Self {
180 alloy_primitives::Address::from(*a.as_evm_bytes())
181 }
182}
183
184impl From<alloy_primitives::Address> for Address {
185 fn from(a: alloy_primitives::Address) -> Self {
187 Address::from_evm_bytes(a.into_array())
188 }
189}
190
191impl Serialize for Address {
194 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
195 serializer.serialize_str(&self.to_base58())
196 }
197}
198
199impl<'de> Deserialize<'de> for Address {
200 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
201 let s = String::deserialize(deserializer)?;
202 s.parse().map_err(serde::de::Error::custom)
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209
210 const B58: &str = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t";
212 const HEX: &str = "41a614f803b6fd780986a42c78ec9c7f77e6ded13c";
213
214 #[test]
215 fn base58_roundtrip() {
216 let a = Address::from_base58(B58).unwrap();
217 assert_eq!(a.to_base58(), B58);
218 assert_eq!(a.to_hex(), HEX);
219 }
220
221 #[test]
222 fn hex_roundtrip() {
223 let a = Address::from_hex(HEX).unwrap();
224 assert_eq!(a.to_base58(), B58);
225 }
226
227 #[test]
228 fn fromstr_detects_format() {
229 assert_eq!(B58.parse::<Address>().unwrap().to_hex(), HEX);
230 assert_eq!(HEX.parse::<Address>().unwrap().to_base58(), B58);
231 let with_0x = format!("0x{HEX}");
232 assert_eq!(with_0x.parse::<Address>().unwrap().to_base58(), B58);
233 }
234
235 #[test]
236 fn bad_prefix_rejected() {
237 let mut bytes = [0u8; ADDRESS_LEN];
238 bytes[0] = 0x42;
239 assert!(matches!(Address::from_bytes(bytes), Err(AddressError::BadPrefix(0x42))));
240 }
241
242 #[test]
243 fn alloy_bridge_roundtrip() {
244 let a = Address::from_base58(B58).unwrap();
245 let evm: alloy_primitives::Address = a.into();
246 assert_eq!(evm.as_slice(), a.as_evm_bytes());
247 let back: Address = evm.into();
248 assert_eq!(back, a);
249 }
250
251 #[test]
252 fn evm_bytes_strip_prefix() {
253 let a = Address::from_hex(HEX).unwrap();
254 assert_eq!(a.as_evm_bytes().len(), 20);
255 assert_eq!(&a.as_bytes()[1..], a.as_evm_bytes());
256 }
257
258 #[test]
259 fn standard_byte_conversions() {
260 let address = Address::from_hex(HEX).unwrap();
261 let bytes: [u8; ADDRESS_LEN] = address.into();
262
263 assert_eq!(Address::try_from(bytes).unwrap(), address);
264 assert_eq!(Address::try_from(bytes.as_slice()).unwrap(), address);
265 assert_eq!(<[u8; ADDRESS_LEN]>::from(&address), bytes);
266 assert_eq!(address.as_ref(), bytes.as_slice());
267 assert_eq!(Address::default(), Address::ZERO);
268 }
269}