stratus/eth/primitives/
address.rs

1use std::fmt::Display;
2use std::ops::Deref;
3use std::str::FromStr;
4
5use alloy_primitives::FixedBytes;
6use anyhow::anyhow;
7use display_json::DebugAsJson;
8use fake::Dummy;
9use fake::Faker;
10use hex_literal::hex;
11
12use crate::alias::RevmAddress;
13use crate::eth::primitives::LogTopic;
14
15/// Address of an Ethereum account (wallet or contract).
16#[derive(DebugAsJson, Clone, Copy, Default, Eq, PartialEq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize)]
17pub struct Address(pub FixedBytes<20>);
18
19impl Address {
20    // Special ETH address used in some contexts.
21    pub const ZERO: Address = Address(FixedBytes::ZERO);
22
23    /// Special address that receives the block reward.
24    pub const COINBASE: Address = Address(FixedBytes(hex!("00000000000000000000000000000000000000ff")));
25    pub const BRLC: Address = Address(FixedBytes(hex!("a9a55a81a4c085ec0c31585aed4cfb09d78dfd53")));
26
27    /// Creates a new address from the given bytes.
28    pub const fn new(bytes: [u8; 20]) -> Self {
29        Self(FixedBytes(bytes))
30    }
31
32    /// Checks if current address is the zero address.
33    pub fn is_zero(&self) -> bool {
34        self == &Self::ZERO
35    }
36
37    /// Checks if current address is the coinbase address.
38    pub fn is_coinbase(&self) -> bool {
39        self == &Self::COINBASE
40    }
41
42    /// Checks if current address should have their updates ignored.
43    ///
44    /// * Coinbase is ignored because we do not charge gas, otherwise it will have to be updated for every transaction.
45    /// * Not sure if zero address should be ignored or not.
46    pub fn is_ignored(&self) -> bool {
47        self.is_coinbase() || self.is_zero()
48    }
49}
50
51impl Display for Address {
52    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53        write!(f, "{}", const_hex::encode_prefixed(self.0))
54    }
55}
56
57impl Dummy<Faker> for Address {
58    fn dummy_with_rng<R: rand::Rng + ?Sized>(_: &Faker, rng: &mut R) -> Self {
59        Address(FixedBytes::random_with(rng))
60    }
61}
62
63impl Deref for Address {
64    type Target = FixedBytes<20>;
65    fn deref(&self) -> &Self::Target {
66        &self.0
67    }
68}
69
70// -----------------------------------------------------------------------------
71// Conversions: Other -> Self
72// -----------------------------------------------------------------------------
73
74impl From<FixedBytes<20>> for Address {
75    fn from(value: FixedBytes<20>) -> Self {
76        Self(value)
77    }
78}
79
80impl From<[u8; 20]> for Address {
81    fn from(value: [u8; 20]) -> Self {
82        Self(FixedBytes::from(value))
83    }
84}
85
86impl From<RevmAddress> for Address {
87    fn from(value: RevmAddress) -> Self {
88        Address(value.0.0.into())
89    }
90}
91
92impl From<LogTopic> for Address {
93    fn from(value: LogTopic) -> Self {
94        Self(FixedBytes::from_slice(&value.0.0[12..32]))
95    }
96}
97
98impl TryFrom<Vec<u8>> for Address {
99    type Error = anyhow::Error;
100
101    fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
102        if value.len() != 20 {
103            return Err(anyhow!("array of bytes to be converted to address must have exactly 20 bytes"));
104        }
105        Ok(Self(FixedBytes::from_slice(&value)))
106    }
107}
108
109// -----------------------------------------------------------------------------
110// Conversions: Self -> Other
111// -----------------------------------------------------------------------------
112/// Converts a hexadecimal string representation to an Address.
113///
114/// The input string can be with or without the "0x" prefix.
115/// If the string has an odd number of digits, a leading zero will be added.
116///
117/// # Examples
118///
119/// ```
120/// use std::str::FromStr;
121/// use stratus::eth::primitives::Address;
122///
123/// // With 0x prefix
124/// let addr1 = Address::from_str("0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266").unwrap();
125///
126/// // Without 0x prefix
127/// let addr2 = Address::from_str("f39fd6e51aad88f6f4ce6ab8827279cfffb92266").unwrap();
128///
129/// assert_eq!(addr1, addr2);
130/// ```
131impl FromStr for Address {
132    type Err = anyhow::Error;
133
134    fn from_str(s: &str) -> Result<Self, Self::Err> {
135        Ok(Self(FixedBytes::from_str(s)?))
136    }
137}
138
139impl From<Address> for FixedBytes<20> {
140    fn from(value: Address) -> Self {
141        value.0
142    }
143}
144
145impl From<Address> for RevmAddress {
146    fn from(value: Address) -> Self {
147        revm::primitives::Address(value.0.0.into())
148    }
149}
150
151impl From<Address> for LogTopic {
152    fn from(value: Address) -> Self {
153        let padding = FixedBytes::<12>::ZERO;
154        Self(padding.concat_const(value.0))
155    }
156}