sui_sdk_types/
address.rs

1/// Unique identifier for an Account on the Sui blockchain.
2///
3/// An `Address` is a 32-byte pseudonymous identifier used to uniquely identify an account and
4/// asset-ownership on the Sui blockchain. Often, human-readable addresses are encoded in
5/// hexadecimal with a `0x` prefix. For example, this is a valid Sui address:
6/// `0x02a212de6a9dfa3a69e22387acfbafbb1a9e591bd9d636e7895dcfc8de05f331`.
7///
8/// ```
9/// use sui_sdk_types::Address;
10///
11/// let hex = "0x02a212de6a9dfa3a69e22387acfbafbb1a9e591bd9d636e7895dcfc8de05f331";
12/// let address = Address::from_hex(hex).unwrap();
13/// println!("Address: {}", address);
14/// assert_eq!(hex, address.to_string());
15/// ```
16///
17/// # Deriving an account Address
18///
19/// Account addresses are cryptographically derived from a number of user account authenticators,
20/// the simplest of which is an [`Ed25519PublicKey`](crate::Ed25519PublicKey).
21///
22/// Deriving an address consists of the Blake2b256 hash of the sequence of bytes of its
23/// corresponding authenticator, prefixed with a domain-separator. For each authenticator, this
24/// domain-separator is the single byte-value of its [`SignatureScheme`](crate::SignatureScheme)
25/// flag. E.g. `hash(signature schema flag || authenticator bytes)`.
26///
27/// Each authenticator includes a convince method for deriving its `Address` as well as
28/// documentation for the specifics of how the derivation is done. See
29/// [`Ed25519PublicKey::derive_address`] for an example.
30///
31/// [`Ed25519PublicKey::derive_address`]: crate::Ed25519PublicKey::derive_address
32///
33/// # Usage as ObjectIds
34///
35/// `Address`es are also used as a way to uniquely identify an [`Object`]. When an `Address` is
36/// used as an object identifierit can also be referred to as an `ObjectId`. `ObjectId`s and
37/// account `Address`es share the same 32-byte addressable space but are derived leveraging
38/// different domain-separator values to ensure, cryptographically, that there won't be any
39/// overlap, e.g. there can't be a valid `Object` whose `ObjectId` is equal to that of the
40/// `Address` of a user account.
41///
42/// [`Object`]: crate::Object
43///
44/// # BCS
45///
46/// An `Address`'s BCS serialized form is defined by the following:
47///
48/// ```text
49/// address = 32OCTET
50/// ```
51#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
52#[cfg_attr(
53    feature = "serde",
54    derive(serde_derive::Serialize, serde_derive::Deserialize)
55)]
56#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
57#[doc(alias = "ObjectId")]
58pub struct Address(
59    #[cfg_attr(
60        feature = "serde",
61        serde(with = "::serde_with::As::<::serde_with::IfIsHumanReadable<ReadableAddress>>")
62    )]
63    [u8; Self::LENGTH],
64);
65
66impl Address {
67    pub const LENGTH: usize = 32;
68    pub const ZERO: Self = Self([0u8; Self::LENGTH]);
69    pub const TWO: Self = Self::from_u8(2);
70    pub const THREE: Self = Self::from_u8(3);
71
72    pub const fn new(bytes: [u8; Self::LENGTH]) -> Self {
73        Self(bytes)
74    }
75
76    const fn from_u8(byte: u8) -> Self {
77        let mut address = Self::ZERO;
78        address.0[31] = byte;
79        address
80    }
81
82    #[cfg(feature = "rand")]
83    #[cfg_attr(doc_cfg, doc(cfg(feature = "rand")))]
84    pub fn generate<R>(mut rng: R) -> Self
85    where
86        R: rand_core::RngCore + rand_core::CryptoRng,
87    {
88        let mut buf: [u8; Self::LENGTH] = [0; Self::LENGTH];
89        rng.fill_bytes(&mut buf);
90        Self::new(buf)
91    }
92
93    /// Return the underlying byte array of a Address.
94    pub const fn into_inner(self) -> [u8; Self::LENGTH] {
95        self.0
96    }
97
98    pub const fn inner(&self) -> &[u8; Self::LENGTH] {
99        &self.0
100    }
101
102    pub const fn as_bytes(&self) -> &[u8] {
103        &self.0
104    }
105
106    pub fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, AddressParseError> {
107        let hex = hex.as_ref();
108
109        if !hex.starts_with(b"0x") {
110            return Err(AddressParseError);
111        }
112
113        let hex = &hex[2..];
114
115        // If the string is too short we'll need to pad with 0's
116        if hex.len() < Self::LENGTH * 2 {
117            let mut buf = [b'0'; Self::LENGTH * 2];
118            let pad_length = (Self::LENGTH * 2) - hex.len();
119
120            buf[pad_length..].copy_from_slice(hex);
121
122            <[u8; Self::LENGTH] as hex::FromHex>::from_hex(buf)
123        } else {
124            <[u8; Self::LENGTH] as hex::FromHex>::from_hex(hex)
125        }
126        .map(Self)
127        //TODO fix error to contain hex parse error
128        .map_err(|_| AddressParseError)
129    }
130
131    pub fn to_hex(&self) -> String {
132        self.to_string()
133    }
134
135    pub fn from_bytes<T: AsRef<[u8]>>(bytes: T) -> Result<Self, AddressParseError> {
136        <[u8; Self::LENGTH]>::try_from(bytes.as_ref())
137            .map_err(|_| AddressParseError)
138            .map(Self)
139    }
140}
141
142impl std::str::FromStr for Address {
143    type Err = AddressParseError;
144
145    fn from_str(s: &str) -> Result<Self, Self::Err> {
146        Self::from_hex(s)
147    }
148}
149
150impl AsRef<[u8]> for Address {
151    fn as_ref(&self) -> &[u8] {
152        &self.0
153    }
154}
155
156impl AsRef<[u8; 32]> for Address {
157    fn as_ref(&self) -> &[u8; 32] {
158        &self.0
159    }
160}
161
162impl From<Address> for [u8; 32] {
163    fn from(address: Address) -> Self {
164        address.into_inner()
165    }
166}
167
168impl From<[u8; 32]> for Address {
169    fn from(address: [u8; 32]) -> Self {
170        Self::new(address)
171    }
172}
173
174impl From<Address> for Vec<u8> {
175    fn from(value: Address) -> Self {
176        value.0.to_vec()
177    }
178}
179
180impl std::fmt::Display for Address {
181    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
182        write!(f, "0x")?;
183        for byte in &self.0 {
184            write!(f, "{byte:02x}")?;
185        }
186
187        Ok(())
188    }
189}
190
191impl std::fmt::Debug for Address {
192    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
193        f.debug_tuple("Address")
194            .field(&format_args!("\"{self}\""))
195            .finish()
196    }
197}
198
199#[cfg(feature = "serde")]
200#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
201struct ReadableAddress;
202
203#[cfg(feature = "serde")]
204#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
205impl serde_with::SerializeAs<[u8; Address::LENGTH]> for ReadableAddress {
206    fn serialize_as<S>(source: &[u8; Address::LENGTH], serializer: S) -> Result<S::Ok, S::Error>
207    where
208        S: serde::Serializer,
209    {
210        let address = Address::new(*source);
211        serde_with::DisplayFromStr::serialize_as(&address, serializer)
212    }
213}
214
215#[cfg(feature = "serde")]
216#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
217impl<'de> serde_with::DeserializeAs<'de, [u8; Address::LENGTH]> for ReadableAddress {
218    fn deserialize_as<D>(deserializer: D) -> Result<[u8; Address::LENGTH], D::Error>
219    where
220        D: serde::Deserializer<'de>,
221    {
222        let address: Address = serde_with::DisplayFromStr::deserialize_as(deserializer)?;
223        Ok(address.into_inner())
224    }
225}
226
227#[derive(Clone, Copy, Debug, PartialEq, Eq)]
228pub struct AddressParseError;
229
230impl std::fmt::Display for AddressParseError {
231    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
232        write!(
233            f,
234            "Unable to parse Address (must be hex string of length {})",
235            Address::LENGTH
236        )
237    }
238}
239
240impl std::error::Error for AddressParseError {}
241
242#[cfg(test)]
243mod test {
244    use super::*;
245    use test_strategy::proptest;
246
247    #[cfg(target_arch = "wasm32")]
248    use wasm_bindgen_test::wasm_bindgen_test as test;
249
250    #[test]
251    fn hex_parsing() {
252        let actual = Address::from_hex("0x2").unwrap();
253        let expected = "0x0000000000000000000000000000000000000000000000000000000000000002";
254
255        assert_eq!(actual.to_string(), expected);
256    }
257
258    #[test]
259    #[cfg(feature = "serde")]
260    fn formats() {
261        let actual = Address::from_hex("0x2").unwrap();
262
263        println!("{}", serde_json::to_string(&actual).unwrap());
264        println!("{:?}", bcs::to_bytes(&actual).unwrap());
265        let a: Address = serde_json::from_str("\"0x2\"").unwrap();
266        println!("{a}");
267    }
268
269    #[proptest]
270    fn roundtrip_display_fromstr(address: Address) {
271        let s = address.to_string();
272        let a = s.parse::<Address>().unwrap();
273        assert_eq!(address, a);
274    }
275}