1use std::cmp::Ordering;
4use std::fmt::{Display, Formatter};
5use std::str::FromStr;
6
7use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
8use ethabi::Token;
9use ethabi::ethereum_types::{H160, U256 as ethUint};
10use eyre::{Context, eyre};
11use namada_macros::BorshDeserializer;
12#[cfg(feature = "migrations")]
13use namada_migrations::*;
14use serde::{Deserialize, Serialize};
15
16use crate::address::Address;
17use crate::borsh::BorshSerializeExt;
18use crate::eth_abi::Encode;
19use crate::ethereum_structs::Erc20Transfer;
20use crate::hash::Hash;
21use crate::keccak::KeccakHash;
22use crate::storage::{DbKeySeg, KeySeg};
23use crate::token::Amount;
24
25#[derive(
27 Copy,
28 Clone,
29 Debug,
30 Default,
31 Hash,
32 PartialEq,
33 Eq,
34 Serialize,
35 Deserialize,
36 BorshSerialize,
37 BorshDeserialize,
38 BorshDeserializer,
39 BorshSchema,
40)]
41#[repr(align(32))]
42pub struct Uint(pub [u64; 4]);
43
44impl PartialOrd for Uint {
45 #[inline]
46 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
47 Some(self.cmp(other))
48 }
49}
50
51impl Ord for Uint {
52 #[inline]
53 fn cmp(&self, other: &Self) -> Ordering {
54 ethUint(self.0).cmp(ðUint(other.0))
55 }
56}
57
58impl Uint {
59 pub fn to_bytes(self) -> [u8; 32] {
65 let mut bytes = [0; 32];
66 ethUint(self.0).to_big_endian(&mut bytes);
67 bytes
68 }
69
70 pub fn checked_increment(self) -> Option<Self> {
73 ethUint::from(self).checked_add(1.into()).map(Self::from)
74 }
75}
76
77impl Display for Uint {
78 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
79 ethUint(self.0).fmt(f)
80 }
81}
82
83impl Encode<1> for Uint {
84 fn tokenize(&self) -> [Token; 1] {
85 [Token::Uint(self.into())]
86 }
87}
88
89impl From<ethUint> for Uint {
90 fn from(value: ethUint) -> Self {
91 Self(value.0)
92 }
93}
94
95impl From<Uint> for ethUint {
96 fn from(value: Uint) -> Self {
97 Self(value.0)
98 }
99}
100
101impl From<&Uint> for ethUint {
102 fn from(value: &Uint) -> Self {
103 Self(value.0)
104 }
105}
106
107impl From<u64> for Uint {
108 fn from(value: u64) -> Self {
109 ethUint::from(value).into()
110 }
111}
112
113#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
116#[derive(
117 Copy,
118 Clone,
119 Debug,
120 PartialEq,
121 Eq,
122 PartialOrd,
123 Ord,
124 Hash,
125 Serialize,
126 Deserialize,
127 BorshSerialize,
128 BorshDeserialize,
129 BorshDeserializer,
130 BorshSchema,
131)]
132#[serde(try_from = "String")]
133#[serde(into = "String")]
134pub struct EthAddress(pub [u8; 20]);
135
136impl EthAddress {
137 pub fn to_canonical(&self) -> String {
141 format!("{:?}", ethabi::ethereum_types::Address::from(&self.0))
142 }
143}
144
145impl From<H160> for EthAddress {
146 fn from(H160(addr): H160) -> Self {
147 Self(addr)
148 }
149}
150
151impl From<EthAddress> for H160 {
152 fn from(EthAddress(addr): EthAddress) -> Self {
153 Self(addr)
154 }
155}
156
157impl Display for EthAddress {
158 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
159 write!(f, "{}", self.to_canonical())
160 }
161}
162
163impl FromStr for EthAddress {
164 type Err = eyre::Error;
165
166 fn from_str(s: &str) -> Result<Self, Self::Err> {
169 let h160 = ethabi::ethereum_types::Address::from_str(s)
170 .wrap_err_with(|| eyre!("couldn't parse Ethereum address {}", s))?;
171 Ok(Self(h160.into()))
172 }
173}
174
175impl TryFrom<String> for EthAddress {
176 type Error = eyre::Error;
177
178 fn try_from(string: String) -> Result<Self, eyre::Error> {
179 Self::from_str(string.as_ref())
180 }
181}
182
183impl From<EthAddress> for String {
184 fn from(addr: EthAddress) -> Self {
185 addr.to_string()
186 }
187}
188
189impl KeySeg for EthAddress {
190 fn parse(string: String) -> crate::storage::Result<Self> {
191 Self::from_str(string.as_str())
192 .map_err(|_| crate::storage::Error::ParseKeySeg(string))
193 }
194
195 fn raw(&self) -> String {
196 self.to_canonical()
197 }
198
199 fn to_db_key(&self) -> DbKeySeg {
200 DbKeySeg::StringSeg(self.raw())
201 }
202}
203
204pub trait GetEventNonce {
206 fn get_event_nonce(&self) -> Uint;
208}
209
210#[derive(
213 PartialEq,
214 Eq,
215 PartialOrd,
216 Hash,
217 Ord,
218 Clone,
219 Debug,
220 BorshSerialize,
221 BorshDeserialize,
222 BorshDeserializer,
223 BorshSchema,
224)]
225pub struct TransfersToNamada {
226 pub nonce: Uint,
228 pub transfers: Vec<TransferToNamada>,
230}
231
232impl GetEventNonce for TransfersToNamada {
233 #[inline]
234 fn get_event_nonce(&self) -> Uint {
235 self.nonce
236 }
237}
238
239impl From<TransfersToNamada> for EthereumEvent {
240 #[inline]
241 fn from(event: TransfersToNamada) -> Self {
242 let TransfersToNamada { nonce, transfers } = event;
243 Self::TransfersToNamada { nonce, transfers }
244 }
245}
246
247#[derive(
249 PartialEq,
250 Eq,
251 PartialOrd,
252 Hash,
253 Ord,
254 Clone,
255 Debug,
256 BorshSerialize,
257 BorshDeserialize,
258 BorshDeserializer,
259 BorshSchema,
260)]
261pub enum EthereumEvent {
264 TransfersToNamada {
267 nonce: Uint,
269 transfers: Vec<TransferToNamada>,
271 },
272 TransfersToEthereum {
275 nonce: Uint,
277 transfers: Vec<TransferToEthereum>,
279 relayer: Address,
282 },
283 ValidatorSetUpdate {
286 nonce: Uint,
288 bridge_validator_hash: KeccakHash,
290 governance_validator_hash: KeccakHash,
292 },
293}
294
295impl EthereumEvent {
296 pub fn hash(&self) -> Result<Hash, std::io::Error> {
298 let bytes = self.serialize_to_vec();
299 Ok(Hash::sha256(bytes))
300 }
301}
302
303#[derive(
305 Clone,
306 Debug,
307 PartialEq,
308 Eq,
309 PartialOrd,
310 Hash,
311 Ord,
312 BorshSerialize,
313 BorshDeserialize,
314 BorshDeserializer,
315 BorshSchema,
316)]
317pub struct TransferToNamada {
318 pub amount: Amount,
320 pub asset: EthAddress,
322 pub receiver: Address,
324}
325
326#[derive(
328 Clone,
329 Debug,
330 PartialEq,
331 Eq,
332 Hash,
333 PartialOrd,
334 Ord,
335 BorshSerialize,
336 BorshDeserialize,
337 BorshDeserializer,
338 BorshSchema,
339 Serialize,
340 Deserialize,
341)]
342pub struct TransferToEthereum {
343 pub amount: Amount,
345 pub asset: EthAddress,
347 pub receiver: EthAddress,
349 pub checksum: Hash,
356}
357
358impl From<Erc20Transfer> for TransferToEthereum {
359 #[inline]
360 fn from(transfer: Erc20Transfer) -> Self {
361 Self {
362 amount: {
363 let uint = {
364 use crate::uint::Uint as NamadaUint;
365 let mut num_buf = [0; 32];
366 transfer.amount.to_little_endian(&mut num_buf);
367 NamadaUint::from_little_endian(&num_buf)
368 };
369 Amount::from_uint(uint, 0).unwrap()
371 },
372 asset: EthAddress(transfer.from.0),
373 receiver: EthAddress(transfer.to.0),
374 checksum: Hash(transfer.data_digest),
375 }
376 }
377}
378
379#[cfg(test)]
380mod tests {
381
382 use super::*;
383
384 #[test]
385 fn test_eth_address_to_canonical() {
386 let canonical = testing::DAI_ERC20_ETH_ADDRESS.to_canonical();
387
388 assert_eq!(
389 testing::DAI_ERC20_ETH_ADDRESS_CHECKSUMMED.to_ascii_lowercase(),
390 canonical,
391 );
392 }
393
394 #[test]
395 fn test_eth_address_from_str() {
396 let addr =
397 EthAddress::from_str(testing::DAI_ERC20_ETH_ADDRESS_CHECKSUMMED)
398 .unwrap();
399
400 assert_eq!(testing::DAI_ERC20_ETH_ADDRESS, addr);
401 }
402
403 #[test]
404 fn test_eth_address_from_str_error() {
405 let result = EthAddress::from_str(
406 "arbitrary string which isn't an Ethereum address",
407 );
408
409 assert!(result.is_err());
410 }
411
412 #[test]
415 fn test_eth_address_serde_roundtrip() {
416 let addr =
417 EthAddress::from_str(testing::DAI_ERC20_ETH_ADDRESS_CHECKSUMMED)
418 .unwrap();
419 let serialized = serde_json::to_string(&addr).expect("Test failed");
420 assert_eq!(
421 serialized,
422 format!(
423 r#""{}""#,
424 testing::DAI_ERC20_ETH_ADDRESS_CHECKSUMMED.to_lowercase()
425 )
426 );
427 let deserialized: EthAddress =
428 serde_json::from_str(&serialized).expect("Test failed");
429 assert_eq!(addr, deserialized);
430 }
431}
432
433#[allow(missing_docs)]
434#[allow(clippy::arithmetic_side_effects)]
435#[cfg(any(test, feature = "testing", feature = "benches"))]
437pub mod testing {
438 use proptest::prop_compose;
439
440 use super::*;
441 use crate::token;
442
443 pub const DAI_ERC20_ETH_ADDRESS_CHECKSUMMED: &str =
444 "0x6B175474E89094C44Da98b954EedeAC495271d0F";
445 pub const DAI_ERC20_ETH_ADDRESS: EthAddress = EthAddress([
446 107, 23, 84, 116, 232, 144, 148, 196, 77, 169, 139, 149, 78, 237, 234,
447 196, 149, 39, 29, 15,
448 ]);
449 pub const USDC_ERC20_ETH_ADDRESS_CHECKSUMMED: &str =
450 "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";
451 pub const USDC_ERC20_ETH_ADDRESS: EthAddress = EthAddress([
452 160, 184, 105, 145, 198, 33, 139, 54, 193, 209, 157, 74, 46, 158, 176,
453 206, 54, 6, 235, 72,
454 ]);
455
456 impl std::ops::Add<u64> for Uint {
457 type Output = Self;
458
459 fn add(self, rhs: u64) -> Self::Output {
460 (ethUint(self.0) + rhs).into()
461 }
462 }
463
464 impl std::ops::Sub<u64> for Uint {
465 type Output = Self;
466
467 fn sub(self, rhs: u64) -> Self::Output {
468 (ethUint(self.0) - rhs).into()
469 }
470 }
471
472 pub fn arbitrary_eth_address() -> EthAddress {
473 DAI_ERC20_ETH_ADDRESS
474 }
475
476 pub fn arbitrary_nonce() -> Uint {
477 0.into()
478 }
479
480 pub fn arbitrary_keccak_hash() -> KeccakHash {
481 KeccakHash([0; 32])
482 }
483
484 pub fn arbitrary_amount() -> Amount {
485 Amount::from(1_000)
486 }
487
488 pub fn arbitrary_bonded_stake() -> token::Amount {
489 token::Amount::from(1_000)
490 }
491
492 pub fn arbitrary_single_transfer(
495 nonce: Uint,
496 receiver: Address,
497 ) -> EthereumEvent {
498 EthereumEvent::TransfersToNamada {
499 nonce,
500 transfers: vec![TransferToNamada {
501 amount: arbitrary_amount(),
502 asset: arbitrary_eth_address(),
503 receiver,
504 }],
505 }
506 }
507
508 prop_compose! {
509 pub fn arb_eth_address()(bytes: [u8; 20]) -> EthAddress {
511 EthAddress(bytes)
512 }
513 }
514}