ic_ethereum_types/address/
mod.rs1#[cfg(test)]
2mod tests;
3
4use minicbor::{Decode, Encode};
5use serde::{Deserialize, Serialize};
6use std::fmt;
7use std::fmt::{Formatter, LowerHex, UpperHex};
8use std::str::FromStr;
9
10#[derive(
27 Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Decode, Deserialize, Encode, Serialize,
28)]
29#[serde(transparent)]
30#[cbor(transparent)]
31pub struct Address(
32 #[serde(with = "crate::serde_data")]
33 #[cbor(n(0), with = "minicbor::bytes")]
34 [u8; 20],
35);
36
37impl AsRef<[u8]> for Address {
38 fn as_ref(&self) -> &[u8] {
39 &self.0
40 }
41}
42
43impl Address {
44 pub const ZERO: Self = Self([0u8; 20]);
52
53 pub const fn new(bytes: [u8; 20]) -> Self {
55 Self(bytes)
56 }
57
58 pub const fn into_bytes(self) -> [u8; 20] {
60 self.0
61 }
62}
63
64impl LowerHex for Address {
65 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
66 write!(f, "0x{}", hex::encode(self.0))
67 }
68}
69
70impl UpperHex for Address {
71 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
72 write!(f, "0x{}", hex::encode_upper(self.0))
73 }
74}
75
76impl TryFrom<&[u8; 32]> for Address {
78 type Error = String;
79
80 fn try_from(value: &[u8; 32]) -> Result<Self, Self::Error> {
81 let (leading_zeroes, address_bytes) = value.split_at(12);
82 if !leading_zeroes.iter().all(|leading_zero| *leading_zero == 0) {
83 return Err(format!(
84 "address has leading non-zero bytes: {:?}",
85 leading_zeroes
86 ));
87 }
88 Ok(Address::new(
89 <[u8; 20]>::try_from(address_bytes).expect("vector has correct length"),
90 ))
91 }
92}
93
94impl From<&Address> for [u8; 32] {
96 fn from(address: &Address) -> Self {
97 let bytes = address.as_ref();
98 let pad = 32 - bytes.len();
99 let mut padded: [u8; 32] = [0; 32];
100 padded[pad..32].copy_from_slice(bytes);
101 padded
102 }
103}
104
105impl FromStr for Address {
106 type Err = String;
107
108 fn from_str(s: &str) -> Result<Self, Self::Err> {
109 if !s.starts_with("0x") {
110 return Err("address doesn't start with '0x'".to_string());
111 }
112 let mut bytes = [0u8; 20];
113 hex::decode_to_slice(&s[2..], &mut bytes)
114 .map_err(|e| format!("address is not hex: {}", e))?;
115 Ok(Self(bytes))
116 }
117}
118
119impl fmt::Debug for Address {
120 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
121 write!(f, "{}", self)
122 }
123}
124
125impl fmt::Display for Address {
127 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128 let mut addr_chars = [0u8; 20 * 2];
129 hex::encode_to_slice(self.0, &mut addr_chars)
130 .expect("bug: failed to encode an address as hex");
131
132 let checksum = keccak(&addr_chars[..]);
133 let mut cs_nibbles = [0u8; 32 * 2];
134 for i in 0..32 {
135 cs_nibbles[2 * i] = checksum[i] >> 4;
136 cs_nibbles[2 * i + 1] = checksum[i] & 0x0f;
137 }
138 write!(f, "0x")?;
139 for (a, cs) in addr_chars.iter().zip(cs_nibbles.iter()) {
140 let ascii_byte = if *cs >= 0x08 {
141 a.to_ascii_uppercase()
142 } else {
143 *a
144 };
145 write!(f, "{}", char::from(ascii_byte))?;
146 }
147 Ok(())
148 }
149}
150
151fn keccak(bytes: &[u8]) -> [u8; 32] {
152 ic_sha3::Keccak256::hash(bytes)
153}