stratus/eth/primitives/
address.rs

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