novax_data/types/
address.rs

1use std::hash::{Hash, Hasher};
2use std::ops::Deref;
3use multiversx_sc::api::ManagedTypeApi;
4use multiversx_sc::types::ManagedAddress;
5use multiversx_sc_codec::{DecodeError, TopDecode, TopDecodeInput};
6use multiversx_sc_scenario::api::StaticApi;
7use multiversx_sc_scenario::scenario_model::AddressValue;
8use multiversx_sdk::data::address::Address as SDKAddress;
9use serde::{Deserialize, Deserializer, Serialize};
10use serde::de::Error;
11use crate::error::AddressError;
12use crate::error::DataError;
13use crate::types::managed::ManagedConvertible;
14use crate::types::native::NativeConvertible;
15
16/// A struct representing a blockchain address.
17/// This struct provides various utility methods for working with addresses,
18/// including conversions from and to Bech32 string representations and byte arrays.
19///
20/// # Serialization
21/// This struct is serializable with the `serde` crate.
22///
23/// # Cloning
24/// Cloning is supported.
25///
26/// # Debugging
27/// Debug printouts are supported.
28///
29/// # Examples
30///
31/// Basic usage:
32///
33/// ```
34/// # use novax_data::Address;
35/// let address = Address::from_bech32_string("erd1qqqqqqqqqqqqqpgq7ykazrzd905zvnlr88dpfw06677lxe9w0n4suz00uh").unwrap();
36/// let bech32 = address.to_bech32_string().unwrap();
37/// assert_eq!(bech32, "erd1qqqqqqqqqqqqqpgq7ykazrzd905zvnlr88dpfw06677lxe9w0n4suz00uh");
38/// ```
39#[derive(Serialize, Clone, Debug)]
40pub struct Address(SDKAddress);
41
42impl Default for Address {
43    fn default() -> Self {
44        Address::from_bytes([0; 32])
45    }
46}
47
48impl PartialEq for Address {
49    fn eq(&self, other: &Self) -> bool {
50        self.to_bytes() == other.to_bytes()
51    }
52}
53
54/// The `Address` struct provides an abstraction over a blockchain address,
55/// with various utility methods for working with addresses.
56impl Address {
57    /// Creates an `Address` instance from a Bech32 string representation.
58    ///
59    /// # Parameters
60    /// - `bech32`: The Bech32 string representation of the address.
61    ///
62    /// # Returns
63    /// - An `Ok(Address)` instance if the conversion is successful.
64    /// - An `Err(DataError)` if the Bech32 string is invalid.
65    ///
66    /// # Example
67    /// ```
68    /// # use novax_data::Address;
69    /// let address = Address::from_bech32_string("erd1qqqqqqqqqqqqqpgq7ykazrzd905zvnlr88dpfw06677lxe9w0n4suz00uh").unwrap();
70    /// ```
71    pub fn from_bech32_string(bech32: &str) -> Result<Address, DataError> {
72        let Ok(address) = SDKAddress::from_bech32_string(bech32) else { return Err(AddressError::InvalidBech32String { invalid_value: bech32.to_string() }.into()) };
73
74        Ok(Address(address))
75    }
76
77    /// Creates an `Address` instance from a byte array.
78    ///
79    /// # Parameters
80    /// - `bytes`: A byte array of length 32.
81    ///
82    /// # Returns
83    /// - An `Address` instance.
84    ///
85    /// # Example
86    /// ```
87    /// # use novax_data::Address;
88    /// let address = Address::from_bytes([0_u8; 32]);
89    /// ```
90    pub fn from_bytes(bytes: [u8; 32]) -> Address {
91        Address(SDKAddress::from_bytes(bytes))
92    }
93
94    /// Converts the `Address` instance to a Bech32 string representation.
95    ///
96    /// # Returns
97    /// - An `Ok(String)` containing the Bech32 string representation if successful.
98    /// - An `Err(DataError)` if the conversion fails.
99    ///
100    /// # Example
101    /// ```
102    /// # use novax_data::Address;
103    /// # let address = Address::from_bytes([0_u8; 32]);
104    /// let bech32_string = address.to_bech32_string().unwrap();
105    /// ```
106    pub fn to_bech32_string(&self) -> Result<String, DataError> {
107        let Ok(string) = self.0.to_bech32_string() else {
108            return Err(AddressError::CannotConvertToBech32String.into())
109        };
110
111        Ok(string)
112    }
113
114    /// Converts the `Address` instance to a byte array.
115    ///
116    /// # Returns
117    /// - A byte array of length 32.
118    ///
119    /// # Example
120    /// ```
121    /// # use novax_data::Address;
122    /// # let address = Address::from_bytes([0_u8; 32]);
123    /// let bytes = address.to_bytes();
124    /// assert_eq!(bytes, [0_u8; 32]);
125    /// ```
126    pub fn to_bytes(&self) -> [u8; 32] {
127        self.0.to_bytes()
128    }
129}
130
131
132impl Deref for Address {
133    type Target = multiversx_sdk::data::address::Address;
134
135    fn deref(&self) -> &Self::Target {
136        &self.0
137    }
138}
139
140impl Hash for Address {
141    fn hash<H: Hasher>(&self, state: &mut H) {
142        self.0.to_bytes().hash(state)
143    }
144}
145
146impl<M: ManagedTypeApi> NativeConvertible for ManagedAddress<M> {
147    type Native = Address;
148
149    fn to_native(&self) -> Self::Native {
150        Address(SDKAddress::from_bytes(self.to_byte_array()))
151    }
152}
153
154impl NativeConvertible for Address {
155    type Native = Self;
156
157    fn to_native(&self) -> Self::Native {
158        self.clone()
159    }
160}
161
162impl ManagedConvertible<ManagedAddress<StaticApi>> for Address {
163    fn to_managed(&self) -> ManagedAddress<StaticApi> {
164        ManagedAddress::from_address(&multiversx_sc::types::Address::from(self.to_bytes()))
165    }
166}
167
168impl TopDecode for Address {
169    fn top_decode<I>(input: I) -> Result<Self, DecodeError> where I: TopDecodeInput {
170        let bytes = ManagedAddress::<StaticApi>::top_decode(input)?.to_byte_array();
171        Ok(Address(SDKAddress::from_bytes(bytes)))
172    }
173}
174
175impl From<&Address> for AddressValue {
176    fn from(value: &Address) -> Self {
177        (&multiversx_sc::types::Address::from(value.0.to_bytes())).into()
178    }
179}
180
181impl From<SDKAddress> for Address {
182    fn from(value: SDKAddress) -> Self {
183        Address::from_bytes(value.to_bytes())
184    }
185}
186
187impl From<&SDKAddress> for Address {
188    fn from(value: &SDKAddress) -> Self {
189        Address::from_bytes(value.to_bytes())
190    }
191}
192
193impl From<&multiversx_sc::types::Address> for Address {
194    fn from(value: &multiversx_sc::types::Address) -> Self {
195        Address::from_bytes(*value.as_array())
196    }
197}
198
199impl From<multiversx_sc::types::Address> for Address {
200    fn from(value: multiversx_sc::types::Address) -> Self {
201        Address::from_bytes(*value.as_array())
202    }
203}
204
205impl From<Address> for multiversx_sc::types::Address {
206    fn from(value: Address) -> Self {
207        multiversx_sc::types::Address::from(value.to_bytes())
208    }
209}
210
211impl From<&str> for Address {
212    fn from(value: &str) -> Self {
213        if value.starts_with("erd1") {
214            Address::from_bech32_string(value).unwrap()
215        } else {
216            (&AddressValue::from(value).value).into()
217        }
218    }
219}
220
221impl From<&String> for Address {
222    fn from(value: &String) -> Self {
223        From::<&str>::from(value)
224    }
225}
226
227impl<'a> Deserialize<'a> for Address {
228    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'a> {
229        let string = String::deserialize(deserializer)?;
230
231        let Ok(address) = Address::from_bech32_string(&string) else {
232            return Err(D::Error::custom(format!("Cannot parse bech32 address : {string}")))
233        };
234
235        Ok(address)
236    }
237}
238
239#[cfg(test)]
240mod tests {
241    use multiversx_sc::types::ManagedAddress;
242    use multiversx_sc_scenario::api::StaticApi;
243    use crate::{Address, AddressError, DataError};
244    use crate::types::managed::ManagedConvertible;
245    use crate::types::native::NativeConvertible;
246
247    #[test]
248    fn test_managed_address_to_native() {
249        let expected = Address::from_bech32_string("erd1qqqqqqqqqqqqqpgq7ykazrzd905zvnlr88dpfw06677lxe9w0n4suz00uh").unwrap();
250        let managed_address: ManagedAddress<StaticApi> = ManagedAddress::from(expected.to_bytes());
251        let native = managed_address.to_native();
252
253        assert_eq!(
254            native.to_bytes(),
255            expected.to_bytes()
256        )
257    }
258
259    #[test]
260    fn test_managed_address_to_managed() {
261        let address = Address::from_bech32_string("erd1qqqqqqqqqqqqqpgq7ykazrzd905zvnlr88dpfw06677lxe9w0n4suz00uh").unwrap();
262        let managed = address.to_managed();
263
264        assert_eq!(
265            address.to_bytes(),
266            managed.to_byte_array()
267        )
268    }
269
270    #[test]
271    fn test_from_bech32_string_valid_address() {
272        Address::from_bech32_string("erd1an4xpn58j7ymd58m2jznr32t0vmas75egrdfa8mta6fzvqn9tkxq4jvghn").unwrap();
273    }
274
275    #[test]
276    fn test_from_bech32_string_invalid_address() {
277        let str = "erd1an4xpn58j7ymd58m2jznr32t0vmas75egrdfa8mta6fzvqn9tkxq4jvghm";
278        let error = Address::from_bech32_string(str).unwrap_err();
279
280        let expected = DataError::Address(AddressError::InvalidBech32String { invalid_value: str.to_string() });
281
282        assert_eq!(error, expected);
283    }
284
285    #[test]
286    fn test_from_bech32_string_invalid_address_bad_length() {
287        let str = "erd1an4xpn58j7ymd58m2jznr32t";
288        let error = Address::from_bech32_string(str).unwrap_err();
289
290        let expected = DataError::Address(AddressError::InvalidBech32String { invalid_value: str.to_string() });
291
292        assert_eq!(error, expected);
293    }
294
295}