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
25#[cfg(feature = "std")]
26impl std::error::Error for AddressError {
27 #[inline]
28 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
29 match self {
30 Self::Hex(err) => Some(err),
31 Self::InvalidChecksum => 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!("d8da6bf26964af9d7eed9e03e53415d37aa96045");
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!("000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa96045");
128 /// assert_eq!(Address::from_word(word), address!("d8da6bf26964af9d7eed9e03e53415d37aa96045"));
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!("d8da6bf26964af9d7eed9e03e53415d37aa96045").into_word(),
144 /// b256!("000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa96045"),
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!("d8da6bf26964af9d7eed9e03e53415d37aa96045");
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!("d8da6bf26964af9d7eed9e03e53415d37aa96045");
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!("d8da6bf26964af9d7eed9e03e53415d37aa96045");
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!("d8da6bf26964af9d7eed9e03e53415d37aa96045");
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!("b20a608c624Ca5003905aA834De7156C68b2E1d0");
340 ///
341 /// let expected = address!("00000000219ab540356cBB839Cbe05303d7705Fa");
342 /// assert_eq!(sender.create(0), expected);
343 ///
344 /// let expected = address!("e33c6e89e69d085897f98e92b06ebd541d1daa99");
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!("8ba1f109551bD432803012645Ac136ddd64DBA72");
392 /// let salt = b256!("7c5ea36004851c764c44143b1dcb59679b11c9a68e5f41497f6cf3d480715331");
393 /// let init_code = bytes!("6394198df16000526103ff60206004601c335afa6040516060f3");
394 /// let expected = address!("533ae9d683B10C02EbDb05471642F85230071FC3");
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!("5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f");
423 /// let salt = b256!("2b2f5776e38002e0c013d0d89828fdb06fee595ea2d5ed4b194e3883e823e350");
424 /// let init_code_hash = b256!("96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f");
425 /// let expected = address!("0d4a11d5EEaaC28EC3F61d100daF4d40471f1852");
426 /// assert_eq!(address.create2(salt, init_code_hash), expected);
427 /// ```
428 #[must_use]
429 pub fn create2<S, H>(&self, salt: S, init_code_hash: H) -> Self
430 where
431 // not `AsRef` because `[u8; N]` does not implement `AsRef<[u8; N]>`
432 S: Borrow<[u8; 32]>,
433 H: Borrow<[u8; 32]>,
434 {
435 self._create2(salt.borrow(), init_code_hash.borrow())
436 }
437
438 // non-generic inner function
439 fn _create2(&self, salt: &[u8; 32], init_code_hash: &[u8; 32]) -> Self {
440 // note: creating a temporary buffer and copying everything over performs
441 // much better than calling `Keccak::update` multiple times
442 let mut bytes = [0; 85];
443 bytes[0] = 0xff;
444 bytes[1..21].copy_from_slice(self.as_slice());
445 bytes[21..53].copy_from_slice(salt);
446 bytes[53..85].copy_from_slice(init_code_hash);
447 let hash = keccak256(bytes);
448 Self::from_word(hash)
449 }
450
451 /// Instantiate by hashing public key bytes.
452 ///
453 /// # Panics
454 ///
455 /// If the input is not exactly 64 bytes
456 pub fn from_raw_public_key(pubkey: &[u8]) -> Self {
457 assert_eq!(pubkey.len(), 64, "raw public key must be 64 bytes");
458 let digest = keccak256(pubkey);
459 Self::from_slice(&digest[12..])
460 }
461
462 /// Converts an ECDSA verifying key to its corresponding Ethereum address.
463 #[inline]
464 #[cfg(feature = "k256")]
465 #[doc(alias = "from_verifying_key")]
466 pub fn from_public_key(pubkey: &k256::ecdsa::VerifyingKey) -> Self {
467 use k256::elliptic_curve::sec1::ToEncodedPoint;
468 let affine: &k256::AffinePoint = pubkey.as_ref();
469 let encoded = affine.to_encoded_point(false);
470 Self::from_raw_public_key(&encoded.as_bytes()[1..])
471 }
472
473 /// Converts an ECDSA signing key to its corresponding Ethereum address.
474 #[inline]
475 #[cfg(feature = "k256")]
476 #[doc(alias = "from_signing_key")]
477 pub fn from_private_key(private_key: &k256::ecdsa::SigningKey) -> Self {
478 Self::from_public_key(private_key.verifying_key())
479 }
480}
481
482/// Stack-allocated buffer for efficiently computing address checksums.
483///
484/// See [`Address::to_checksum_buffer`] for more information.
485#[must_use]
486#[allow(missing_copy_implementations)]
487#[derive(Clone)]
488pub struct AddressChecksumBuffer(MaybeUninit<[u8; 42]>);
489
490impl fmt::Debug for AddressChecksumBuffer {
491 #[inline]
492 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
493 self.as_str().fmt(f)
494 }
495}
496
497impl fmt::Display for AddressChecksumBuffer {
498 #[inline]
499 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
500 self.as_str().fmt(f)
501 }
502}
503
504impl AddressChecksumBuffer {
505 /// Creates a new buffer.
506 ///
507 /// # Safety
508 ///
509 /// The buffer must be initialized with [`format`](Self::format) before use.
510 /// Prefer [`Address::to_checksum_buffer`] instead.
511 #[inline]
512 pub const unsafe fn new() -> Self {
513 Self(MaybeUninit::uninit())
514 }
515
516 /// Calculates the checksum of an address into the buffer.
517 ///
518 /// See [`Address::to_checksum_buffer`] for more information.
519 #[inline]
520 pub fn format(&mut self, address: &Address, chain_id: Option<u64>) -> &mut str {
521 address.to_checksum_inner(unsafe { self.0.assume_init_mut() }, chain_id);
522 self.as_mut_str()
523 }
524
525 /// Returns the checksum of a formatted address.
526 #[inline]
527 pub const fn as_str(&self) -> &str {
528 unsafe { str::from_utf8_unchecked(self.0.assume_init_ref()) }
529 }
530
531 /// Returns the checksum of a formatted address.
532 #[inline]
533 pub fn as_mut_str(&mut self) -> &mut str {
534 unsafe { str::from_utf8_unchecked_mut(self.0.assume_init_mut()) }
535 }
536
537 /// Returns the checksum of a formatted address.
538 #[inline]
539 #[allow(clippy::inherent_to_string_shadow_display)]
540 pub fn to_string(&self) -> String {
541 self.as_str().to_string()
542 }
543
544 /// Returns the backing buffer.
545 #[inline]
546 pub const fn into_inner(self) -> [u8; 42] {
547 unsafe { self.0.assume_init() }
548 }
549}
550
551#[cfg(test)]
552mod tests {
553 use super::*;
554 use crate::hex;
555
556 #[test]
557 fn parse() {
558 let expected = hex!("0102030405060708090a0b0c0d0e0f1011121314");
559 assert_eq!(
560 "0102030405060708090a0b0c0d0e0f1011121314".parse::<Address>().unwrap().into_array(),
561 expected
562 );
563 assert_eq!(
564 "0x0102030405060708090a0b0c0d0e0f1011121314".parse::<Address>().unwrap(),
565 expected
566 );
567 }
568
569 // https://eips.ethereum.org/EIPS/eip-55
570 #[test]
571 fn checksum() {
572 let addresses = [
573 // All caps
574 "0x52908400098527886E0F7030069857D2E4169EE7",
575 "0x8617E340B3D01FA5F11F306F4090FD50E238070D",
576 // All Lower
577 "0xde709f2102306220921060314715629080e2fb77",
578 "0x27b1fdb04752bbc536007a920d24acb045561c26",
579 // Normal
580 "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed",
581 "0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359",
582 "0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB",
583 "0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb",
584 ];
585 for addr in addresses {
586 let parsed1: Address = addr.parse().unwrap();
587 let parsed2 = Address::parse_checksummed(addr, None).unwrap();
588 assert_eq!(parsed1, parsed2);
589 assert_eq!(parsed2.to_checksum(None), addr);
590 }
591 }
592
593 // https://eips.ethereum.org/EIPS/eip-1191
594 #[test]
595 fn checksum_chain_id() {
596 let eth_mainnet = [
597 "0x27b1fdb04752bbc536007a920d24acb045561c26",
598 "0x3599689E6292b81B2d85451025146515070129Bb",
599 "0x42712D45473476b98452f434e72461577D686318",
600 "0x52908400098527886E0F7030069857D2E4169EE7",
601 "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed",
602 "0x6549f4939460DE12611948b3f82b88C3C8975323",
603 "0x66f9664f97F2b50F62D13eA064982f936dE76657",
604 "0x8617E340B3D01FA5F11F306F4090FD50E238070D",
605 "0x88021160C5C792225E4E5452585947470010289D",
606 "0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb",
607 "0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB",
608 "0xde709f2102306220921060314715629080e2fb77",
609 "0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359",
610 ];
611 let rsk_mainnet = [
612 "0x27b1FdB04752BBc536007A920D24ACB045561c26",
613 "0x3599689E6292B81B2D85451025146515070129Bb",
614 "0x42712D45473476B98452f434E72461577d686318",
615 "0x52908400098527886E0F7030069857D2E4169ee7",
616 "0x5aaEB6053f3e94c9b9a09f33669435E7ef1bEAeD",
617 "0x6549F4939460DE12611948B3F82B88C3C8975323",
618 "0x66F9664f97f2B50F62d13EA064982F936de76657",
619 "0x8617E340b3D01Fa5f11f306f4090fd50E238070D",
620 "0x88021160c5C792225E4E5452585947470010289d",
621 "0xD1220A0Cf47c7B9BE7a2e6ba89F429762E7B9adB",
622 "0xDBF03B407c01E7CD3cBea99509D93F8Dddc8C6FB",
623 "0xDe709F2102306220921060314715629080e2FB77",
624 "0xFb6916095cA1Df60bb79ce92cE3EA74c37c5d359",
625 ];
626 let rsk_testnet = [
627 "0x27B1FdB04752BbC536007a920D24acB045561C26",
628 "0x3599689e6292b81b2D85451025146515070129Bb",
629 "0x42712D45473476B98452F434E72461577D686318",
630 "0x52908400098527886E0F7030069857D2e4169EE7",
631 "0x5aAeb6053F3e94c9b9A09F33669435E7EF1BEaEd",
632 "0x6549f4939460dE12611948b3f82b88C3c8975323",
633 "0x66f9664F97F2b50f62d13eA064982F936DE76657",
634 "0x8617e340b3D01fa5F11f306F4090Fd50e238070d",
635 "0x88021160c5C792225E4E5452585947470010289d",
636 "0xd1220a0CF47c7B9Be7A2E6Ba89f429762E7b9adB",
637 "0xdbF03B407C01E7cd3cbEa99509D93f8dDDc8C6fB",
638 "0xDE709F2102306220921060314715629080e2Fb77",
639 "0xFb6916095CA1dF60bb79CE92ce3Ea74C37c5D359",
640 ];
641 for (addresses, chain_id) in [(eth_mainnet, 1), (rsk_mainnet, 30), (rsk_testnet, 31)] {
642 // EIP-1191 test cases treat mainnet as "not adopted"
643 let id = if chain_id == 1 { None } else { Some(chain_id) };
644 for addr in addresses {
645 let parsed1: Address = addr.parse().unwrap();
646 let parsed2 = Address::parse_checksummed(addr, id).unwrap();
647 assert_eq!(parsed1, parsed2);
648 assert_eq!(parsed2.to_checksum(id), addr);
649 }
650 }
651 }
652
653 // https://ethereum.stackexchange.com/questions/760/how-is-the-address-of-an-ethereum-contract-computed
654 #[test]
655 #[cfg(feature = "rlp")]
656 fn create() {
657 let from = "0x6ac7ea33f8831ea9dcc53393aaa88b25a785dbf0".parse::<Address>().unwrap();
658 for (nonce, expected) in [
659 "0xcd234a471b72ba2f1ccf0a70fcaba648a5eecd8d",
660 "0x343c43a37d37dff08ae8c4a11544c718abb4fcf8",
661 "0xf778b86fa74e846c4f0a1fbd1335fe81c00a0c91",
662 "0xfffd933a0bc612844eaf0c6fe3e5b8e9b6c1d19c",
663 ]
664 .into_iter()
665 .enumerate()
666 {
667 let address = from.create(nonce as u64);
668 assert_eq!(address, expected.parse::<Address>().unwrap());
669 }
670 }
671
672 #[test]
673 #[cfg(all(feature = "rlp", feature = "arbitrary"))]
674 #[cfg_attr(miri, ignore = "doesn't run in isolation and would take too long")]
675 fn create_correctness() {
676 fn create_slow(address: &Address, nonce: u64) -> Address {
677 use alloy_rlp::Encodable;
678
679 let mut out = vec![];
680
681 alloy_rlp::Header { list: true, payload_length: address.length() + nonce.length() }
682 .encode(&mut out);
683 address.encode(&mut out);
684 nonce.encode(&mut out);
685
686 Address::from_word(keccak256(out))
687 }
688
689 proptest::proptest!(|(address: Address, nonce: u64)| {
690 proptest::prop_assert_eq!(address.create(nonce), create_slow(&address, nonce));
691 });
692 }
693
694 // https://eips.ethereum.org/EIPS/eip-1014
695 #[test]
696 fn create2() {
697 let tests = [
698 (
699 "0000000000000000000000000000000000000000",
700 "0000000000000000000000000000000000000000000000000000000000000000",
701 "00",
702 "4D1A2e2bB4F88F0250f26Ffff098B0b30B26BF38",
703 ),
704 (
705 "deadbeef00000000000000000000000000000000",
706 "0000000000000000000000000000000000000000000000000000000000000000",
707 "00",
708 "B928f69Bb1D91Cd65274e3c79d8986362984fDA3",
709 ),
710 (
711 "deadbeef00000000000000000000000000000000",
712 "000000000000000000000000feed000000000000000000000000000000000000",
713 "00",
714 "D04116cDd17beBE565EB2422F2497E06cC1C9833",
715 ),
716 (
717 "0000000000000000000000000000000000000000",
718 "0000000000000000000000000000000000000000000000000000000000000000",
719 "deadbeef",
720 "70f2b2914A2a4b783FaEFb75f459A580616Fcb5e",
721 ),
722 (
723 "00000000000000000000000000000000deadbeef",
724 "00000000000000000000000000000000000000000000000000000000cafebabe",
725 "deadbeef",
726 "60f3f640a8508fC6a86d45DF051962668E1e8AC7",
727 ),
728 (
729 "00000000000000000000000000000000deadbeef",
730 "00000000000000000000000000000000000000000000000000000000cafebabe",
731 "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
732 "1d8bfDC5D46DC4f61D6b6115972536eBE6A8854C",
733 ),
734 (
735 "0000000000000000000000000000000000000000",
736 "0000000000000000000000000000000000000000000000000000000000000000",
737 "",
738 "E33C0C7F7df4809055C3ebA6c09CFe4BaF1BD9e0",
739 ),
740 ];
741 for (from, salt, init_code, expected) in tests {
742 let from = from.parse::<Address>().unwrap();
743
744 let salt = hex::decode(salt).unwrap();
745 let salt: [u8; 32] = salt.try_into().unwrap();
746
747 let init_code = hex::decode(init_code).unwrap();
748 let init_code_hash = keccak256(&init_code);
749
750 let expected = expected.parse::<Address>().unwrap();
751
752 assert_eq!(expected, from.create2(salt, init_code_hash));
753 assert_eq!(expected, from.create2_from_code(salt, init_code));
754 }
755 }
756
757 #[test]
758 fn test_raw_public_key_to_address() {
759 let addr = "0Ac1dF02185025F65202660F8167210A80dD5086".parse::<Address>().unwrap();
760
761 let pubkey_bytes = hex::decode("76698beebe8ee5c74d8cc50ab84ac301ee8f10af6f28d0ffd6adf4d6d3b9b762d46ca56d3dad2ce13213a6f42278dabbb53259f2d92681ea6a0b98197a719be3").unwrap();
762
763 assert_eq!(Address::from_raw_public_key(&pubkey_bytes), addr);
764 }
765}