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, bincode::Encode, bincode::Decode)]
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_str(value, Some(prefix)).map(Self::Cosmos)
34 }
35 AddrKind::Evm => EvmAddr::new_str(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
138impl TryFrom<&cosmwasm_std::Addr> for Address {
139 type Error = anyhow::Error;
140
141 fn try_from(addr: &cosmwasm_std::Addr) -> Result<Self> {
142 Ok(Self::Cosmos(addr.try_into()?))
143 }
144}
145
146#[cw_serde]
147#[derive(Eq, Hash)]
148pub enum AddrKind {
149 Cosmos { prefix: String },
150 Evm,
151}
152
153impl AddrKind {
154 pub fn parse_address(&self, value: &str) -> Result<Address> {
155 Address::try_from_str(value, self)
156 }
157
158 pub fn address_from_pub_key(&self, pub_key: &tendermint::PublicKey) -> Result<Address> {
159 Address::try_from_pub_key(pub_key, self)
160 }
161}
162
163#[cfg(test)]
164mod test {
165 use super::{Address, CosmosAddr, EvmAddr};
166
167 const TEST_COSMOS_STR: &str = "osmo1h5qke5tzc0fgz93wcxg8da2en3advfect0gh4a";
170 const TEST_COSMOS_PREFIX: &str = "osmo";
171 const TEST_EVM_STR: &str = "0xb794f5ea0ba39494ce839613fffba74279579268";
172
173 #[test]
174 fn test_basic_roundtrip_evm() {
175 let test_string = TEST_EVM_STR;
176 let addr_evm: EvmAddr = test_string.parse().unwrap();
177 let addr: Address = addr_evm.clone().into();
178
179 assert_eq!(addr.to_string(), test_string);
180
181 let addr_evm_2: EvmAddr = addr.clone().try_into().unwrap();
182 assert_eq!(addr_evm_2, addr_evm);
183
184 assert_eq!(
186 serde_json::to_string(&addr_evm).unwrap(),
187 format!("\"{test_string}\"")
188 );
189 assert_eq!(
190 serde_json::from_str::<EvmAddr>(&format!("\"{test_string}\"")).unwrap(),
191 addr_evm
192 );
193 }
194
195 #[test]
196 fn test_basic_roundtrip_cosmos() {
197 let cosmos_addr = CosmosAddr::new_str(TEST_COSMOS_STR, None).unwrap();
198 let addr: Address = cosmos_addr.clone().into();
199
200 assert_eq!(addr.to_string(), TEST_COSMOS_STR);
201 assert_eq!(cosmos_addr.prefix(), TEST_COSMOS_PREFIX);
202 }
203
204 #[test]
205 fn test_serde_roundtrip_cosmos() {
206 #[derive(serde::Serialize, serde::Deserialize)]
207 struct TestStruct {
208 addr: CosmosAddr,
209 }
210
211 let test_struct: TestStruct =
212 serde_json::from_str(&format!(r#"{{ "addr": "{TEST_COSMOS_STR}"}}"#)).unwrap();
213 let addr: Address = test_struct.addr.clone().into();
214
215 let test_struct_2 = TestStruct {
216 addr: addr.try_into().unwrap(),
217 };
218
219 assert_eq!(
220 serde_json::to_string(&test_struct_2).unwrap().trim(),
221 format!(r#"{{"addr":"{TEST_COSMOS_STR}"}}"#).trim()
222 );
223 assert_eq!(test_struct_2.addr.prefix(), TEST_COSMOS_PREFIX);
224 }
225
226 #[test]
227 fn test_convert_evm_to_cosmos() {
228 }
234
235 #[test]
236 fn test_convert_cosmos_to_evm() {
237 }
243}