sui_jsonrpc/
serde.rs

1use std::fmt::Display;
2use std::str::FromStr;
3
4use serde::{Deserialize, Deserializer, Serialize, Serializer};
5use serde_with::{DeserializeAs, DisplayFromStr, SerializeAs, serde_as};
6use sui_sdk_types::GasCostSummary;
7
8fn decode_base58(base58: &str) -> Result<Vec<u8>, bs58::decode::Error> {
9    bs58::decode(base58).into_vec()
10}
11
12/// Convenience method for decoding base64 bytes the way Sui expects.
13fn decode_base64_default(base64: &str) -> Result<Vec<u8>, base64ct::Error> {
14    // let mut result: Vec<u8> = Vec::<u8>::from(base64);
15    // let s = <base64ct::Base64 as base64ct::Encoding>::decode_in_place(&mut result)?;
16    // let len = s.len();
17    // result.truncate(len);
18    // Ok(result)
19
20    <base64ct::Base64 as base64ct::Encoding>::decode_vec(base64)
21}
22
23/// Convenience method for encoding bytes to base64 the way Sui expects.
24pub(crate) fn encode_base64_default(bytes: impl AsRef<[u8]>) -> String {
25    <base64ct::Base64 as base64ct::Encoding>::encode_string(bytes.as_ref())
26}
27
28// =============================================================================
29//  BigInt
30// =============================================================================
31
32#[serde_as]
33#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Copy)]
34pub struct BigInt<T>(#[serde_as(as = "serde_with::DisplayFromStr")] T)
35where
36    T: Display + FromStr,
37    <T as FromStr>::Err: Display;
38
39impl<T> BigInt<T>
40where
41    T: Display + FromStr,
42    <T as FromStr>::Err: Display,
43{
44    pub fn into_inner(self) -> T {
45        self.0
46    }
47}
48
49impl<T> SerializeAs<T> for BigInt<T>
50where
51    T: Display + FromStr + Copy,
52    <T as FromStr>::Err: Display,
53{
54    fn serialize_as<S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
55    where
56        S: Serializer,
57    {
58        Self(*value).serialize(serializer)
59    }
60}
61
62impl<'de, T> DeserializeAs<'de, T> for BigInt<T>
63where
64    T: Display + FromStr + Copy,
65    <T as FromStr>::Err: Display,
66{
67    fn deserialize_as<D>(deserializer: D) -> Result<T, D::Error>
68    where
69        D: Deserializer<'de>,
70    {
71        Ok(*Self::deserialize(deserializer)?)
72    }
73}
74
75impl<T> From<T> for BigInt<T>
76where
77    T: Display + FromStr,
78    <T as FromStr>::Err: Display,
79{
80    fn from(v: T) -> Self {
81        Self(v)
82    }
83}
84
85impl<T> std::ops::Deref for BigInt<T>
86where
87    T: Display + FromStr,
88    <T as FromStr>::Err: Display,
89{
90    type Target = T;
91
92    fn deref(&self) -> &Self::Target {
93        &self.0
94    }
95}
96
97impl<T> Display for BigInt<T>
98where
99    T: Display + FromStr,
100    <T as FromStr>::Err: Display,
101{
102    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103        write!(f, "{}", self.0)
104    }
105}
106
107// =============================================================================
108//  Base64orBase58
109// =============================================================================
110
111/// Always serialize as base64, but deserialize from either Base64 or Base58
112pub(crate) struct Base64orBase58;
113
114impl<T> SerializeAs<T> for Base64orBase58
115where
116    T: AsRef<[u8]>,
117{
118    fn serialize_as<S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
119    where
120        S: Serializer,
121    {
122        let encoded_string = encode_base64_default(value);
123        encoded_string.serialize(serializer)
124    }
125}
126
127impl<'de> DeserializeAs<'de, Vec<u8>> for Base64orBase58 {
128    fn deserialize_as<D>(deserializer: D) -> Result<Vec<u8>, D::Error>
129    where
130        D: Deserializer<'de>,
131    {
132        let s = String::deserialize(deserializer)?;
133
134        decode_base64_default(&s)
135            .or_else(|_| decode_base58(&s))
136            .map_err(|_| serde::de::Error::custom("Deserialization failed"))
137    }
138}
139
140#[serde_as]
141#[derive(Serialize, Deserialize)]
142#[serde(rename_all = "camelCase")]
143pub(crate) struct GasCostSummaryJson {
144    #[serde_as(as = "DisplayFromStr")]
145    computation_cost: u64,
146    #[serde_as(as = "DisplayFromStr")]
147    storage_cost: u64,
148    #[serde_as(as = "DisplayFromStr")]
149    storage_rebate: u64,
150    #[serde_as(as = "DisplayFromStr")]
151    non_refundable_storage_fee: u64,
152}
153
154impl From<GasCostSummaryJson> for GasCostSummary {
155    fn from(
156        GasCostSummaryJson {
157            computation_cost,
158            storage_cost,
159            storage_rebate,
160            non_refundable_storage_fee,
161        }: GasCostSummaryJson,
162    ) -> Self {
163        Self {
164            computation_cost,
165            storage_cost,
166            storage_rebate,
167            non_refundable_storage_fee,
168        }
169    }
170}
171
172impl From<GasCostSummary> for GasCostSummaryJson {
173    fn from(
174        GasCostSummary {
175            computation_cost,
176            storage_cost,
177            storage_rebate,
178            non_refundable_storage_fee,
179        }: GasCostSummary,
180    ) -> Self {
181        Self {
182            computation_cost,
183            storage_cost,
184            storage_rebate,
185            non_refundable_storage_fee,
186        }
187    }
188}
189
190#[cfg(test)]
191mod test {
192    use super::*;
193
194    #[serde_as]
195    #[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
196    #[serde(rename_all = "camelCase")]
197    struct Bcs {
198        #[serde_as(as = "Base64orBase58")]
199        bcs: Vec<u8>,
200    }
201
202    #[test]
203    fn new_bcs_format() {
204        let bytes = vec![0, 1, 2, 3, 4];
205        let untagged_base58 = r#"{"bcs":"12VfUX"}"#;
206        let tagged_base58 = r#"{"bcsEncoding":"base58","bcs":"12VfUX"}"#;
207        let tagged_base64 = r#"{"bcsEncoding":"base64","bcs":"AAECAwQ="}"#;
208
209        println!(
210            "{}",
211            serde_json::to_string(&Bcs { bcs: bytes.clone() }).unwrap()
212        );
213
214        assert_eq!(
215            bytes,
216            serde_json::from_str::<Bcs>(untagged_base58).unwrap().bcs
217        );
218        assert_eq!(
219            bytes,
220            serde_json::from_str::<Bcs>(tagged_base58).unwrap().bcs
221        );
222        assert_eq!(
223            bytes,
224            serde_json::from_str::<Bcs>(tagged_base64).unwrap().bcs
225        );
226
227        // Roundtrip base64
228        let name = serde_json::from_str::<Bcs>(tagged_base64).unwrap();
229        let json = serde_json::to_string(&name).unwrap();
230        let from_json = serde_json::from_str::<Bcs>(&json).unwrap();
231        assert_eq!(name, from_json);
232
233        // Roundtrip base58
234        let name = serde_json::from_str::<Bcs>(tagged_base58).unwrap();
235        let json = serde_json::to_string(&name).unwrap();
236        let from_json = serde_json::from_str::<Bcs>(&json).unwrap();
237        assert_eq!(name, from_json);
238    }
239}