1#![allow(dead_code)]
2use anyhow::{anyhow, Result};
7use crypto::{digest::Digest, sha3::Sha3};
8use regex::Regex;
9
10pub fn is_hex_string(value: String, length: usize) -> Result<bool> {
11 let re = Regex::new("^0x[0-9A-Fa-f]*$")?;
12 let is_hex = if !re.is_match(&value) {
13 false
14 } else if value.len() != 2 + 2 * length {
15 false
16 } else {
17 true
18 };
19 Ok(is_hex)
20}
21
22pub fn get_checksum_address(a: String) -> Result<String> {
24 if !is_hex_string(a.clone(), 20)? {
25 return Err(anyhow!("Invalid address. Not a hex address"));
26 }
27 let addr = a.trim_start_matches("0x").to_lowercase();
28 let address_hash = {
29 let mut hasher = Sha3::keccak256();
30 hasher.input(addr.as_bytes());
31 hasher.result_str()
32 };
33
34 Ok(addr
35 .char_indices()
36 .fold(String::from("0x"), |mut acc, (index, address_char)| {
37 let n = u16::from_str_radix(&address_hash[index..index + 1], 16).unwrap();
39
40 if n > 7 {
41 acc.push_str(&address_char.to_uppercase().to_string())
43 } else {
44 acc.push(address_char)
46 }
47
48 acc
49 }))
50}
51
52pub fn get_address(addr: String) -> Result<String> {
53 let re = Regex::new("^(0x)?[0-9a-fA-F]{40}$")?;
54 let mut prefixed_addr = String::from("0x");
55 let result: String;
56
57 if re.is_match(&addr) {
58 if &addr[..2] != "0x" {
60 prefixed_addr.push_str(&addr);
61 } else {
62 prefixed_addr = addr;
63 }
64
65 result = get_checksum_address(prefixed_addr.clone())?;
66
67 let checksum_re = Regex::new("([A-F].*[a-f])|([a-f].*[A-F])")?;
69
70 if checksum_re.is_match(&prefixed_addr) && result != prefixed_addr {
72 return Err(anyhow!(
73 "Bad address checksum for address {}",
74 prefixed_addr
75 ));
76 }
77 } else {
78 return Err(anyhow!("Invalid address"));
79 }
80
81 Ok(result)
82}
83
84pub fn is_address(addr: String) -> bool {
85 match get_address(addr) {
86 Ok(_) => true,
87 Err(_) => false,
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94 use anyhow::Result;
95
96 #[test]
97 fn test_is_address() {
98 let good = String::from("0xC0404ed740a69d11201f5eD297c5732F562c6E4e");
99 assert_eq!(is_address(good), true);
100
101 let bad = String::from("0xC0404ed740a69d11201fffr5y7c5732F562c6E4e");
103 assert_eq!(is_address(bad), false);
104 }
105
106 #[test]
107 fn test_get_address() -> Result<()> {
108 let good = String::from("C0404ed740a69d11201f5eD297c5732F562c6E4e");
110 let expected = String::from("0xC0404ed740a69d11201f5eD297c5732F562c6E4e");
111 assert_eq!(get_address(good)?, expected);
112
113 let mut bad = String::from("0xa54D3c09E34aC96807c1CC397404bF2B98DC4eFb");
115 let mut expected_err = String::from(format!("Bad address checksum for address {}", bad));
116 assert_eq!(get_address(bad).unwrap_err().to_string(), expected_err);
117
118 bad = String::from("c09E34aC96807c1CCZUUWS882SSS");
120 expected_err = String::from("Invalid address");
121 assert_eq!(get_address(bad).unwrap_err().to_string(), expected_err);
122
123 Ok(())
124 }
125
126 #[test]
127 fn test_get_checksum_address() -> Result<()> {
128 let bad_addr = String::from("0xzZzZ4ed740a69d11201f5eD297c5732F562c6E4e");
129 let expected_err = String::from("Invalid address. Not a hex address");
130 assert_eq!(
131 get_checksum_address(bad_addr).unwrap_err().to_string(),
132 expected_err
133 );
134
135 let good_addr = String::from("0xC0404ed740a69d11201f5eD297c5732F562c6E4e");
136 assert_eq!(get_checksum_address(good_addr.clone())?, good_addr);
137
138 Ok(())
139 }
140
141 #[test]
142 fn test_is_hex_string() -> Result<()> {
143 let good = String::from("0xC0404ed740a69d11201f5eD297c5732F562c6E4e");
144 assert_eq!(is_hex_string(good, 20)?, true);
145
146 let mut bad = String::from("0xC0404ed740a69d11201fffr5y7c5732F562c6E4e");
147 assert_eq!(is_hex_string(bad, 20)?, false);
148
149 bad = String::from(
151 "0xC0404ed740a69d11201f5eD297c5732F562c6E4eC0404ed740a69d11201f5eD297c5732F562c6E4e",
152 );
153 assert_eq!(is_hex_string(bad, 20)?, false);
154
155 Ok(())
156 }
157}