layer_climb_address/address/
evm.rs

1use std::{borrow::Cow, str::FromStr};
2
3use anyhow::{anyhow, bail, Result};
4use cosmwasm_schema::cw_schema;
5
6/// EVM address
7// we implement our own Serialize/Deserialize to ensure it is serialized as a hex string
8// so we need to manually implement the cw_serde derives from https://github.com/CosmWasm/cosmwasm/blob/fa5439a9e4e6884abe1e76f04443a95961eaa73f/packages/schema-derive/src/cw_serde.rs#L47C5-L61C7
9#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, bincode::Encode, bincode::Decode)]
10#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
11#[cfg_attr(feature = "cw-storage", derive(cw_storage_plus::NewTypeKey))]
12pub struct EvmAddr([u8; 20]);
13
14impl EvmAddr {
15    pub fn new(bytes: [u8; 20]) -> Self {
16        Self(bytes)
17    }
18
19    pub fn new_vec(bytes: Vec<u8>) -> Result<Self> {
20        if bytes.len() != 20 {
21            bail!("Invalid length for EVM address");
22        }
23        let mut arr = [0u8; 20];
24        arr.copy_from_slice(&bytes);
25        Ok(Self(arr))
26    }
27
28    pub fn new_pub_key(_pub_key: &tendermint::PublicKey) -> Result<Self> {
29        Err(anyhow!("TODO - support EVM pub key"))
30    }
31
32    pub fn new_str(s: &str) -> Result<Self> {
33        Self::new_vec(const_hex::decode(s.trim())?)
34    }
35
36    pub fn as_bytes(&self) -> [u8; 20] {
37        self.0
38    }
39}
40impl cw_schema::Schemaifier for EvmAddr {
41    #[inline]
42    fn visit_schema(visitor: &mut cw_schema::SchemaVisitor) -> cw_schema::DefinitionReference {
43        let node = cw_schema::Node {
44            name: Cow::Borrowed(std::any::type_name::<Self>()),
45            description: None,
46            value: cw_schema::NodeType::String,
47        };
48
49        visitor.insert(Self::id(), node)
50    }
51}
52
53impl cosmwasm_schema::schemars::JsonSchema for EvmAddr {
54    fn schema_name() -> String {
55        "EvmAddr".into()
56    }
57
58    fn json_schema(
59        _generator: &mut cosmwasm_schema::schemars::r#gen::SchemaGenerator,
60    ) -> cosmwasm_schema::schemars::schema::Schema {
61        cosmwasm_schema::schemars::schema::Schema::Object(
62            cosmwasm_schema::schemars::schema::SchemaObject {
63                instance_type: Some(cosmwasm_schema::schemars::schema::SingleOrVec::Single(
64                    Box::new(cosmwasm_schema::schemars::schema::InstanceType::String),
65                )),
66                format: Some("evm-address".into()),
67                ..Default::default()
68            },
69        )
70    }
71}
72
73impl std::fmt::Display for EvmAddr {
74    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75        write!(f, "0x{}", const_hex::encode(self.0))
76    }
77}
78
79impl FromStr for EvmAddr {
80    type Err = anyhow::Error;
81
82    fn from_str(s: &str) -> Result<Self> {
83        Self::new_str(s)
84    }
85}
86
87impl serde::Serialize for EvmAddr {
88    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
89    where
90        S: serde::Serializer,
91    {
92        serializer.serialize_str(&self.to_string())
93    }
94}
95
96impl<'de> serde::Deserialize<'de> for EvmAddr {
97    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
98    where
99        D: serde::Deserializer<'de>,
100    {
101        let s = String::deserialize(deserializer)?;
102        s.parse().map_err(serde::de::Error::custom)
103    }
104}
105
106// From/into impls
107impl From<alloy_primitives::Address> for EvmAddr {
108    fn from(addr: alloy_primitives::Address) -> Self {
109        Self(**addr)
110    }
111}
112
113impl From<EvmAddr> for alloy_primitives::Address {
114    fn from(addr: EvmAddr) -> Self {
115        alloy_primitives::Address::new(addr.0)
116    }
117}