iota_json_rpc_types/
iota_primitives.rs

1// Copyright (c) 2026 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4//! JSON Schema adapter types for the IOTA JSON-RPC surface, applied at field
5//! sites via `#[serde_as(as = "...")]`. Core serde behaviour lives in
6//! `iota_types::iota_serde`; this module adds the `schemars::JsonSchema` layer
7//! on top (the `iota-types` crate intentionally has no `schemars` dependency).
8//!
9//! To add a new adapter, prefer a unit marker struct with a manual
10//! `JsonSchema` impl for explicit control over description, format, and shape.
11//! If custom serialisation is needed, delegate `SerializeAs` / `DeserializeAs`
12//! to the corresponding adapter in `iota_types::iota_serde` so the two crates
13//! cannot drift. Newtype wrappers (e.g. `SequenceNumberString(u64)`) are only
14//! appropriate when the wrapper itself is the serialised value.
15
16use iota_types::{
17    base_types::{StructTag as NativeStructTag, TypeTag as NativeTypeTag},
18    iota_serde::{IotaStructTag, IotaTypeTag},
19};
20use schemars::{
21    JsonSchema,
22    schema::{InstanceType, Metadata, NumberValidation, SchemaObject},
23};
24use serde::{Deserialize, Deserializer, Serialize, Serializer};
25use serde_with::{DeserializeAs, DisplayFromStr, SerializeAs, serde_as};
26
27/// A schema type that defines the JSON representation of the
28/// [`IotaAddress`](iota_types::base_types::IotaAddress) type.
29pub struct IotaAddress;
30
31impl JsonSchema for IotaAddress {
32    fn schema_name() -> String {
33        "IotaAddress".to_owned()
34    }
35
36    fn json_schema(_: &mut schemars::r#gen::SchemaGenerator) -> schemars::schema::Schema {
37        SchemaObject {
38            metadata: Some(Box::new(Metadata {
39                description: Some("IOTA address as a hex string".to_owned()),
40                ..Default::default()
41            })),
42            instance_type: Some(InstanceType::String.into()),
43            format: Some("hex".to_owned()),
44            ..Default::default()
45        }
46        .into()
47    }
48}
49
50/// A schema type that defines the JSON representation of the
51/// [`ObjectID`](iota_types::base_types::ObjectID) type.
52pub struct ObjectID;
53
54impl JsonSchema for ObjectID {
55    fn schema_name() -> String {
56        "ObjectID".to_owned()
57    }
58
59    fn json_schema(_: &mut schemars::r#gen::SchemaGenerator) -> schemars::schema::Schema {
60        SchemaObject {
61            metadata: Some(Box::new(Metadata {
62                description: Some("Object ID as a hex string".to_owned()),
63                ..Default::default()
64            })),
65            instance_type: Some(InstanceType::String.into()),
66            format: Some("hex".to_owned()),
67            ..Default::default()
68        }
69        .into()
70    }
71}
72
73/// A schema type that defines the JSON representation of the
74/// [`SequenceNumber`](iota_types::base_types::SequenceNumber) type as a string
75/// and provides an alternate serialization usable via `#[serde_as]`.
76#[serde_as]
77#[derive(Serialize, Deserialize)]
78pub struct SequenceNumberString(#[serde_as(as = "DisplayFromStr")] u64);
79
80impl JsonSchema for SequenceNumberString {
81    fn schema_name() -> String {
82        "SequenceNumberString".to_owned()
83    }
84
85    fn json_schema(_: &mut schemars::r#gen::SchemaGenerator) -> schemars::schema::Schema {
86        SchemaObject {
87            metadata: Some(Box::new(Metadata {
88                description: Some("Sequence number as a string".to_owned()),
89                ..Default::default()
90            })),
91            instance_type: Some(InstanceType::String.into()),
92            ..Default::default()
93        }
94        .into()
95    }
96}
97
98impl SerializeAs<iota_types::base_types::SequenceNumber> for SequenceNumberString {
99    fn serialize_as<S>(
100        source: &iota_types::base_types::SequenceNumber,
101        serializer: S,
102    ) -> Result<S::Ok, S::Error>
103    where
104        S: Serializer,
105    {
106        SequenceNumberString(source.as_u64()).serialize(serializer)
107    }
108}
109
110impl<'de> DeserializeAs<'de, iota_types::base_types::SequenceNumber> for SequenceNumberString {
111    fn deserialize_as<D>(
112        deserializer: D,
113    ) -> Result<iota_types::base_types::SequenceNumber, D::Error>
114    where
115        D: Deserializer<'de>,
116    {
117        let schema = SequenceNumberString::deserialize(deserializer)?;
118        Ok(iota_types::base_types::SequenceNumber::from_u64(schema.0))
119    }
120}
121
122/// A schema type that defines the JSON representation of the
123/// [`SequenceNumber`](iota_types::base_types::SequenceNumber) type as a u64
124/// integer and uses the default serialization.
125pub struct SequenceNumberU64;
126
127impl JsonSchema for SequenceNumberU64 {
128    fn schema_name() -> String {
129        "SequenceNumberU64".to_owned()
130    }
131
132    fn json_schema(_: &mut schemars::r#gen::SchemaGenerator) -> schemars::schema::Schema {
133        SchemaObject {
134            metadata: Some(Box::new(Metadata {
135                description: Some("Sequence number as a u64 integer".to_owned()),
136                ..Default::default()
137            })),
138            format: Some("uint64".to_owned()),
139            number: Some(Box::new(NumberValidation {
140                minimum: Some(0.0),
141                ..Default::default()
142            })),
143            instance_type: Some(InstanceType::Integer.into()),
144            ..Default::default()
145        }
146        .into()
147    }
148}
149
150/// A schema type that defines the JSON representation of the
151/// [`ProtocolVersion`](iota_protocol_config::ProtocolVersion) type as a string
152/// and provides an alternate serialization usable via `#[serde_as]`.
153#[serde_as]
154#[derive(Serialize, Deserialize, JsonSchema)]
155pub struct ProtocolVersion(
156    #[schemars(with = "String")]
157    #[serde_as(as = "DisplayFromStr")]
158    u64,
159);
160
161impl SerializeAs<iota_protocol_config::ProtocolVersion> for ProtocolVersion {
162    fn serialize_as<S>(
163        source: &iota_protocol_config::ProtocolVersion,
164        serializer: S,
165    ) -> Result<S::Ok, S::Error>
166    where
167        S: Serializer,
168    {
169        ProtocolVersion(source.as_u64()).serialize(serializer)
170    }
171}
172
173impl<'de> DeserializeAs<'de, iota_protocol_config::ProtocolVersion> for ProtocolVersion {
174    fn deserialize_as<D>(deserializer: D) -> Result<iota_protocol_config::ProtocolVersion, D::Error>
175    where
176        D: Deserializer<'de>,
177    {
178        let schema = ProtocolVersion::deserialize(deserializer)?;
179        Ok(iota_protocol_config::ProtocolVersion::new(schema.0))
180    }
181}
182
183/// A schema type that defines the JSON representation of a Base58 encoded
184/// string. A custom JsonSchema impl is necessary to add the "base58" format to
185/// the schema.
186pub struct Base58;
187
188impl JsonSchema for Base58 {
189    fn schema_name() -> String {
190        "Base58".to_owned()
191    }
192
193    fn json_schema(_: &mut schemars::r#gen::SchemaGenerator) -> schemars::schema::Schema {
194        SchemaObject {
195            metadata: Some(Box::new(Metadata {
196                description: Some("Base58 encoded data".to_owned()),
197                ..Default::default()
198            })),
199            instance_type: Some(InstanceType::String.into()),
200            format: Some("base58".to_owned()),
201            ..Default::default()
202        }
203        .into()
204    }
205}
206
207/// A schema type that defines the JSON representation of a Base64 encoded
208/// string. A custom JsonSchema impl is necessary to add the "base64" format to
209/// the schema.
210pub struct Base64;
211
212impl JsonSchema for Base64 {
213    fn schema_name() -> String {
214        "Base64".to_owned()
215    }
216
217    fn json_schema(_: &mut schemars::r#gen::SchemaGenerator) -> schemars::schema::Schema {
218        SchemaObject {
219            metadata: Some(Box::new(Metadata {
220                description: Some("Base64 encoded data".to_owned()),
221                ..Default::default()
222            })),
223            instance_type: Some(InstanceType::String.into()),
224            format: Some("base64".to_owned()),
225            ..Default::default()
226        }
227        .into()
228    }
229}
230
231/// A schema type that defines the JSON representation of a Base64 encoded
232/// signature.
233pub struct GenericSignature;
234
235impl JsonSchema for GenericSignature {
236    fn schema_name() -> String {
237        "GenericSignature".to_owned()
238    }
239
240    fn json_schema(_: &mut schemars::r#gen::SchemaGenerator) -> schemars::schema::Schema {
241        SchemaObject {
242            metadata: Some(Box::new(Metadata {
243                description: Some("Base64 encoded signature".to_owned()),
244                ..Default::default()
245            })),
246            instance_type: Some(InstanceType::String.into()),
247            format: Some("base64".to_owned()),
248            ..Default::default()
249        }
250        .into()
251    }
252}
253
254/// A schema type that defines the JSON representation of a Move
255/// [`StructTag`](iota_types::base_types::StructTag) as a string, and
256/// provides a string serialization usable via `#[serde_as]`.
257pub struct StructTag;
258
259impl JsonSchema for StructTag {
260    fn schema_name() -> String {
261        "StructTag".to_owned()
262    }
263
264    fn json_schema(_: &mut schemars::r#gen::SchemaGenerator) -> schemars::schema::Schema {
265        SchemaObject {
266            metadata: Some(Box::new(Metadata {
267                description: Some(
268                    "Move struct tag, in the format 'address::module::name<type_params>'"
269                        .to_owned(),
270                ),
271                ..Default::default()
272            })),
273            instance_type: Some(InstanceType::String.into()),
274            ..Default::default()
275        }
276        .into()
277    }
278}
279
280impl SerializeAs<NativeStructTag> for StructTag {
281    fn serialize_as<S>(value: &NativeStructTag, serializer: S) -> Result<S::Ok, S::Error>
282    where
283        S: Serializer,
284    {
285        IotaStructTag::serialize_as(value, serializer)
286    }
287}
288
289impl<'de> DeserializeAs<'de, NativeStructTag> for StructTag {
290    fn deserialize_as<D>(deserializer: D) -> Result<NativeStructTag, D::Error>
291    where
292        D: Deserializer<'de>,
293    {
294        IotaStructTag::deserialize_as(deserializer)
295    }
296}
297
298/// A schema type that defines the JSON representation of a Move
299/// [`TypeTag`](iota_types::base_types::TypeTag) as a string, and
300/// provides a string serialization usable via `#[serde_as]`.
301pub struct TypeTag;
302
303impl JsonSchema for TypeTag {
304    fn schema_name() -> String {
305        "TypeTag".to_owned()
306    }
307
308    fn json_schema(_: &mut schemars::r#gen::SchemaGenerator) -> schemars::schema::Schema {
309        SchemaObject {
310            metadata: Some(Box::new(Metadata {
311                description: Some("Move type tag as a string".to_owned()),
312                ..Default::default()
313            })),
314            instance_type: Some(InstanceType::String.into()),
315            ..Default::default()
316        }
317        .into()
318    }
319}
320
321impl SerializeAs<NativeTypeTag> for TypeTag {
322    fn serialize_as<S>(value: &NativeTypeTag, serializer: S) -> Result<S::Ok, S::Error>
323    where
324        S: Serializer,
325    {
326        IotaTypeTag::serialize_as(value, serializer)
327    }
328}
329
330impl<'de> DeserializeAs<'de, NativeTypeTag> for TypeTag {
331    fn deserialize_as<D>(deserializer: D) -> Result<NativeTypeTag, D::Error>
332    where
333        D: Deserializer<'de>,
334    {
335        IotaTypeTag::deserialize_as(deserializer)
336    }
337}
338
339/// A schema type that defines the JSON representation of a Move identifier,
340/// and provides a string serialization usable via `#[serde_as]`.
341pub struct Identifier;
342
343impl JsonSchema for Identifier {
344    fn schema_name() -> String {
345        "Identifier".to_owned()
346    }
347
348    fn json_schema(_: &mut schemars::r#gen::SchemaGenerator) -> schemars::schema::Schema {
349        SchemaObject {
350            metadata: Some(Box::new(Metadata {
351                description: Some("Move identifier".to_owned()),
352                ..Default::default()
353            })),
354            instance_type: Some(InstanceType::String.into()),
355            ..Default::default()
356        }
357        .into()
358    }
359}