iota_json_rpc_types/
lib.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5pub use balance_changes::*;
6use fastcrypto::encoding::{Base58, Base64};
7pub use iota_checkpoint::*;
8pub use iota_coin::*;
9pub use iota_event::*;
10pub use iota_extended::*;
11pub use iota_governance::*;
12pub use iota_indexer::*;
13pub use iota_move::*;
14pub use iota_object::*;
15pub use iota_protocol::*;
16pub use iota_transaction::*;
17use iota_types::base_types::ObjectID;
18pub use object_changes::*;
19use schemars::JsonSchema;
20use serde::{Deserialize, Serialize};
21use serde_with::serde_as;
22
23#[cfg(test)]
24#[path = "unit_tests/rpc_types_tests.rs"]
25mod rpc_types_tests;
26
27mod balance_changes;
28mod displays;
29mod iota_checkpoint;
30mod iota_coin;
31mod iota_event;
32mod iota_extended;
33mod iota_governance;
34mod iota_indexer;
35mod iota_move;
36mod iota_object;
37mod iota_protocol;
38mod iota_transaction;
39mod object_changes;
40
41pub type DynamicFieldPage = Page<DynamicFieldInfo, ObjectID>;
42/// `next_cursor` points to the last item in the page;
43/// Reading with `next_cursor` will start from the next item after `next_cursor`
44/// if `next_cursor` is `Some`, otherwise it will start from the first item.
45#[derive(Clone, Debug, JsonSchema, Serialize, Deserialize, PartialEq, Eq)]
46#[serde(rename_all = "camelCase")]
47pub struct Page<T, C> {
48    pub data: Vec<T>,
49    pub next_cursor: Option<C>,
50    pub has_next_page: bool,
51}
52
53impl<T, C> Page<T, C> {
54    pub fn empty() -> Self {
55        Self {
56            data: vec![],
57            next_cursor: None,
58            has_next_page: false,
59        }
60    }
61}
62
63#[serde_with::serde_as]
64#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug)]
65#[serde(rename_all = "camelCase")]
66pub struct DynamicFieldInfo {
67    pub name: iota_types::dynamic_field::DynamicFieldName,
68    #[serde(flatten)]
69    pub bcs_name: BcsName,
70    pub type_: iota_types::dynamic_field::DynamicFieldType,
71    pub object_type: String,
72    pub object_id: ObjectID,
73    pub version: iota_types::base_types::SequenceNumber,
74    pub digest: iota_types::digests::ObjectDigest,
75}
76
77impl From<iota_types::dynamic_field::DynamicFieldInfo> for DynamicFieldInfo {
78    fn from(
79        iota_types::dynamic_field::DynamicFieldInfo {
80            name,
81            bcs_name,
82            type_,
83            object_type,
84            object_id,
85            version,
86            digest,
87        }: iota_types::dynamic_field::DynamicFieldInfo,
88    ) -> Self {
89        Self {
90            name,
91            bcs_name: BcsName::new(bcs_name),
92            type_,
93            object_type,
94            object_id,
95            version,
96            digest,
97        }
98    }
99}
100
101#[serde_as]
102#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, JsonSchema)]
103#[serde(rename_all = "camelCase", tag = "bcsEncoding")]
104#[serde(from = "MaybeTaggedBcsName")]
105pub enum BcsName {
106    Base64 {
107        #[serde_as(as = "Base64")]
108        #[schemars(with = "Base64")]
109        #[serde(rename = "bcsName")]
110        bcs_name: Vec<u8>,
111    },
112    Base58 {
113        #[serde_as(as = "Base58")]
114        #[schemars(with = "Base58")]
115        #[serde(rename = "bcsName")]
116        bcs_name: Vec<u8>,
117    },
118}
119
120impl BcsName {
121    pub fn new(bytes: Vec<u8>) -> Self {
122        Self::Base64 { bcs_name: bytes }
123    }
124
125    pub fn bytes(&self) -> &[u8] {
126        match self {
127            BcsName::Base64 { bcs_name } => bcs_name.as_ref(),
128            BcsName::Base58 { bcs_name } => bcs_name.as_ref(),
129        }
130    }
131
132    pub fn into_bytes(self) -> Vec<u8> {
133        match self {
134            BcsName::Base64 { bcs_name } => bcs_name,
135            BcsName::Base58 { bcs_name } => bcs_name,
136        }
137    }
138}
139
140#[allow(unused)]
141#[serde_as]
142#[derive(Serialize, Deserialize)]
143#[serde(rename_all = "camelCase", untagged)]
144enum MaybeTaggedBcsName {
145    Tagged(TaggedBcsName),
146    Base58 {
147        #[serde_as(as = "Base58")]
148        #[serde(rename = "bcsName")]
149        bcs_name: Vec<u8>,
150    },
151}
152
153#[serde_as]
154#[derive(Serialize, Deserialize)]
155#[serde(rename_all = "camelCase", tag = "bcsEncoding")]
156enum TaggedBcsName {
157    Base64 {
158        #[serde_as(as = "Base64")]
159        #[serde(rename = "bcsName")]
160        bcs_name: Vec<u8>,
161    },
162    Base58 {
163        #[serde_as(as = "Base58")]
164        #[serde(rename = "bcsName")]
165        bcs_name: Vec<u8>,
166    },
167}
168
169impl From<MaybeTaggedBcsName> for BcsName {
170    fn from(name: MaybeTaggedBcsName) -> BcsName {
171        let bcs_name = match name {
172            MaybeTaggedBcsName::Tagged(TaggedBcsName::Base58 { bcs_name })
173            | MaybeTaggedBcsName::Base58 { bcs_name } => bcs_name,
174            MaybeTaggedBcsName::Tagged(TaggedBcsName::Base64 { bcs_name }) => bcs_name,
175        };
176
177        // Bytes are already decoded, force into Base64 variant to avoid serializing to
178        // base58
179        Self::Base64 { bcs_name }
180    }
181}
182
183#[cfg(test)]
184mod test {
185    use super::*;
186
187    #[test]
188    fn bcs_name_test() {
189        let bytes = vec![0, 1, 2, 3, 4];
190        let untagged_base58 = r#"{"bcsName":"12VfUX"}"#;
191        let tagged_base58 = r#"{"bcsEncoding":"base58","bcsName":"12VfUX"}"#;
192        let tagged_base64 = r#"{"bcsEncoding":"base64","bcsName":"AAECAwQ="}"#;
193
194        assert_eq!(
195            bytes,
196            serde_json::from_str::<BcsName>(untagged_base58)
197                .unwrap()
198                .into_bytes()
199        );
200        assert_eq!(
201            bytes,
202            serde_json::from_str::<BcsName>(tagged_base58)
203                .unwrap()
204                .into_bytes()
205        );
206        assert_eq!(
207            bytes,
208            serde_json::from_str::<BcsName>(tagged_base64)
209                .unwrap()
210                .into_bytes()
211        );
212
213        // Roundtrip base64
214        let name = serde_json::from_str::<BcsName>(tagged_base64).unwrap();
215        let json = serde_json::to_string(&name).unwrap();
216        let from_json = serde_json::from_str::<BcsName>(&json).unwrap();
217        assert_eq!(name, from_json);
218
219        // Roundtrip base58
220        let name = serde_json::from_str::<BcsName>(tagged_base58).unwrap();
221        let json = serde_json::to_string(&name).unwrap();
222        let from_json = serde_json::from_str::<BcsName>(&json).unwrap();
223        assert_eq!(name, from_json);
224    }
225}