1pub use alloy::primitives::Address;
2#[cfg(feature = "serde")]
3use serde::{Deserialize, Serialize};
4use std::ops::Deref;
5use std::str::FromStr;
6
7#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
34#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
35pub struct SqlAddress(Address);
36
37impl SqlAddress {
38 pub const ZERO: Self = SqlAddress(Address::ZERO);
43
44 pub fn new(bytes: [u8; 20]) -> Self {
55 SqlAddress(Address::new(bytes))
56 }
57
58 pub const fn new_from_address(addr: Address) -> Self {
71 SqlAddress(addr)
72 }
73
74 pub fn inner(&self) -> &Address {
79 &self.0
80 }
81
82 pub fn into_inner(self) -> Address {
84 self.0
85 }
86
87 pub fn from_slice(bytes: &[u8]) -> Self {
93 SqlAddress(Address::from_slice(bytes))
94 }
95}
96
97impl AsRef<Address> for SqlAddress {
98 fn as_ref(&self) -> &Address {
100 &self.0
101 }
102}
103
104impl Deref for SqlAddress {
105 type Target = Address;
106
107 fn deref(&self) -> &Self::Target {
112 &self.0
113 }
114}
115
116impl From<Address> for SqlAddress {
117 fn from(address: Address) -> Self {
119 SqlAddress(address)
120 }
121}
122
123impl From<SqlAddress> for Address {
124 fn from(sql_address: SqlAddress) -> Self {
126 sql_address.0
127 }
128}
129
130impl FromStr for SqlAddress {
131 type Err = <Address as FromStr>::Err;
132
133 fn from_str(s: &str) -> Result<Self, Self::Err> {
140 Ok(SqlAddress(s.parse()?))
141 }
142}
143
144impl std::fmt::Display for SqlAddress {
145 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
147 self.0.fmt(f)
148 }
149}
150
151impl Default for SqlAddress {
152 fn default() -> Self {
153 SqlAddress::ZERO
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160 use crate::sqladdress;
161 use alloy::primitives::Address;
162 use std::str::FromStr;
163
164 const TEST_ADDRESS_STR: &str = "0x742d35Cc6635C0532925a3b8D42cC72b5c2A9A1d";
165 const ZERO_ADDRESS_STR: &str = "0x0000000000000000000000000000000000000000";
166
167 #[test]
168 fn test_sql_address_creation() {
169 let addr = Address::ZERO;
171 let sql_addr = SqlAddress::from(addr);
172 assert_eq!(sql_addr.into_inner(), addr);
173
174 let sql_addr = SqlAddress::from_str(ZERO_ADDRESS_STR).unwrap();
176 assert_eq!(sql_addr.into_inner(), Address::ZERO);
177 }
178
179 #[test]
180 fn test_sql_address_conversions() {
181 let original_addr = TEST_ADDRESS_STR.parse::<Address>().unwrap();
182
183 let sql_addr = SqlAddress::from(original_addr);
185 let converted_back: Address = sql_addr.into();
186 assert_eq!(original_addr, converted_back);
187
188 let sql_addr = SqlAddress::from_str(TEST_ADDRESS_STR).unwrap();
190 let result_str = sql_addr.to_string();
191 assert_eq!(result_str.to_lowercase(), TEST_ADDRESS_STR.to_lowercase());
192 }
193
194 #[test]
195 fn test_sql_address_display() {
196 let sql_addr = SqlAddress::from_str(TEST_ADDRESS_STR).unwrap();
197 let displayed = format!("{}", sql_addr);
198 assert_eq!(displayed.to_lowercase(), TEST_ADDRESS_STR.to_lowercase());
200 }
201
202 #[test]
203 fn test_sql_address_deref() {
204 let sql_addr = SqlAddress::from_str(TEST_ADDRESS_STR).unwrap();
205
206 let _checksum = sql_addr.to_checksum(None);
208
209 let addr_ref: &Address = sql_addr.as_ref();
211 assert_eq!(addr_ref, sql_addr.inner());
212 }
213
214 #[test]
215 fn test_sql_address_equality() {
216 let addr1 = SqlAddress::from_str(TEST_ADDRESS_STR).unwrap();
217 let addr2 = SqlAddress::from_str(TEST_ADDRESS_STR).unwrap();
218 let addr3 = SqlAddress::from_str(ZERO_ADDRESS_STR).unwrap();
219
220 assert_eq!(addr1, addr2);
221 assert_ne!(addr1, addr3);
222 }
223
224 #[test]
225 fn test_sql_address_debug() {
226 let sql_addr = SqlAddress::from_str(TEST_ADDRESS_STR).unwrap();
227 let debug_str = format!("{:?}", sql_addr);
228 assert!(debug_str.contains("SqlAddress"));
229 }
230
231 #[test]
232 fn test_invalid_address() {
233 let invalid_addresses = vec![
234 "invalid",
235 "0x123", "0xgg42d35Cc6635C0532925a3b8D42cC72b5c2A9A1d", "", ];
239
240 for invalid_addr in invalid_addresses {
241 assert!(SqlAddress::from_str(invalid_addr).is_err());
242 }
243 }
244
245 #[cfg(feature = "serde")]
246 #[test]
247 fn test_sql_address_serde() {
248 let sql_addr = SqlAddress::from_str(TEST_ADDRESS_STR).unwrap();
249
250 let serialized = serde_json::to_string(&sql_addr).unwrap();
252 assert!(serialized.contains(TEST_ADDRESS_STR.to_lowercase().trim_start_matches("0x")));
253
254 let deserialized: SqlAddress = serde_json::from_str(&serialized).unwrap();
256 assert_eq!(sql_addr, deserialized);
257 }
258
259 #[cfg(feature = "serde")]
260 #[test]
261 fn test_sql_address_serde_with_various_formats() {
262 let test_cases = vec![
264 (TEST_ADDRESS_STR, true),
266 ("742d35Cc6635C0532925a3b8D42cC72b5c2A9A1d", true),
268 ("0x742d35cc6635c0532925a3b8d42cc72b5c2a9a1d", true),
270 ("0x742D35CC6635C0532925A3B8D42CC72B5C2A9A1D", true),
272 ("invalid", false),
274 ];
275
276 for (addr_str, should_succeed) in test_cases {
277 let result = SqlAddress::from_str(addr_str);
278 assert_eq!(
279 result.is_ok(),
280 should_succeed,
281 "Failed for address: {}",
282 addr_str
283 );
284
285 if should_succeed {
286 let sql_addr = result.unwrap();
287 let serialized = serde_json::to_string(&sql_addr).unwrap();
288 let deserialized: SqlAddress = serde_json::from_str(&serialized).unwrap();
289 assert_eq!(sql_addr, deserialized);
290 }
291 }
292 }
293
294 #[test]
295 fn test_sqladdress_macro() {
296 let addr1 = sqladdress!("0x742d35Cc6635C0532925a3b8D42cC72b5c2A9A1d");
298 let addr_from_str =
299 SqlAddress::from_str("0x742d35Cc6635C0532925a3b8D42cC72b5c2A9A1d").unwrap();
300 assert_eq!(addr1, addr_from_str);
301
302 let addr2 = sqladdress!("742d35Cc6635C0532925a3b8D42cC72b5c2A9A1d");
304 assert_eq!(addr2, addr_from_str);
305
306 let zero_addr = sqladdress!("0x0000000000000000000000000000000000000000");
308 let zero_from_str = SqlAddress::from_str(ZERO_ADDRESS_STR).unwrap();
309 assert_eq!(zero_addr, zero_from_str);
310
311 let addr = sqladdress!("0x742d35Cc6635C0532925a3b8D42cC72b5c2A9A1d");
313 let _inner = addr.inner();
314 let _string = addr.to_string();
315 let _display = format!("{}", addr);
316 let _debug = format!("{:?}", addr);
317 }
318
319 #[test]
320 fn test_sqladdress_macro_compile_time_validation() {
321 let _valid_addresses = [
323 sqladdress!("0x742d35Cc6635C0532925a3b8D42cC72b5c2A9A1d"),
324 sqladdress!("742d35Cc6635C0532925a3b8D42cC72b5c2A9A1d"),
325 sqladdress!("0x0000000000000000000000000000000000000000"),
326 sqladdress!("0xffffffffffffffffffffffffffffffffffffffff"),
327 ];
328
329 for addr in _valid_addresses.iter() {
331 assert_ne!(
332 *addr,
333 SqlAddress::from_str("0x1111111111111111111111111111111111111111").unwrap()
334 );
335 }
336 }
337
338 #[test]
339 fn test_sql_address_zero_constant() {
340 assert_eq!(
342 SqlAddress::ZERO.to_string(),
343 "0x0000000000000000000000000000000000000000"
344 );
345
346 let zero_from_str =
348 SqlAddress::from_str("0x0000000000000000000000000000000000000000").unwrap();
349 let zero_from_macro = sqladdress!("0x0000000000000000000000000000000000000000");
350 let zero_from_alloy = SqlAddress::ZERO;
351
352 assert_eq!(SqlAddress::ZERO, zero_from_str);
353 assert_eq!(SqlAddress::ZERO, zero_from_macro);
354 assert_eq!(SqlAddress::ZERO, zero_from_alloy);
355
356 assert_eq!(SqlAddress::ZERO.into_inner(), Address::ZERO);
358 assert_eq!(*SqlAddress::ZERO, Address::ZERO);
359
360 const ZERO_CONST: SqlAddress = SqlAddress::ZERO;
362 assert_eq!(ZERO_CONST, SqlAddress::ZERO);
363 }
364
365 #[test]
366 fn test_sql_address_hash() {
367 use std::collections::{HashMap, HashSet};
368
369 let addr1 = sqladdress!("0x742d35Cc6635C0532925a3b8D42cC72b5c2A9A1d");
370 let addr2 = sqladdress!("0x742d35Cc6635C0532925a3b8D42cC72b5c2A9A1d");
371 let addr3 = sqladdress!("0x1234567890123456789012345678901234567890");
372
373 use std::hash::{DefaultHasher, Hash, Hasher};
375
376 let mut hasher1 = DefaultHasher::new();
377 let mut hasher2 = DefaultHasher::new();
378 let mut hasher3 = DefaultHasher::new();
379
380 addr1.hash(&mut hasher1);
381 addr2.hash(&mut hasher2);
382 addr3.hash(&mut hasher3);
383
384 assert_eq!(hasher1.finish(), hasher2.finish());
385 assert_ne!(hasher1.finish(), hasher3.finish());
386
387 let mut address_set = HashSet::new();
389 address_set.insert(addr1);
390 address_set.insert(addr2); address_set.insert(addr3);
392
393 assert_eq!(address_set.len(), 2);
394 assert!(address_set.contains(&addr1));
395 assert!(address_set.contains(&addr2));
396 assert!(address_set.contains(&addr3));
397
398 let mut address_map = HashMap::new();
400 address_map.insert(addr1, "First address");
401 address_map.insert(addr2, "Same address"); address_map.insert(addr3, "Different address");
403
404 assert_eq!(address_map.len(), 2);
405 assert_eq!(address_map.get(&addr1), Some(&"Same address"));
406 assert_eq!(address_map.get(&addr2), Some(&"Same address"));
407 assert_eq!(address_map.get(&addr3), Some(&"Different address"));
408 }
409
410 #[test]
411 fn test_sql_address_hash_consistency_with_alloy_address() {
412 use std::hash::{DefaultHasher, Hash, Hasher};
413
414 fn calculate_hash<T: Hash>(t: &T) -> u64 {
415 let mut hasher = DefaultHasher::new();
416 t.hash(&mut hasher);
417 hasher.finish()
418 }
419
420 let test_addresses = [
421 "0x742d35Cc6635C0532925a3b8D42cC72b5c2A9A1d",
422 "0x0000000000000000000000000000000000000000",
423 "0xffffffffffffffffffffffffffffffffffffffff",
424 "0x1234567890123456789012345678901234567890",
425 ];
426
427 for addr_str in &test_addresses {
428 let alloy_addr = Address::from_str(addr_str).unwrap();
429 let sql_addr = SqlAddress::from_str(addr_str).unwrap();
430
431 let alloy_hash = calculate_hash(&alloy_addr);
432 let sql_hash = calculate_hash(&sql_addr);
433
434 assert_eq!(
436 alloy_hash, sql_hash,
437 "Hash mismatch for address {}: alloy={}, sql={}",
438 addr_str, alloy_hash, sql_hash
439 );
440 }
441
442 let original = Address::from_str(TEST_ADDRESS_STR).unwrap();
444 let sql_wrapped = SqlAddress::from(original);
445 let converted_back: Address = sql_wrapped.into();
446
447 assert_eq!(calculate_hash(&original), calculate_hash(&sql_wrapped));
448 assert_eq!(calculate_hash(&original), calculate_hash(&converted_back));
449 assert_eq!(
450 calculate_hash(&sql_wrapped),
451 calculate_hash(&converted_back)
452 );
453
454 assert_eq!(
456 calculate_hash(&Address::ZERO),
457 calculate_hash(&SqlAddress::ZERO)
458 );
459 }
460}