ethers_types_rs/
address.rs

1use ethers_hash_rs::keccak256;
2use hex::FromHexError;
3
4use ethabi::ethereum_types::Address;
5
6use crate::bytes::bytes_to_string;
7
8#[derive(Debug, Clone, thiserror::Error)]
9pub enum AddressError {
10    #[error("Convert address from str error,{0}")]
11    HexFormat(FromHexError),
12
13    #[error("Convert bytes to compressed public key error, {0}")]
14    CompressedPubKey(String),
15
16    #[error("Address public key len either 33 or 65")]
17    InvalidPubKeyLength,
18
19    #[error("Address checksum mismatch")]
20    AddressChecksum,
21}
22
23/// Extend `Address` structure, add some usefull helper fns.
24pub trait AddressEx {
25    /// Create address from public key.
26    fn from_pub_key<K>(key: K) -> anyhow::Result<Address>
27    where
28        K: TryInto<[u8; 65]>,
29        K::Error: std::error::Error + Send + Sync + 'static,
30    {
31        let key = key.try_into()?;
32
33        let buf: [u8; 20] = keccak256(&key[1..])[12..]
34            .try_into()
35            .expect("To address array");
36
37        Ok(buf.into())
38    }
39
40    /// Create address from compressed public key.
41    #[cfg(feature = "rust_crypto")]
42    fn from_pub_key_compressed<K>(key: K) -> anyhow::Result<Address>
43    where
44        K: TryInto<[u8; 33]>,
45        K::Error: std::error::Error + Send + Sync + 'static,
46    {
47        let key = key.try_into()?;
48
49        let key = k256::EncodedPoint::from_bytes(&key)
50            .map_err(|err| AddressError::CompressedPubKey(err.to_string()))?;
51
52        let key = k256::ecdsa::VerifyingKey::from_encoded_point(&key)?;
53
54        Self::from_pub_key(key.to_encoded_point(false).as_bytes())
55    }
56
57    /// Create address from `AsRef<[u8]>`, and auto detecting public key type.
58    fn from_any_pub_key<S>(key: S) -> anyhow::Result<Address>
59    where
60        S: AsRef<[u8]>,
61    {
62        let key = key.as_ref();
63
64        match key.len() {
65            33 => Self::from_pub_key_compressed(key),
66            65 => Self::from_pub_key(key),
67            _ => Err(AddressError::InvalidPubKeyLength.into()),
68        }
69    }
70
71    /// Create address from private key
72    #[cfg(feature = "rust_crypto")]
73    fn from_private_key(key: &[u8]) -> anyhow::Result<Address> {
74        let pk = k256::ecdsa::SigningKey::from_bytes(key)?;
75
76        Self::from_pub_key(pk.verifying_key().to_encoded_point(false).as_bytes())
77    }
78}
79
80impl AddressEx for Address {}
81
82pub trait Eip55: Sized {
83    fn to_checksum_string(&self) -> String;
84
85    fn from_checksum_string(source: &str) -> anyhow::Result<Self>;
86}
87
88impl Eip55 for Address {
89    fn to_checksum_string(&self) -> String {
90        let mut data = bytes_to_string(self.as_bytes());
91
92        let digest = keccak256(&data.as_bytes()[2..]);
93
94        let addr = unsafe { &mut data.as_bytes_mut()[2..] };
95
96        for i in 0..addr.len() {
97            let byte = digest[i / 2];
98            let nibble = 0xf & if i % 2 == 0 { byte >> 4 } else { byte };
99            if nibble >= 8 {
100                addr[i] = addr[i].to_ascii_uppercase();
101            }
102        }
103
104        data
105    }
106
107    fn from_checksum_string(source: &str) -> anyhow::Result<Self> {
108        let address: Address = Self::try_from(source)?;
109
110        let expected = address.to_checksum_string();
111
112        if expected != source {
113            return Err(AddressError::AddressChecksum.into());
114        }
115
116        Ok(address)
117    }
118}