1use bech32::{self, FromBase32, ToBase32};
4
5use crate::errors::{NibiruError, NibiruResult};
6
7pub fn nibiru_bech32_to_eth_address(bech32_addr: &str) -> NibiruResult<String> {
32 let (hrp, data, _variant) = bech32::decode(bech32_addr)?;
34
35 if hrp != "nibi" {
37 return Err(NibiruError::InvalidBech32Prefix {
38 expected: "nibi".to_string(),
39 actual: hrp,
40 });
41 }
42
43 let bytes = Vec::<u8>::from_base32(&data)?;
45
46 if bytes.len() < 20 {
48 return Err(NibiruError::InvalidAddressLength);
49 }
50
51 let eth_addr = format!("0x{}", hex::encode(&bytes[..20]));
53 Ok(eth_addr)
54}
55
56pub fn eth_address_to_nibiru_bech32(eth_addr: &str) -> NibiruResult<String> {
80 let hex_str = eth_addr.strip_prefix("0x").unwrap_or(eth_addr);
82
83 if hex_str.len() != 40 {
85 return Err(NibiruError::InvalidEthAddress(format!(
86 "Ethereum address must be 20 bytes (40 hex chars), got {} chars",
87 hex_str.len()
88 )));
89 }
90
91 let bytes = hex::decode(hex_str)?;
93
94 if bytes.len() != 20 {
96 return Err(NibiruError::InvalidEthAddress(format!(
97 "Invalid Ethereum address length: expected 20 bytes, got {}",
98 bytes.len()
99 )));
100 }
101
102 let bech32_addr =
104 bech32::encode("nibi", bytes.to_base32(), bech32::Variant::Bech32)?;
105 Ok(bech32_addr)
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
113 fn test_nibiru_bech32_to_eth_address_valid() {
114 let bech32_addr = "nibi1gc24lt74ses9swkq6g7cug4e5y72p7e34jqgul";
116 let expected_eth = "0x46155fafd58660583ac0d23d8e22b9a13ca0fb31";
117
118 let result = nibiru_bech32_to_eth_address(bech32_addr).unwrap();
119 assert_eq!(result.to_lowercase(), expected_eth);
120 }
121
122 #[test]
123 fn test_nibiru_bech32_to_eth_address_invalid_prefix() {
124 let bech32_addr = "cosmos1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqnrql8a";
126
127 let result = nibiru_bech32_to_eth_address(bech32_addr);
128 match result {
129 Err(NibiruError::InvalidBech32Prefix { expected, actual }) => {
130 assert_eq!(expected, "nibi");
131 assert_eq!(actual, "cosmos");
132 }
133 _ => panic!("Expected InvalidBech32Prefix error, got: {:?}", result),
134 }
135 }
136
137 #[test]
138 fn test_nibiru_bech32_to_eth_address_invalid_bech32() {
139 let invalid_addr = "nibi1invalid!@#$";
140
141 let result = nibiru_bech32_to_eth_address(invalid_addr);
142 assert!(matches!(result, Err(NibiruError::Bech32Error(_))));
143 }
144
145 #[test]
146 fn test_nibiru_bech32_to_eth_address_length_validation() {
147 use bech32::ToBase32;
150
151 let short_data = vec![0u8; 10];
153 let short_addr = bech32::encode(
154 "nibi",
155 short_data.to_base32(),
156 bech32::Variant::Bech32,
157 )
158 .unwrap();
159
160 let result = nibiru_bech32_to_eth_address(&short_addr);
161 match result {
162 Err(NibiruError::InvalidAddressLength) => {}
163 _ => {
164 panic!("Expected InvalidAddressLength error, got: {:?}", result)
165 }
166 }
167 }
168
169 #[test]
170 fn test_nibiru_bech32_to_eth_address_case_sensitivity() {
171 let bech32_addr = "nibi1gc24lt74ses9swkq6g7cug4e5y72p7e34jqgul";
173 let result = nibiru_bech32_to_eth_address(bech32_addr).unwrap();
174
175 assert!(result.starts_with("0x"));
177 assert_eq!(
179 result.to_lowercase(),
180 "0x46155fafd58660583ac0d23d8e22b9a13ca0fb31"
181 );
182 }
183
184 #[test]
185 fn test_eth_address_to_nibiru_bech32_valid() {
186 let eth_addr = "0x46155fAfd58660583ac0d23d8E22B9A13Ca0fb31";
188 let expected_bech32 = "nibi1gc24lt74ses9swkq6g7cug4e5y72p7e34jqgul";
189
190 let result = eth_address_to_nibiru_bech32(eth_addr).unwrap();
191 assert_eq!(result, expected_bech32);
192 }
193
194 #[test]
195 fn test_eth_address_to_nibiru_bech32_without_prefix() {
196 let eth_addr = "46155fAfd58660583ac0d23d8E22B9A13Ca0fb31";
198 let expected_bech32 = "nibi1gc24lt74ses9swkq6g7cug4e5y72p7e34jqgul";
199
200 let result = eth_address_to_nibiru_bech32(eth_addr).unwrap();
201 assert_eq!(result, expected_bech32);
202 }
203
204 #[test]
205 fn test_eth_address_to_nibiru_bech32_lowercase() {
206 let eth_addr = "0x46155fafd58660583ac0d23d8e22b9a13ca0fb31";
208 let expected_bech32 = "nibi1gc24lt74ses9swkq6g7cug4e5y72p7e34jqgul";
209
210 let result = eth_address_to_nibiru_bech32(eth_addr).unwrap();
211 assert_eq!(result, expected_bech32);
212 }
213
214 #[test]
215 fn test_eth_address_to_nibiru_bech32_invalid_length() {
216 let short_addr = "0x46155fafd58660583ac0d23d8e22b9a13ca0fb";
218 let result = eth_address_to_nibiru_bech32(short_addr);
219 match result {
220 Err(NibiruError::InvalidEthAddress(msg)) => {
221 assert!(msg.contains("40 hex chars"));
222 }
223 _ => panic!("Expected InvalidEthAddress error"),
224 }
225
226 let long_addr = "0x46155fafd58660583ac0d23d8e22b9a13ca0fb3100";
228 let result = eth_address_to_nibiru_bech32(long_addr);
229 match result {
230 Err(NibiruError::InvalidEthAddress(msg)) => {
231 assert!(msg.contains("40 hex chars"));
232 }
233 _ => panic!("Expected InvalidEthAddress error"),
234 }
235 }
236
237 #[test]
238 fn test_eth_address_to_nibiru_bech32_invalid_hex() {
239 let invalid_addr = "0x46155fXXd58660583ac0d23d8e22b9a13ca0fb31";
241 let result = eth_address_to_nibiru_bech32(invalid_addr);
242 assert!(matches!(result, Err(NibiruError::HexError(_))));
243 }
244
245 #[test]
246 fn test_round_trip_conversion() {
247 let original_bech32 = "nibi1gc24lt74ses9swkq6g7cug4e5y72p7e34jqgul";
249
250 let eth_addr = nibiru_bech32_to_eth_address(original_bech32).unwrap();
252
253 let result_bech32 = eth_address_to_nibiru_bech32(ð_addr).unwrap();
255
256 assert_eq!(original_bech32, result_bech32);
257 }
258
259 #[test]
260 fn test_multiple_round_trips() {
261 use bech32::ToBase32;
264
265 let test_bytes = vec![
266 vec![0u8; 20], vec![255u8; 20], vec![
269 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
270 19, 20,
271 ], ];
273
274 for bytes in test_bytes {
275 let original_bech32 = bech32::encode(
277 "nibi",
278 bytes.to_base32(),
279 bech32::Variant::Bech32,
280 )
281 .unwrap();
282
283 let eth_addr =
285 nibiru_bech32_to_eth_address(&original_bech32).unwrap();
286
287 let result_bech32 = eth_address_to_nibiru_bech32(ð_addr).unwrap();
289
290 assert_eq!(
291 original_bech32, result_bech32,
292 "Round trip failed for address"
293 );
294 }
295 }
296}