alloy_primitives/bits/
address.rs

1use crate::{aliases::U160, utils::keccak256, FixedBytes};
2use alloc::{
3    borrow::Borrow,
4    string::{String, ToString},
5};
6use core::{fmt, mem::MaybeUninit, str};
7
8/// Error type for address checksum validation.
9#[derive(Clone, Copy, Debug)]
10pub enum AddressError {
11    /// Error while decoding hex.
12    Hex(hex::FromHexError),
13
14    /// Invalid ERC-55 checksum.
15    InvalidChecksum,
16}
17
18impl From<hex::FromHexError> for AddressError {
19    #[inline]
20    fn from(value: hex::FromHexError) -> Self {
21        Self::Hex(value)
22    }
23}
24
25impl core::error::Error for AddressError {
26    #[inline]
27    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
28        match self {
29            #[cfg(any(feature = "std", not(feature = "hex-compat")))]
30            Self::Hex(err) => Some(err),
31            _ => None,
32        }
33    }
34}
35
36impl fmt::Display for AddressError {
37    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38        match self {
39            Self::Hex(err) => err.fmt(f),
40            Self::InvalidChecksum => f.write_str("Bad address checksum"),
41        }
42    }
43}
44
45wrap_fixed_bytes!(
46    // we implement Display with the checksum, so we don't derive it
47    extra_derives: [],
48    /// An Ethereum address, 20 bytes in length.
49    ///
50    /// This type is separate from [`B160`](crate::B160) / [`FixedBytes<20>`]
51    /// and is declared with the [`wrap_fixed_bytes!`] macro. This allows us
52    /// to implement address-specific functionality.
53    ///
54    /// The main difference with the generic [`FixedBytes`] implementation is that
55    /// [`Display`] formats the address using its [EIP-55] checksum
56    /// ([`to_checksum`]).
57    /// Use [`Debug`] to display the raw bytes without the checksum.
58    ///
59    /// [EIP-55]: https://eips.ethereum.org/EIPS/eip-55
60    /// [`Debug`]: fmt::Debug
61    /// [`Display`]: fmt::Display
62    /// [`to_checksum`]: Address::to_checksum
63    ///
64    /// # Examples
65    ///
66    /// Parsing and formatting:
67    ///
68    /// ```
69    /// use alloy_primitives::{address, Address};
70    ///
71    /// let checksummed = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045";
72    /// let expected = address!("0xd8da6bf26964af9d7eed9e03e53415d37aa96045");
73    /// let address = Address::parse_checksummed(checksummed, None).expect("valid checksum");
74    /// assert_eq!(address, expected);
75    ///
76    /// // Format the address with the checksum
77    /// assert_eq!(address.to_string(), checksummed);
78    /// assert_eq!(address.to_checksum(None), checksummed);
79    ///
80    /// // Format the compressed checksummed address
81    /// assert_eq!(format!("{address:#}"), "0xd8dA…6045");
82    ///
83    /// // Format the address without the checksum
84    /// assert_eq!(format!("{address:?}"), "0xd8da6bf26964af9d7eed9e03e53415d37aa96045");
85    /// ```
86    pub struct Address<20>;
87);
88
89impl From<U160> for Address {
90    #[inline]
91    fn from(value: U160) -> Self {
92        Self(FixedBytes(value.to_be_bytes()))
93    }
94}
95
96impl From<Address> for U160 {
97    #[inline]
98    fn from(value: Address) -> Self {
99        Self::from_be_bytes(value.0 .0)
100    }
101}
102
103impl fmt::Display for Address {
104    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105        let checksum = self.to_checksum_buffer(None);
106        let checksum = checksum.as_str();
107        if f.alternate() {
108            // If the alternate flag is set, use middle-out compression
109            // "0x" + first 4 bytes + "…" + last 4 bytes
110            f.write_str(&checksum[..6])?;
111            f.write_str("…")?;
112            f.write_str(&checksum[38..])
113        } else {
114            f.write_str(checksum)
115        }
116    }
117}
118
119impl Address {
120    /// Creates an Ethereum address from an EVM word's upper 20 bytes
121    /// (`word[12..]`).
122    ///
123    /// # Examples
124    ///
125    /// ```
126    /// # use alloy_primitives::{address, b256, Address};
127    /// let word = b256!("0x000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa96045");
128    /// assert_eq!(Address::from_word(word), address!("0xd8da6bf26964af9d7eed9e03e53415d37aa96045"));
129    /// ```
130    #[inline]
131    #[must_use]
132    pub fn from_word(word: FixedBytes<32>) -> Self {
133        Self(FixedBytes(word[12..].try_into().unwrap()))
134    }
135
136    /// Left-pads the address to 32 bytes (EVM word size).
137    ///
138    /// # Examples
139    ///
140    /// ```
141    /// # use alloy_primitives::{address, b256, Address};
142    /// assert_eq!(
143    ///     address!("0xd8da6bf26964af9d7eed9e03e53415d37aa96045").into_word(),
144    ///     b256!("0x000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa96045"),
145    /// );
146    /// ```
147    #[inline]
148    #[must_use]
149    pub fn into_word(&self) -> FixedBytes<32> {
150        let mut word = [0; 32];
151        word[12..].copy_from_slice(self.as_slice());
152        FixedBytes(word)
153    }
154
155    /// Parse an Ethereum address, verifying its [EIP-55] checksum.
156    ///
157    /// You can optionally specify an [EIP-155 chain ID] to check the address
158    /// using [EIP-1191].
159    ///
160    /// [EIP-55]: https://eips.ethereum.org/EIPS/eip-55
161    /// [EIP-155 chain ID]: https://eips.ethereum.org/EIPS/eip-155
162    /// [EIP-1191]: https://eips.ethereum.org/EIPS/eip-1191
163    ///
164    /// # Errors
165    ///
166    /// This method returns an error if the provided string does not match the
167    /// expected checksum.
168    ///
169    /// # Examples
170    ///
171    /// ```
172    /// # use alloy_primitives::{address, Address};
173    /// let checksummed = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045";
174    /// let address = Address::parse_checksummed(checksummed, None).unwrap();
175    /// let expected = address!("0xd8da6bf26964af9d7eed9e03e53415d37aa96045");
176    /// assert_eq!(address, expected);
177    /// ```
178    pub fn parse_checksummed<S: AsRef<str>>(
179        s: S,
180        chain_id: Option<u64>,
181    ) -> Result<Self, AddressError> {
182        fn parse_checksummed(s: &str, chain_id: Option<u64>) -> Result<Address, AddressError> {
183            // checksummed addresses always start with the "0x" prefix
184            if !s.starts_with("0x") {
185                return Err(AddressError::Hex(hex::FromHexError::InvalidStringLength));
186            }
187
188            let address: Address = s.parse()?;
189            if s == address.to_checksum_buffer(chain_id).as_str() {
190                Ok(address)
191            } else {
192                Err(AddressError::InvalidChecksum)
193            }
194        }
195
196        parse_checksummed(s.as_ref(), chain_id)
197    }
198
199    /// Encodes an Ethereum address to its [EIP-55] checksum into a heap-allocated string.
200    ///
201    /// You can optionally specify an [EIP-155 chain ID] to encode the address
202    /// using [EIP-1191].
203    ///
204    /// [EIP-55]: https://eips.ethereum.org/EIPS/eip-55
205    /// [EIP-155 chain ID]: https://eips.ethereum.org/EIPS/eip-155
206    /// [EIP-1191]: https://eips.ethereum.org/EIPS/eip-1191
207    ///
208    /// # Examples
209    ///
210    /// ```
211    /// # use alloy_primitives::{address, Address};
212    /// let address = address!("0xd8da6bf26964af9d7eed9e03e53415d37aa96045");
213    ///
214    /// let checksummed: String = address.to_checksum(None);
215    /// assert_eq!(checksummed, "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045");
216    ///
217    /// let checksummed: String = address.to_checksum(Some(1));
218    /// assert_eq!(checksummed, "0xD8Da6bf26964Af9d7EEd9e03e53415d37AA96045");
219    /// ```
220    #[inline]
221    #[must_use]
222    pub fn to_checksum(&self, chain_id: Option<u64>) -> String {
223        self.to_checksum_buffer(chain_id).as_str().into()
224    }
225
226    /// Encodes an Ethereum address to its [EIP-55] checksum into the given buffer.
227    ///
228    /// For convenience, the buffer is returned as a `&mut str`, as the bytes
229    /// are guaranteed to be valid UTF-8.
230    ///
231    /// You can optionally specify an [EIP-155 chain ID] to encode the address
232    /// using [EIP-1191].
233    ///
234    /// [EIP-55]: https://eips.ethereum.org/EIPS/eip-55
235    /// [EIP-155 chain ID]: https://eips.ethereum.org/EIPS/eip-155
236    /// [EIP-1191]: https://eips.ethereum.org/EIPS/eip-1191
237    ///
238    /// # Panics
239    ///
240    /// Panics if `buf` is not exactly 42 bytes long.
241    ///
242    /// # Examples
243    ///
244    /// ```
245    /// # use alloy_primitives::{address, Address};
246    /// let address = address!("0xd8da6bf26964af9d7eed9e03e53415d37aa96045");
247    /// let mut buf = [0; 42];
248    ///
249    /// let checksummed: &mut str = address.to_checksum_raw(&mut buf, None);
250    /// assert_eq!(checksummed, "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045");
251    ///
252    /// let checksummed: &mut str = address.to_checksum_raw(&mut buf, Some(1));
253    /// assert_eq!(checksummed, "0xD8Da6bf26964Af9d7EEd9e03e53415d37AA96045");
254    /// ```
255    #[inline]
256    #[must_use]
257    pub fn to_checksum_raw<'a>(&self, buf: &'a mut [u8], chain_id: Option<u64>) -> &'a mut str {
258        let buf: &mut [u8; 42] = buf.try_into().expect("buffer must be exactly 42 bytes long");
259        self.to_checksum_inner(buf, chain_id);
260        // SAFETY: All bytes in the buffer are valid UTF-8.
261        unsafe { str::from_utf8_unchecked_mut(buf) }
262    }
263
264    /// Encodes an Ethereum address to its [EIP-55] checksum into a stack-allocated buffer.
265    ///
266    /// You can optionally specify an [EIP-155 chain ID] to encode the address
267    /// using [EIP-1191].
268    ///
269    /// [EIP-55]: https://eips.ethereum.org/EIPS/eip-55
270    /// [EIP-155 chain ID]: https://eips.ethereum.org/EIPS/eip-155
271    /// [EIP-1191]: https://eips.ethereum.org/EIPS/eip-1191
272    ///
273    /// # Examples
274    ///
275    /// ```
276    /// # use alloy_primitives::{address, Address, AddressChecksumBuffer};
277    /// let address = address!("0xd8da6bf26964af9d7eed9e03e53415d37aa96045");
278    ///
279    /// let mut buffer: AddressChecksumBuffer = address.to_checksum_buffer(None);
280    /// assert_eq!(buffer.as_str(), "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045");
281    ///
282    /// let checksummed: &str = buffer.format(&address, Some(1));
283    /// assert_eq!(checksummed, "0xD8Da6bf26964Af9d7EEd9e03e53415d37AA96045");
284    /// ```
285    #[inline]
286    pub fn to_checksum_buffer(&self, chain_id: Option<u64>) -> AddressChecksumBuffer {
287        // SAFETY: The buffer is initialized by `format`.
288        let mut buf = unsafe { AddressChecksumBuffer::new() };
289        buf.format(self, chain_id);
290        buf
291    }
292
293    // https://eips.ethereum.org/EIPS/eip-55
294    // > In English, convert the address to hex, but if the `i`th digit is a letter (ie. it’s one of
295    // > `abcdef`) print it in uppercase if the `4*i`th bit of the hash of the lowercase hexadecimal
296    // > address is 1 otherwise print it in lowercase.
297    //
298    // https://eips.ethereum.org/EIPS/eip-1191
299    // > [...] If the chain id passed to the function belongs to a network that opted for using this
300    // > checksum variant, prefix the address with the chain id and the `0x` separator before
301    // > calculating the hash. [...]
302    #[allow(clippy::wrong_self_convention)]
303    fn to_checksum_inner(&self, buf: &mut [u8; 42], chain_id: Option<u64>) {
304        buf[0] = b'0';
305        buf[1] = b'x';
306        hex::encode_to_slice(self, &mut buf[2..]).unwrap();
307
308        let mut hasher = crate::Keccak256::new();
309        match chain_id {
310            Some(chain_id) => {
311                hasher.update(itoa::Buffer::new().format(chain_id).as_bytes());
312                // Clippy suggests an unnecessary copy.
313                #[allow(clippy::needless_borrows_for_generic_args)]
314                hasher.update(&*buf);
315            }
316            None => hasher.update(&buf[2..]),
317        }
318        let hash = hasher.finalize();
319
320        for (i, out) in buf[2..].iter_mut().enumerate() {
321            // This is made branchless for easier vectorization.
322            // Get the i-th nibble of the hash.
323            let hash_nibble = (hash[i / 2] >> (4 * (1 - i % 2))) & 0xf;
324            // Make the character ASCII uppercase if it's a hex letter and the hash nibble is >= 8.
325            // We can use a simpler comparison for checking if the character is a hex letter because
326            // we know `out` is a hex-encoded character (`b'0'..=b'9' | b'a'..=b'f'`).
327            *out ^= 0b0010_0000 * ((*out >= b'a') & (hash_nibble >= 8)) as u8;
328        }
329    }
330
331    /// Computes the `create` address for this address and nonce:
332    ///
333    /// `keccak256(rlp([sender, nonce]))[12:]`
334    ///
335    /// # Examples
336    ///
337    /// ```
338    /// # use alloy_primitives::{address, Address};
339    /// let sender = address!("0xb20a608c624Ca5003905aA834De7156C68b2E1d0");
340    ///
341    /// let expected = address!("0x00000000219ab540356cBB839Cbe05303d7705Fa");
342    /// assert_eq!(sender.create(0), expected);
343    ///
344    /// let expected = address!("0xe33c6e89e69d085897f98e92b06ebd541d1daa99");
345    /// assert_eq!(sender.create(1), expected);
346    /// ```
347    #[cfg(feature = "rlp")]
348    #[inline]
349    #[must_use]
350    pub fn create(&self, nonce: u64) -> Self {
351        use alloy_rlp::{Encodable, EMPTY_LIST_CODE, EMPTY_STRING_CODE};
352
353        // max u64 encoded length is `1 + u64::BYTES`
354        const MAX_LEN: usize = 1 + (1 + 20) + 9;
355
356        let len = 22 + nonce.length();
357        debug_assert!(len <= MAX_LEN);
358
359        let mut out = [0u8; MAX_LEN];
360
361        // list header
362        // minus 1 to account for the list header itself
363        out[0] = EMPTY_LIST_CODE + len as u8 - 1;
364
365        // address header + address
366        out[1] = EMPTY_STRING_CODE + 20;
367        out[2..22].copy_from_slice(self.as_slice());
368
369        // nonce
370        nonce.encode(&mut &mut out[22..]);
371
372        let hash = keccak256(&out[..len]);
373        Self::from_word(hash)
374    }
375
376    /// Computes the `CREATE2` address of a smart contract as specified in
377    /// [EIP-1014]:
378    ///
379    /// `keccak256(0xff ++ address ++ salt ++ keccak256(init_code))[12:]`
380    ///
381    /// The `init_code` is the code that, when executed, produces the runtime
382    /// bytecode that will be placed into the state, and which typically is used
383    /// by high level languages to implement a ‘constructor’.
384    ///
385    /// [EIP-1014]: https://eips.ethereum.org/EIPS/eip-1014
386    ///
387    /// # Examples
388    ///
389    /// ```
390    /// # use alloy_primitives::{address, b256, bytes, Address};
391    /// let address = address!("0x8ba1f109551bD432803012645Ac136ddd64DBA72");
392    /// let salt = b256!("0x7c5ea36004851c764c44143b1dcb59679b11c9a68e5f41497f6cf3d480715331");
393    /// let init_code = bytes!("6394198df16000526103ff60206004601c335afa6040516060f3");
394    /// let expected = address!("0x533ae9d683B10C02EbDb05471642F85230071FC3");
395    /// assert_eq!(address.create2_from_code(salt, init_code), expected);
396    /// ```
397    #[must_use]
398    pub fn create2_from_code<S, C>(&self, salt: S, init_code: C) -> Self
399    where
400        // not `AsRef` because `[u8; N]` does not implement `AsRef<[u8; N]>`
401        S: Borrow<[u8; 32]>,
402        C: AsRef<[u8]>,
403    {
404        self._create2(salt.borrow(), &keccak256(init_code.as_ref()).0)
405    }
406
407    /// Computes the `CREATE2` address of a smart contract as specified in
408    /// [EIP-1014], taking the pre-computed hash of the init code as input:
409    ///
410    /// `keccak256(0xff ++ address ++ salt ++ init_code_hash)[12:]`
411    ///
412    /// The `init_code` is the code that, when executed, produces the runtime
413    /// bytecode that will be placed into the state, and which typically is used
414    /// by high level languages to implement a ‘constructor’.
415    ///
416    /// [EIP-1014]: https://eips.ethereum.org/EIPS/eip-1014
417    ///
418    /// # Examples
419    ///
420    /// ```
421    /// # use alloy_primitives::{address, b256, Address};
422    /// let address = address!("0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f");
423    /// let salt = b256!("0x2b2f5776e38002e0c013d0d89828fdb06fee595ea2d5ed4b194e3883e823e350");
424    /// let init_code_hash =
425    ///     b256!("0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f");
426    /// let expected = address!("0x0d4a11d5EEaaC28EC3F61d100daF4d40471f1852");
427    /// assert_eq!(address.create2(salt, init_code_hash), expected);
428    /// ```
429    #[must_use]
430    pub fn create2<S, H>(&self, salt: S, init_code_hash: H) -> Self
431    where
432        // not `AsRef` because `[u8; N]` does not implement `AsRef<[u8; N]>`
433        S: Borrow<[u8; 32]>,
434        H: Borrow<[u8; 32]>,
435    {
436        self._create2(salt.borrow(), init_code_hash.borrow())
437    }
438
439    // non-generic inner function
440    fn _create2(&self, salt: &[u8; 32], init_code_hash: &[u8; 32]) -> Self {
441        // note: creating a temporary buffer and copying everything over performs
442        // much better than calling `Keccak::update` multiple times
443        let mut bytes = [0; 85];
444        bytes[0] = 0xff;
445        bytes[1..21].copy_from_slice(self.as_slice());
446        bytes[21..53].copy_from_slice(salt);
447        bytes[53..85].copy_from_slice(init_code_hash);
448        let hash = keccak256(bytes);
449        Self::from_word(hash)
450    }
451
452    /// Computes the address created by the `EOFCREATE` opcode, where `self` is the sender.
453    ///
454    /// The address is calculated as `keccak256(0xff || sender32 || salt)[12:]`, where sender32 is
455    /// the sender address left-padded to 32 bytes with zeros.
456    ///
457    /// See [EIP-7620](https://eips.ethereum.org/EIPS/eip-7620) for more details.
458    ///
459    /// <div class="warning">
460    /// This function's stability is not guaranteed. It may change in the future as the EIP is
461    /// not yet accepted.
462    /// </div>
463    ///
464    /// # Examples
465    ///
466    /// ```
467    /// # use alloy_primitives::{address, b256, Address};
468    /// let address = address!("0xb20a608c624Ca5003905aA834De7156C68b2E1d0");
469    /// let salt = b256!("0x7c5ea36004851c764c44143b1dcb59679b11c9a68e5f41497f6cf3d480715331");
470    /// // Create an address using CREATE_EOF
471    /// let eof_address = address.create_eof(salt);
472    /// ```
473    #[must_use]
474    #[doc(alias = "eof_create")]
475    pub fn create_eof<S>(&self, salt: S) -> Self
476    where
477        // not `AsRef` because `[u8; N]` does not implement `AsRef<[u8; N]>`
478        S: Borrow<[u8; 32]>,
479    {
480        self._create_eof(salt.borrow())
481    }
482
483    // non-generic inner function
484    fn _create_eof(&self, salt: &[u8; 32]) -> Self {
485        let mut buffer = [0; 65];
486        buffer[0] = 0xff;
487        // 1..13 is zero pad (already initialized to 0)
488        buffer[13..33].copy_from_slice(self.as_slice());
489        buffer[33..].copy_from_slice(salt);
490        Self::from_word(keccak256(buffer))
491    }
492
493    /// Instantiate by hashing public key bytes.
494    ///
495    /// # Panics
496    ///
497    /// If the input is not exactly 64 bytes
498    pub fn from_raw_public_key(pubkey: &[u8]) -> Self {
499        assert_eq!(pubkey.len(), 64, "raw public key must be 64 bytes");
500        let digest = keccak256(pubkey);
501        Self::from_slice(&digest[12..])
502    }
503
504    /// Converts an ECDSA verifying key to its corresponding Ethereum address.
505    #[inline]
506    #[cfg(feature = "k256")]
507    #[doc(alias = "from_verifying_key")]
508    pub fn from_public_key(pubkey: &k256::ecdsa::VerifyingKey) -> Self {
509        use k256::elliptic_curve::sec1::ToEncodedPoint;
510        let affine: &k256::AffinePoint = pubkey.as_ref();
511        let encoded = affine.to_encoded_point(false);
512        Self::from_raw_public_key(&encoded.as_bytes()[1..])
513    }
514
515    /// Converts an ECDSA signing key to its corresponding Ethereum address.
516    #[inline]
517    #[cfg(feature = "k256")]
518    #[doc(alias = "from_signing_key")]
519    pub fn from_private_key(private_key: &k256::ecdsa::SigningKey) -> Self {
520        Self::from_public_key(private_key.verifying_key())
521    }
522}
523
524/// Stack-allocated buffer for efficiently computing address checksums.
525///
526/// See [`Address::to_checksum_buffer`] for more information.
527#[must_use]
528#[allow(missing_copy_implementations)]
529#[derive(Clone)]
530pub struct AddressChecksumBuffer(MaybeUninit<[u8; 42]>);
531
532impl fmt::Debug for AddressChecksumBuffer {
533    #[inline]
534    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
535        self.as_str().fmt(f)
536    }
537}
538
539impl fmt::Display for AddressChecksumBuffer {
540    #[inline]
541    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
542        self.as_str().fmt(f)
543    }
544}
545
546impl AddressChecksumBuffer {
547    /// Creates a new buffer.
548    ///
549    /// # Safety
550    ///
551    /// The buffer must be initialized with [`format`](Self::format) before use.
552    /// Prefer [`Address::to_checksum_buffer`] instead.
553    #[inline]
554    pub const unsafe fn new() -> Self {
555        Self(MaybeUninit::uninit())
556    }
557
558    /// Calculates the checksum of an address into the buffer.
559    ///
560    /// See [`Address::to_checksum_buffer`] for more information.
561    #[inline]
562    pub fn format(&mut self, address: &Address, chain_id: Option<u64>) -> &mut str {
563        address.to_checksum_inner(unsafe { self.0.assume_init_mut() }, chain_id);
564        self.as_mut_str()
565    }
566
567    /// Returns the checksum of a formatted address.
568    #[inline]
569    pub const fn as_str(&self) -> &str {
570        unsafe { str::from_utf8_unchecked(self.0.assume_init_ref()) }
571    }
572
573    /// Returns the checksum of a formatted address.
574    #[inline]
575    pub fn as_mut_str(&mut self) -> &mut str {
576        unsafe { str::from_utf8_unchecked_mut(self.0.assume_init_mut()) }
577    }
578
579    /// Returns the checksum of a formatted address.
580    #[inline]
581    #[allow(clippy::inherent_to_string_shadow_display)]
582    pub fn to_string(&self) -> String {
583        self.as_str().to_string()
584    }
585
586    /// Returns the backing buffer.
587    #[inline]
588    pub const fn into_inner(self) -> [u8; 42] {
589        unsafe { self.0.assume_init() }
590    }
591}
592
593#[cfg(test)]
594mod tests {
595    use super::*;
596    use crate::hex;
597
598    #[test]
599    fn parse() {
600        let expected = hex!("0102030405060708090a0b0c0d0e0f1011121314");
601        assert_eq!(
602            "0102030405060708090a0b0c0d0e0f1011121314".parse::<Address>().unwrap().into_array(),
603            expected
604        );
605        assert_eq!(
606            "0x0102030405060708090a0b0c0d0e0f1011121314".parse::<Address>().unwrap(),
607            expected
608        );
609    }
610
611    // https://eips.ethereum.org/EIPS/eip-55
612    #[test]
613    fn checksum() {
614        let addresses = [
615            // All caps
616            "0x52908400098527886E0F7030069857D2E4169EE7",
617            "0x8617E340B3D01FA5F11F306F4090FD50E238070D",
618            // All Lower
619            "0xde709f2102306220921060314715629080e2fb77",
620            "0x27b1fdb04752bbc536007a920d24acb045561c26",
621            // Normal
622            "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed",
623            "0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359",
624            "0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB",
625            "0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb",
626        ];
627        for addr in addresses {
628            let parsed1: Address = addr.parse().unwrap();
629            let parsed2 = Address::parse_checksummed(addr, None).unwrap();
630            assert_eq!(parsed1, parsed2);
631            assert_eq!(parsed2.to_checksum(None), addr);
632        }
633    }
634
635    // https://eips.ethereum.org/EIPS/eip-1191
636    #[test]
637    fn checksum_chain_id() {
638        let eth_mainnet = [
639            "0x27b1fdb04752bbc536007a920d24acb045561c26",
640            "0x3599689E6292b81B2d85451025146515070129Bb",
641            "0x42712D45473476b98452f434e72461577D686318",
642            "0x52908400098527886E0F7030069857D2E4169EE7",
643            "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed",
644            "0x6549f4939460DE12611948b3f82b88C3C8975323",
645            "0x66f9664f97F2b50F62D13eA064982f936dE76657",
646            "0x8617E340B3D01FA5F11F306F4090FD50E238070D",
647            "0x88021160C5C792225E4E5452585947470010289D",
648            "0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb",
649            "0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB",
650            "0xde709f2102306220921060314715629080e2fb77",
651            "0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359",
652        ];
653        let rsk_mainnet = [
654            "0x27b1FdB04752BBc536007A920D24ACB045561c26",
655            "0x3599689E6292B81B2D85451025146515070129Bb",
656            "0x42712D45473476B98452f434E72461577d686318",
657            "0x52908400098527886E0F7030069857D2E4169ee7",
658            "0x5aaEB6053f3e94c9b9a09f33669435E7ef1bEAeD",
659            "0x6549F4939460DE12611948B3F82B88C3C8975323",
660            "0x66F9664f97f2B50F62d13EA064982F936de76657",
661            "0x8617E340b3D01Fa5f11f306f4090fd50E238070D",
662            "0x88021160c5C792225E4E5452585947470010289d",
663            "0xD1220A0Cf47c7B9BE7a2e6ba89F429762E7B9adB",
664            "0xDBF03B407c01E7CD3cBea99509D93F8Dddc8C6FB",
665            "0xDe709F2102306220921060314715629080e2FB77",
666            "0xFb6916095cA1Df60bb79ce92cE3EA74c37c5d359",
667        ];
668        let rsk_testnet = [
669            "0x27B1FdB04752BbC536007a920D24acB045561C26",
670            "0x3599689e6292b81b2D85451025146515070129Bb",
671            "0x42712D45473476B98452F434E72461577D686318",
672            "0x52908400098527886E0F7030069857D2e4169EE7",
673            "0x5aAeb6053F3e94c9b9A09F33669435E7EF1BEaEd",
674            "0x6549f4939460dE12611948b3f82b88C3c8975323",
675            "0x66f9664F97F2b50f62d13eA064982F936DE76657",
676            "0x8617e340b3D01fa5F11f306F4090Fd50e238070d",
677            "0x88021160c5C792225E4E5452585947470010289d",
678            "0xd1220a0CF47c7B9Be7A2E6Ba89f429762E7b9adB",
679            "0xdbF03B407C01E7cd3cbEa99509D93f8dDDc8C6fB",
680            "0xDE709F2102306220921060314715629080e2Fb77",
681            "0xFb6916095CA1dF60bb79CE92ce3Ea74C37c5D359",
682        ];
683        for (addresses, chain_id) in [(eth_mainnet, 1), (rsk_mainnet, 30), (rsk_testnet, 31)] {
684            // EIP-1191 test cases treat mainnet as "not adopted"
685            let id = if chain_id == 1 { None } else { Some(chain_id) };
686            for addr in addresses {
687                let parsed1: Address = addr.parse().unwrap();
688                let parsed2 = Address::parse_checksummed(addr, id).unwrap();
689                assert_eq!(parsed1, parsed2);
690                assert_eq!(parsed2.to_checksum(id), addr);
691            }
692        }
693    }
694
695    // https://ethereum.stackexchange.com/questions/760/how-is-the-address-of-an-ethereum-contract-computed
696    #[test]
697    #[cfg(feature = "rlp")]
698    fn create() {
699        let from = "0x6ac7ea33f8831ea9dcc53393aaa88b25a785dbf0".parse::<Address>().unwrap();
700        for (nonce, expected) in [
701            "0xcd234a471b72ba2f1ccf0a70fcaba648a5eecd8d",
702            "0x343c43a37d37dff08ae8c4a11544c718abb4fcf8",
703            "0xf778b86fa74e846c4f0a1fbd1335fe81c00a0c91",
704            "0xfffd933a0bc612844eaf0c6fe3e5b8e9b6c1d19c",
705        ]
706        .into_iter()
707        .enumerate()
708        {
709            let address = from.create(nonce as u64);
710            assert_eq!(address, expected.parse::<Address>().unwrap());
711        }
712    }
713
714    #[test]
715    #[cfg(all(feature = "rlp", feature = "arbitrary"))]
716    #[cfg_attr(miri, ignore = "doesn't run in isolation and would take too long")]
717    fn create_correctness() {
718        fn create_slow(address: &Address, nonce: u64) -> Address {
719            use alloy_rlp::Encodable;
720
721            let mut out = vec![];
722
723            alloy_rlp::Header { list: true, payload_length: address.length() + nonce.length() }
724                .encode(&mut out);
725            address.encode(&mut out);
726            nonce.encode(&mut out);
727
728            Address::from_word(keccak256(out))
729        }
730
731        proptest::proptest!(|(address: Address, nonce: u64)| {
732            proptest::prop_assert_eq!(address.create(nonce), create_slow(&address, nonce));
733        });
734    }
735
736    // https://eips.ethereum.org/EIPS/eip-1014
737    #[test]
738    fn create2() {
739        let tests = [
740            (
741                "0000000000000000000000000000000000000000",
742                "0000000000000000000000000000000000000000000000000000000000000000",
743                "00",
744                "4D1A2e2bB4F88F0250f26Ffff098B0b30B26BF38",
745            ),
746            (
747                "deadbeef00000000000000000000000000000000",
748                "0000000000000000000000000000000000000000000000000000000000000000",
749                "00",
750                "B928f69Bb1D91Cd65274e3c79d8986362984fDA3",
751            ),
752            (
753                "deadbeef00000000000000000000000000000000",
754                "000000000000000000000000feed000000000000000000000000000000000000",
755                "00",
756                "D04116cDd17beBE565EB2422F2497E06cC1C9833",
757            ),
758            (
759                "0000000000000000000000000000000000000000",
760                "0000000000000000000000000000000000000000000000000000000000000000",
761                "deadbeef",
762                "70f2b2914A2a4b783FaEFb75f459A580616Fcb5e",
763            ),
764            (
765                "00000000000000000000000000000000deadbeef",
766                "00000000000000000000000000000000000000000000000000000000cafebabe",
767                "deadbeef",
768                "60f3f640a8508fC6a86d45DF051962668E1e8AC7",
769            ),
770            (
771                "00000000000000000000000000000000deadbeef",
772                "00000000000000000000000000000000000000000000000000000000cafebabe",
773                "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
774                "1d8bfDC5D46DC4f61D6b6115972536eBE6A8854C",
775            ),
776            (
777                "0000000000000000000000000000000000000000",
778                "0000000000000000000000000000000000000000000000000000000000000000",
779                "",
780                "E33C0C7F7df4809055C3ebA6c09CFe4BaF1BD9e0",
781            ),
782        ];
783        for (from, salt, init_code, expected) in tests {
784            let from = from.parse::<Address>().unwrap();
785
786            let salt = hex::decode(salt).unwrap();
787            let salt: [u8; 32] = salt.try_into().unwrap();
788
789            let init_code = hex::decode(init_code).unwrap();
790            let init_code_hash = keccak256(&init_code);
791
792            let expected = expected.parse::<Address>().unwrap();
793
794            assert_eq!(expected, from.create2(salt, init_code_hash));
795            assert_eq!(expected, from.create2_from_code(salt, init_code));
796        }
797    }
798
799    #[test]
800    fn create_eof() {
801        // Test cases with (from_address, salt, expected_result)
802        let tests = [
803            (
804                "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
805                "0000000000000000000000000000000000000000000000000000000000000000",
806                "02b6826e9392ee6bf6479e413c570846ab0107ec",
807            ),
808            (
809                "0000000000000000000000000000000000000000",
810                "0000000000000000000000000000000000000000000000000000000000000000",
811                "47f3f8F550f58348651C4c3E8cCD414b35d2E9fC",
812            ),
813            (
814                "deadbeef00000000000000000000000000000000",
815                "0000000000000000000000000000000000000000000000000000000000000000",
816                "D146E87a5EA438103eF31cB75B432EecF0c855cc",
817            ),
818        ];
819
820        for (from, salt, expected) in tests {
821            let from = from.parse::<Address>().unwrap();
822
823            let salt = hex::decode(salt).unwrap();
824            let salt: [u8; 32] = salt.try_into().unwrap();
825
826            let expected = expected.parse::<Address>().unwrap();
827
828            assert_eq!(expected, from.create_eof(salt));
829        }
830    }
831
832    #[test]
833    fn test_raw_public_key_to_address() {
834        let addr = "0Ac1dF02185025F65202660F8167210A80dD5086".parse::<Address>().unwrap();
835
836        let pubkey_bytes = hex::decode("76698beebe8ee5c74d8cc50ab84ac301ee8f10af6f28d0ffd6adf4d6d3b9b762d46ca56d3dad2ce13213a6f42278dabbb53259f2d92681ea6a0b98197a719be3").unwrap();
837
838        assert_eq!(Address::from_raw_public_key(&pubkey_bytes), addr);
839    }
840}