iota_sdk_types/
address.rs1#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
59#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
60#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
61pub struct Address(
62 #[cfg_attr(
63 feature = "serde",
64 serde(with = "::serde_with::As::<::serde_with::IfIsHumanReadable<ReadableAddress>>")
65 )]
66 [u8; Self::LENGTH],
67);
68
69impl Address {
70 pub const LENGTH: usize = 32;
71 pub const ZERO: Self = Self([0u8; Self::LENGTH]);
72 pub const STD_LIB: Self = Self::from_u8(1);
73 pub const FRAMEWORK: Self = Self::from_u8(2);
74 pub const SYSTEM: Self = Self::from_u8(3);
75
76 pub const fn new(bytes: [u8; Self::LENGTH]) -> Self {
77 Self(bytes)
78 }
79
80 pub(crate) const fn from_u8(byte: u8) -> Self {
81 let mut address = Self::ZERO;
82 address.0[31] = byte;
83 address
84 }
85
86 #[cfg(feature = "rand")]
87 #[cfg_attr(doc_cfg, doc(cfg(feature = "rand")))]
88 pub fn generate<R>(mut rng: R) -> Self
89 where
90 R: rand_core::RngCore + rand_core::CryptoRng,
91 {
92 let mut buf: [u8; Self::LENGTH] = [0; Self::LENGTH];
93 rng.fill_bytes(&mut buf);
94 Self::new(buf)
95 }
96
97 pub const fn into_inner(self) -> [u8; Self::LENGTH] {
99 self.0
100 }
101
102 pub const fn inner(&self) -> &[u8; Self::LENGTH] {
103 &self.0
104 }
105
106 pub const fn as_bytes(&self) -> &[u8] {
107 &self.0
108 }
109
110 pub fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, AddressParseError> {
111 let hex = hex.as_ref();
112
113 if !hex.starts_with(b"0x") {
114 return Err(AddressParseError);
115 }
116
117 let hex = &hex[2..];
118
119 if hex.len() < Self::LENGTH * 2 {
121 let mut buf = [b'0'; Self::LENGTH * 2];
122 let pad_length = (Self::LENGTH * 2) - hex.len();
123
124 buf[pad_length..].copy_from_slice(hex);
125
126 <[u8; Self::LENGTH] as hex::FromHex>::from_hex(buf)
127 } else {
128 <[u8; Self::LENGTH] as hex::FromHex>::from_hex(hex)
129 }
130 .map(Self)
131 .map_err(|_| AddressParseError)
133 }
134
135 pub fn to_hex(&self) -> String {
136 self.to_string()
137 }
138
139 pub fn from_bytes<T: AsRef<[u8]>>(bytes: T) -> Result<Self, AddressParseError> {
140 <[u8; Self::LENGTH]>::try_from(bytes.as_ref())
141 .map_err(|_| AddressParseError)
142 .map(Self)
143 }
144}
145
146impl std::str::FromStr for Address {
147 type Err = AddressParseError;
148
149 fn from_str(s: &str) -> Result<Self, Self::Err> {
150 Self::from_hex(s)
151 }
152}
153
154impl AsRef<[u8]> for Address {
155 fn as_ref(&self) -> &[u8] {
156 &self.0
157 }
158}
159
160impl AsRef<[u8; 32]> for Address {
161 fn as_ref(&self) -> &[u8; 32] {
162 &self.0
163 }
164}
165
166impl From<Address> for [u8; 32] {
167 fn from(address: Address) -> Self {
168 address.into_inner()
169 }
170}
171
172impl From<[u8; 32]> for Address {
173 fn from(address: [u8; 32]) -> Self {
174 Self::new(address)
175 }
176}
177
178impl From<Address> for Vec<u8> {
179 fn from(value: Address) -> Self {
180 value.0.to_vec()
181 }
182}
183
184impl From<super::ObjectId> for Address {
185 fn from(value: super::ObjectId) -> Self {
186 Self::new(value.into_inner())
187 }
188}
189
190impl std::fmt::Display for Address {
191 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
192 write!(f, "0x")?;
193 for byte in &self.0 {
194 write!(f, "{byte:02x}")?;
195 }
196
197 Ok(())
198 }
199}
200
201impl std::fmt::Debug for Address {
202 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
203 f.debug_tuple("Address")
204 .field(&format_args!("\"{self}\""))
205 .finish()
206 }
207}
208
209#[cfg(feature = "serde")]
210#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
211struct ReadableAddress;
212
213#[cfg(feature = "serde")]
214#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
215impl serde_with::SerializeAs<[u8; Address::LENGTH]> for ReadableAddress {
216 fn serialize_as<S>(source: &[u8; Address::LENGTH], serializer: S) -> Result<S::Ok, S::Error>
217 where
218 S: serde::Serializer,
219 {
220 let address = Address::new(*source);
221 serde_with::DisplayFromStr::serialize_as(&address, serializer)
222 }
223}
224
225#[cfg(feature = "serde")]
226#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
227impl<'de> serde_with::DeserializeAs<'de, [u8; Address::LENGTH]> for ReadableAddress {
228 fn deserialize_as<D>(deserializer: D) -> Result<[u8; Address::LENGTH], D::Error>
229 where
230 D: serde::Deserializer<'de>,
231 {
232 let address: Address = serde_with::DisplayFromStr::deserialize_as(deserializer)?;
233 Ok(address.into_inner())
234 }
235}
236
237#[derive(Clone, Copy, Debug, PartialEq, Eq)]
238pub struct AddressParseError;
239
240impl std::fmt::Display for AddressParseError {
241 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
242 write!(
243 f,
244 "Unable to parse Address (must be hex string of length {})",
245 2 * Address::LENGTH
246 )
247 }
248}
249
250impl std::error::Error for AddressParseError {}
251
252#[cfg(feature = "schemars")]
253impl schemars::JsonSchema for Address {
254 fn schema_name() -> String {
255 "Address".to_owned()
256 }
257
258 fn json_schema(_: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
259 use schemars::schema::{InstanceType, Metadata, SchemaObject, StringValidation};
260
261 let hex_length = Address::LENGTH * 2;
262 SchemaObject {
263 metadata: Some(Box::new(Metadata {
264 title: Some(Self::schema_name()),
265 description: Some("A 32-byte IOTA address, encoded as a hex string.".to_owned()),
266 examples: vec![serde_json::to_value(Address::FRAMEWORK).unwrap()],
267 ..Default::default()
268 })),
269 instance_type: Some(InstanceType::String.into()),
270 format: Some("hex".to_owned()),
271 string: Some(Box::new(StringValidation {
272 max_length: Some((hex_length + 2) as u32),
273 min_length: None,
274 pattern: Some(format!("0x[a-z0-9]{{1,{hex_length}}}")),
275 })),
276 ..Default::default()
277 }
278 .into()
279 }
280}
281
282#[cfg(test)]
283mod tests {
284 use test_strategy::proptest;
285 #[cfg(target_arch = "wasm32")]
286 use wasm_bindgen_test::wasm_bindgen_test as test;
287
288 use super::*;
289
290 #[test]
291 fn hex_parsing() {
292 let actual = Address::from_hex("0x2").unwrap();
293 let expected = "0x0000000000000000000000000000000000000000000000000000000000000002";
294
295 assert_eq!(actual.to_string(), expected);
296 }
297
298 #[test]
299 #[cfg(feature = "serde")]
300 fn formats() {
301 let actual = Address::from_hex("0x2").unwrap();
302
303 println!("{}", serde_json::to_string(&actual).unwrap());
304 println!("{:?}", bcs::to_bytes(&actual).unwrap());
305 let a: Address = serde_json::from_str("\"0x2\"").unwrap();
306 println!("{a}");
307 }
308
309 #[proptest]
310 fn roundtrip_display_fromstr(address: Address) {
311 let s = address.to_string();
312 let a = s.parse::<Address>().unwrap();
313 assert_eq!(address, a);
314 }
315}