moverox_types/
address.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4/// Corresponds to the `address` type in Move.
5///
6/// It is a 32-byte pseudonymous identifier used to uniquely identify an account and asset-ownership
7/// on the blockchain.
8///
9/// Often, human-readable addresses are encoded in hexadecimal with a `0x` prefix. For example, this
10/// is a valid address: `0x02a212de6a9dfa3a69e22387acfbafbb1a9e591bd9d636e7895dcfc8de05f331`.
11///
12/// ```
13/// use moverox_types::Address;
14///
15/// let hex = "0x02a212de6a9dfa3a69e22387acfbafbb1a9e591bd9d636e7895dcfc8de05f331";
16/// let address = Address::from_hex(hex).unwrap();
17/// println!("Address: {}", address);
18/// assert_eq!(hex, address.to_string());
19/// ```
20///
21/// # BCS
22///
23/// An `Address`'s BCS serialized form is defined by the following:
24///
25/// ```text
26/// address = 32OCTET
27/// ```
28#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
29#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
30#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
31pub struct Address(
32    #[cfg_attr(
33        feature = "serde",
34        serde(with = "::serde_with::As::<::serde_with::IfIsHumanReadable<ReadableAddress>>")
35    )]
36    [u8; Self::LENGTH],
37);
38
39impl Address {
40    pub const LENGTH: usize = 32;
41    pub const ZERO: Self = Self([0u8; Self::LENGTH]);
42    pub const TWO: Self = Self::from_u8(2);
43    pub const THREE: Self = Self::from_u8(3);
44
45    pub const fn new(bytes: [u8; Self::LENGTH]) -> Self {
46        Self(bytes)
47    }
48
49    const fn from_u8(byte: u8) -> Self {
50        let mut address = Self::ZERO;
51        address.0[31] = byte;
52        address
53    }
54
55    /// Return the underlying byte array of a Address.
56    pub const fn into_inner(self) -> [u8; Self::LENGTH] {
57        self.0
58    }
59
60    pub const fn inner(&self) -> &[u8; Self::LENGTH] {
61        &self.0
62    }
63
64    pub const fn as_bytes(&self) -> &[u8] {
65        &self.0
66    }
67
68    pub fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, AddressParseError> {
69        let hex = hex.as_ref();
70
71        if !hex.starts_with(b"0x") {
72            return Err(AddressParseError);
73        }
74
75        let hex = &hex[2..];
76
77        // If the string is too short we'll need to pad with 0's
78        if hex.len() < Self::LENGTH * 2 {
79            let mut buf = [b'0'; Self::LENGTH * 2];
80            let pad_length = (Self::LENGTH * 2) - hex.len();
81
82            buf[pad_length..].copy_from_slice(hex);
83
84            <[u8; Self::LENGTH] as const_hex::FromHex>::from_hex(buf)
85        } else {
86            <[u8; Self::LENGTH] as const_hex::FromHex>::from_hex(hex)
87        }
88        .map(Self)
89        //TODO fix error to contain hex parse error
90        .map_err(|_| AddressParseError)
91    }
92
93    pub fn as_hex(&self) -> String {
94        self.to_string()
95    }
96
97    pub fn from_bytes<T: AsRef<[u8]>>(bytes: T) -> Result<Self, AddressParseError> {
98        <[u8; Self::LENGTH]>::try_from(bytes.as_ref())
99            .map_err(|_| AddressParseError)
100            .map(Self)
101    }
102}
103
104impl std::str::FromStr for Address {
105    type Err = AddressParseError;
106
107    fn from_str(s: &str) -> Result<Self, Self::Err> {
108        Self::from_hex(s)
109    }
110}
111
112impl AsRef<[u8]> for Address {
113    fn as_ref(&self) -> &[u8] {
114        &self.0
115    }
116}
117
118impl AsRef<[u8; 32]> for Address {
119    fn as_ref(&self) -> &[u8; 32] {
120        &self.0
121    }
122}
123
124impl From<Address> for [u8; 32] {
125    fn from(address: Address) -> Self {
126        address.into_inner()
127    }
128}
129
130impl From<[u8; 32]> for Address {
131    fn from(address: [u8; 32]) -> Self {
132        Self::new(address)
133    }
134}
135
136impl From<Address> for Vec<u8> {
137    fn from(value: Address) -> Self {
138        value.0.to_vec()
139    }
140}
141
142impl std::fmt::Display for Address {
143    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
144        write!(f, "0x")?;
145        for byte in &self.0 {
146            write!(f, "{byte:02x}")?;
147        }
148
149        Ok(())
150    }
151}
152
153impl std::fmt::Debug for Address {
154    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
155        f.debug_tuple("Address")
156            .field(&format_args!("\"{self}\""))
157            .finish()
158    }
159}
160
161#[cfg(feature = "serde")]
162struct ReadableAddress;
163
164#[cfg(feature = "serde")]
165impl serde_with::SerializeAs<[u8; Address::LENGTH]> for ReadableAddress {
166    fn serialize_as<S>(source: &[u8; Address::LENGTH], serializer: S) -> Result<S::Ok, S::Error>
167    where
168        S: serde::Serializer,
169    {
170        let address = Address::new(*source);
171        serde_with::DisplayFromStr::serialize_as(&address, serializer)
172    }
173}
174
175#[cfg(feature = "serde")]
176impl<'de> serde_with::DeserializeAs<'de, [u8; Address::LENGTH]> for ReadableAddress {
177    fn deserialize_as<D>(deserializer: D) -> Result<[u8; Address::LENGTH], D::Error>
178    where
179        D: serde::Deserializer<'de>,
180    {
181        let address: Address = serde_with::DisplayFromStr::deserialize_as(deserializer)?;
182        Ok(address.into_inner())
183    }
184}
185
186#[derive(Clone, Copy, Debug, PartialEq, Eq)]
187pub struct AddressParseError;
188
189impl std::fmt::Display for AddressParseError {
190    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
191        write!(
192            f,
193            "Unable to parse Address (must be hex string of length {})",
194            Address::LENGTH
195        )
196    }
197}
198
199impl std::error::Error for AddressParseError {}
200
201#[cfg(test)]
202mod test {
203    use super::*;
204
205    #[test]
206    fn hex_parsing() {
207        let actual = Address::from_hex("0x2").unwrap();
208        let expected = "0x0000000000000000000000000000000000000000000000000000000000000002";
209
210        assert_eq!(actual.to_string(), expected);
211    }
212
213    #[test]
214    #[cfg(feature = "serde")]
215    fn formats() {
216        let actual = Address::from_hex("0x2").unwrap();
217
218        println!("{}", serde_json::to_string(&actual).unwrap());
219        println!("{:?}", bcs::to_bytes(&actual).unwrap());
220        let a: Address = serde_json::from_str("\"0x2\"").unwrap();
221        println!("{a}");
222    }
223
224    #[cfg(feature = "proptest")]
225    #[test_strategy::proptest]
226    fn roundtrip_display_fromstr(address: Address) {
227        let s = address.to_string();
228        let a = s.parse::<Address>().unwrap();
229        assert_eq!(address, a);
230    }
231}