layer_climb_address/
address.rs1mod cosmos;
2mod evm;
3
4use anyhow::{bail, Result};
5use cosmwasm_schema::cw_serde;
6use std::hash::Hash;
7
8pub use cosmos::CosmosAddr;
9pub use evm::EvmAddr;
10
11#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
15#[derive(Eq, PartialOrd, Ord, Hash)]
16#[cw_serde]
17pub enum Address {
18 Cosmos(CosmosAddr),
19 Evm(EvmAddr),
20}
21
22impl Address {
23 pub fn as_bytes(&self) -> Vec<u8> {
24 match self {
25 Address::Cosmos(addr_cosmos) => addr_cosmos.to_vec(),
26 Address::Evm(addr_evm) => addr_evm.as_bytes().to_vec(),
27 }
28 }
29
30 pub fn try_from_str(value: &str, addr_kind: &AddrKind) -> Result<Self> {
31 match addr_kind {
32 AddrKind::Cosmos { prefix } => {
33 CosmosAddr::new_string(value, Some(prefix)).map(Self::Cosmos)
34 }
35 AddrKind::Evm => EvmAddr::new_string(value).map(Self::Evm),
36 }
37 }
38
39 pub fn try_from_pub_key(
40 pub_key: &tendermint::PublicKey,
41 addr_kind: &AddrKind,
42 ) -> Result<Address> {
43 match addr_kind {
44 AddrKind::Cosmos { prefix } => {
45 CosmosAddr::new_pub_key(pub_key, prefix).map(Self::Cosmos)
46 }
47 AddrKind::Evm => EvmAddr::new_pub_key(pub_key).map(Self::Evm),
48 }
49 }
50}
51
52impl std::fmt::Display for Address {
54 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55 match self {
56 Self::Cosmos(addr_cosmos) => {
57 write!(f, "{addr_cosmos}")
58 }
59 Self::Evm(addr_evm) => {
60 write!(f, "{addr_evm}")
61 }
62 }
63 }
64}
65
66impl TryFrom<Address> for EvmAddr {
68 type Error = anyhow::Error;
69
70 fn try_from(addr: Address) -> Result<Self> {
71 match addr {
72 Address::Evm(addr) => Ok(addr),
73 Address::Cosmos(_) => bail!("Address is not EVM"),
74 }
75 }
76}
77
78impl TryFrom<Address> for CosmosAddr {
79 type Error = anyhow::Error;
80
81 fn try_from(addr: Address) -> Result<Self> {
82 match addr {
83 Address::Cosmos(addr) => Ok(addr),
84 Address::Evm(_) => bail!("Address is not Cosmos"),
85 }
86 }
87}
88
89impl TryFrom<Address> for alloy_primitives::Address {
90 type Error = anyhow::Error;
91
92 fn try_from(addr: Address) -> Result<Self> {
93 match addr {
94 Address::Evm(addr) => Ok(addr.into()),
95 Address::Cosmos(_) => bail!("Address is not EVM"),
96 }
97 }
98}
99
100impl TryFrom<Address> for cosmwasm_std::Addr {
101 type Error = anyhow::Error;
102
103 fn try_from(addr: Address) -> Result<Self> {
104 match addr {
105 Address::Cosmos(addr) => Ok(addr.into()),
106 Address::Evm(_) => bail!("Address is not Cosmos"),
107 }
108 }
109}
110
111impl From<EvmAddr> for Address {
113 fn from(addr: EvmAddr) -> Self {
114 Self::Evm(addr)
115 }
116}
117
118impl From<CosmosAddr> for Address {
119 fn from(addr: CosmosAddr) -> Self {
120 Self::Cosmos(addr)
121 }
122}
123
124impl From<alloy_primitives::Address> for Address {
125 fn from(addr: alloy_primitives::Address) -> Self {
126 Self::Evm(addr.into())
127 }
128}
129
130impl TryFrom<cosmwasm_std::Addr> for Address {
131 type Error = anyhow::Error;
132
133 fn try_from(addr: cosmwasm_std::Addr) -> Result<Self> {
134 Ok(Self::Cosmos(addr.try_into()?))
135 }
136}
137
138#[cw_serde]
139#[derive(Eq, Hash)]
140pub enum AddrKind {
141 Cosmos { prefix: String },
142 Evm,
143}
144
145impl AddrKind {
146 pub fn parse_address(&self, value: &str) -> Result<Address> {
147 Address::try_from_str(value, self)
148 }
149
150 pub fn address_from_pub_key(&self, pub_key: &tendermint::PublicKey) -> Result<Address> {
151 Address::try_from_pub_key(pub_key, self)
152 }
153}
154
155#[cfg(test)]
156mod test {
157 use super::{Address, CosmosAddr, EvmAddr};
158
159 const TEST_COSMOS_STR: &str = "osmo1h5qke5tzc0fgz93wcxg8da2en3advfect0gh4a";
162 const TEST_COSMOS_PREFIX: &str = "osmo";
163 const TEST_EVM_STR: &str = "0xb794f5ea0ba39494ce839613fffba74279579268";
164
165 #[test]
166 fn test_basic_roundtrip_evm() {
167 let test_string = TEST_EVM_STR;
168 let addr_evm: EvmAddr = test_string.parse().unwrap();
169 let addr: Address = addr_evm.clone().into();
170
171 assert_eq!(addr.to_string(), test_string);
172
173 let addr_evm_2: EvmAddr = addr.clone().try_into().unwrap();
174 assert_eq!(addr_evm_2, addr_evm);
175
176 assert_eq!(
178 serde_json::to_string(&addr_evm).unwrap(),
179 format!("\"{test_string}\"")
180 );
181 assert_eq!(
182 serde_json::from_str::<EvmAddr>(&format!("\"{test_string}\"")).unwrap(),
183 addr_evm
184 );
185 }
186
187 #[test]
188 fn test_basic_roundtrip_cosmos() {
189 let cosmos_addr = CosmosAddr::new_string(TEST_COSMOS_STR, None).unwrap();
190 let addr: Address = cosmos_addr.clone().into();
191
192 assert_eq!(addr.to_string(), TEST_COSMOS_STR);
193 assert_eq!(cosmos_addr.prefix(), TEST_COSMOS_PREFIX);
194 }
195
196 #[test]
197 fn test_serde_roundtrip_cosmos() {
198 #[derive(serde::Serialize, serde::Deserialize)]
199 struct TestStruct {
200 addr: CosmosAddr,
201 }
202
203 let test_struct: TestStruct =
204 serde_json::from_str(&format!(r#"{{ "addr": "{TEST_COSMOS_STR}"}}"#)).unwrap();
205 let addr: Address = test_struct.addr.clone().into();
206
207 let test_struct_2 = TestStruct {
208 addr: addr.try_into().unwrap(),
209 };
210
211 assert_eq!(
212 serde_json::to_string(&test_struct_2).unwrap().trim(),
213 format!(r#"{{"addr":"{TEST_COSMOS_STR}"}}"#).trim()
214 );
215 assert_eq!(test_struct_2.addr.prefix(), TEST_COSMOS_PREFIX);
216 }
217
218 #[test]
219 fn test_convert_evm_to_cosmos() {
220 }
226
227 #[test]
228 fn test_convert_cosmos_to_evm() {
229 }
235}