Skip to main content

sui_json_rpc_types/
lib.rs

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