#![cfg_attr(not(any(feature = "std", test)), no_std)]
mod checksum;
mod hex;
mod keccak;
#[cfg(feature = "serde")]
mod serde;
use crate::hex::{Alphabet, FormattingBuffer, ParseHexError};
use core::{
array::{IntoIter, TryFromSliceError},
fmt::{self, Debug, Display, Formatter, LowerHex, UpperHex},
ops::{Deref, DerefMut},
slice::Iter,
str::{self, FromStr},
};
#[macro_export]
macro_rules! address {
($address:expr $(,)?) => {{
const VALUE: $crate::Address = $crate::Address::const_from_str_checksum($address);
VALUE
}};
(~$address:expr $(,)?) => {{
const VALUE: $crate::Address = $crate::Address::const_from_str($address);
VALUE
}};
}
#[repr(transparent)]
#[derive(Copy, Clone, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Address(pub [u8; 20]);
impl Address {
pub fn from_slice(slice: &[u8]) -> Self {
slice.try_into().unwrap()
}
pub fn from_ref(array: &[u8; 20]) -> &'_ Self {
unsafe { &*(array as *const [u8; 20]).cast::<Self>() }
}
pub fn from_mut(array: &mut [u8; 20]) -> &'_ mut Self {
unsafe { &mut *(array as *mut [u8; 20]).cast::<Self>() }
}
pub fn from_str_checksum(s: &str) -> Result<Self, ParseAddressError> {
let bytes = hex::decode(s)?;
checksum::verify(&bytes, s).map_err(|_| ParseAddressError::ChecksumMismatch)?;
Ok(Self(bytes))
}
#[doc(hidden)]
pub const fn const_from_str(src: &str) -> Self {
Self(hex::const_decode(src))
}
#[doc(hidden)]
pub const fn const_from_str_checksum(src: &str) -> Self {
let Address(addr) = Self::const_from_str(src);
if !checksum::const_verify(&addr, src) {
panic!("invalid address checksum");
}
Address(addr)
}
fn fmt_buffer(&self, alphabet: Alphabet) -> FormattingBuffer<42> {
hex::encode(self, alphabet)
}
fn fmt(&self) -> FormattingBuffer<42> {
checksum::fmt(self)
}
}
impl Debug for Address {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.debug_tuple("Address")
.field(&format_args!("{self}"))
.finish()
}
}
impl Display for Address {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad(self.fmt().as_str())
}
}
impl LowerHex for Address {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let buffer = self.fmt_buffer(Alphabet::Lower);
f.pad(if f.alternate() {
buffer.as_str()
} else {
buffer.as_bytes_str()
})
}
}
impl UpperHex for Address {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let buffer = self.fmt_buffer(Alphabet::Upper);
f.pad(if f.alternate() {
buffer.as_str()
} else {
buffer.as_bytes_str()
})
}
}
impl AsRef<[u8; 20]> for Address {
fn as_ref(&self) -> &[u8; 20] {
&self.0
}
}
impl AsRef<[u8]> for Address {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl AsMut<[u8; 20]> for Address {
fn as_mut(&mut self) -> &mut [u8; 20] {
&mut self.0
}
}
impl AsMut<[u8]> for Address {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.0
}
}
impl Deref for Address {
type Target = [u8; 20];
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for Address {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl FromStr for Address {
type Err = ParseAddressError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self(hex::decode(s)?))
}
}
impl IntoIterator for Address {
type Item = u8;
type IntoIter = IntoIter<u8, 20>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<'a> IntoIterator for &'a Address {
type Item = &'a u8;
type IntoIter = Iter<'a, u8>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
impl PartialEq<[u8; 20]> for Address {
fn eq(&self, other: &'_ [u8; 20]) -> bool {
**self == *other
}
}
impl PartialEq<[u8]> for Address {
fn eq(&self, other: &'_ [u8]) -> bool {
**self == *other
}
}
impl PartialEq<&'_ [u8]> for Address {
fn eq(&self, other: &&'_ [u8]) -> bool {
**self == **other
}
}
impl PartialEq<&'_ mut [u8]> for Address {
fn eq(&self, other: &&'_ mut [u8]) -> bool {
**self == **other
}
}
#[cfg(feature = "std")]
impl PartialEq<Vec<u8>> for Address {
fn eq(&self, other: &Vec<u8>) -> bool {
**self == **other
}
}
impl TryFrom<&'_ [u8]> for Address {
type Error = TryFromSliceError;
fn try_from(value: &'_ [u8]) -> Result<Self, Self::Error> {
Ok(Self(value.try_into()?))
}
}
impl TryFrom<&'_ mut [u8]> for Address {
type Error = TryFromSliceError;
fn try_from(value: &'_ mut [u8]) -> Result<Self, Self::Error> {
Ok(Self(value.try_into()?))
}
}
impl<'a> TryFrom<&'a [u8]> for &'a Address {
type Error = TryFromSliceError;
fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
Ok(Address::from_ref(value.try_into()?))
}
}
impl<'a> TryFrom<&'a mut [u8]> for &'a mut Address {
type Error = TryFromSliceError;
fn try_from(value: &'a mut [u8]) -> Result<Self, Self::Error> {
Ok(Address::from_mut(value.try_into()?))
}
}
#[cfg(feature = "std")]
impl TryFrom<Vec<u8>> for Address {
type Error = Vec<u8>;
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
Ok(Self(value.try_into()?))
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ParseAddressError {
InvalidLength,
InvalidHexCharacter { c: char, index: usize },
ChecksumMismatch,
}
impl Display for ParseAddressError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::InvalidLength => write!(f, "{}", ParseHexError::InvalidLength),
Self::InvalidHexCharacter { c, index } => {
let (c, index) = (*c, *index);
write!(f, "{}", ParseHexError::InvalidHexCharacter { c, index })
}
Self::ChecksumMismatch => {
write!(f, "address checksum does not match")
}
}
}
}
impl From<ParseHexError> for ParseAddressError {
fn from(err: ParseHexError) -> Self {
match err {
ParseHexError::InvalidLength => Self::InvalidLength,
ParseHexError::InvalidHexCharacter { c, index } => {
Self::InvalidHexCharacter { c, index }
}
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for ParseAddressError {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn checksum_address() {
for s in [
"0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1",
"0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
] {
let address = s.parse::<Address>().unwrap();
assert_eq!(address.to_string(), s);
}
}
#[test]
fn without_prefix_and_checksum() {
assert_eq!(
"eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
.parse::<Address>()
.unwrap(),
Address([0xee; 20]),
);
}
#[test]
fn verify_address_checksum() {
for address in [
"0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
"EeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
] {
assert_eq!(
Address::from_str_checksum(address).unwrap(),
Address([0xee; 20])
);
assert_eq!(
Address::const_from_str_checksum(address),
Address([0xee; 20])
);
}
for address in [
"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
"eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
] {
assert!(Address::from_str_checksum(address).is_err());
}
}
#[test]
#[should_panic]
fn const_verify_address_checksum_error() {
Address::const_from_str_checksum("0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee");
}
#[test]
fn hex_formatting() {
let address = Address([0xee; 20]);
assert_eq!(
format!("{address:x}"),
"eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
);
assert_eq!(
format!("{address:#x}"),
"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
);
assert_eq!(
format!("{address:X}"),
"EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE"
);
assert_eq!(
format!("{address:#X}"),
"0xEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE"
);
}
}