provwasm_std/
shim.rs

1use std::fmt;
2use std::str::FromStr;
3
4use ::serde::{ser, Deserialize, Deserializer, Serialize, Serializer};
5use chrono::{DateTime, Utc};
6use cosmwasm_std::{Binary, StdResult};
7use serde::de;
8use serde::de::Visitor;
9use serde::ser::SerializeMap;
10
11#[derive(Clone, Copy, PartialEq, Eq, ::prost::Message, schemars::JsonSchema)]
12pub struct Timestamp {
13    /// Represents seconds of UTC time since Unix epoch
14    /// 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
15    /// 9999-12-31T23:59:59Z inclusive.
16    #[prost(int64, tag = "1")]
17    pub seconds: i64,
18    /// Non-negative fractions of a second at nanosecond resolution. Negative
19    /// second values with fractions must still have non-negative nanos values
20    /// that count forward in time. Must be from 0 to 999,999,999
21    /// inclusive.
22    #[prost(int32, tag = "2")]
23    pub nanos: i32,
24}
25
26impl Serialize for Timestamp {
27    fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
28    where
29        S: Serializer,
30    {
31        let mut ts = prost_types::Timestamp {
32            seconds: self.seconds,
33            nanos: self.nanos,
34        };
35        ts.normalize();
36        let dt = DateTime::from_timestamp(ts.seconds, ts.nanos as u32).ok_or_else(|| {
37            ser::Error::custom(format!(
38                "failed to parse as NativeDateTime: seconds: {}, nanos: {}",
39                self.seconds, self.nanos
40            ))
41        })?;
42        serializer.serialize_str(format!("{:?}", dt).as_str())
43    }
44}
45
46impl<'de> Deserialize<'de> for Timestamp {
47    fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
48    where
49        D: Deserializer<'de>,
50    {
51        struct TimestampVisitor;
52
53        impl<'de> Visitor<'de> for TimestampVisitor {
54            type Value = Timestamp;
55
56            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
57                formatter.write_str("Timestamp in RFC3339 format")
58            }
59
60            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
61            where
62                E: de::Error,
63            {
64                let utc: DateTime<Utc> = chrono::DateTime::from_str(value).map_err(|err| {
65                    serde::de::Error::custom(format!(
66                        "Failed to parse {} as datetime: {:?}",
67                        value, err
68                    ))
69                })?;
70                let ts = Timestamp::from(utc);
71                Ok(ts)
72            }
73        }
74        deserializer.deserialize_str(TimestampVisitor)
75    }
76}
77
78impl From<DateTime<Utc>> for Timestamp {
79    fn from(dt: DateTime<Utc>) -> Self {
80        Timestamp {
81            seconds: dt.timestamp(),
82            nanos: dt.timestamp_subsec_nanos() as i32,
83        }
84    }
85}
86#[derive(Clone, Copy, PartialEq, Eq, ::prost::Message, schemars::JsonSchema)]
87pub struct Duration {
88    /// Signed seconds of the span of time. Must be from -315,576,000,000
89    /// to +315,576,000,000 inclusive. Note: these bounds are computed from:
90    /// 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years
91    #[prost(int64, tag = "1")]
92    pub seconds: i64,
93    /// Signed fractions of a second at nanosecond resolution of the span
94    /// of time. Durations less than one second are represented with a 0
95    /// `seconds` field and a positive or negative `nanos` field. For durations
96    /// of one second or more, a non-zero value for the `nanos` field must be
97    /// of the same sign as the `seconds` field. Must be from -999,999,999
98    /// to +999,999,999 inclusive.
99    #[prost(int32, tag = "2")]
100    pub nanos: i32,
101}
102
103impl Serialize for Duration {
104    fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
105    where
106        S: Serializer,
107    {
108        let mut d = prost_types::Duration::from(self.to_owned());
109        d.normalize();
110
111        serializer.serialize_str(d.to_string().as_str())
112    }
113}
114
115impl<'de> Deserialize<'de> for Duration {
116    fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
117    where
118        D: Deserializer<'de>,
119    {
120        struct DurationVisitor;
121
122        impl<'de> Visitor<'de> for DurationVisitor {
123            type Value = Duration;
124
125            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
126                formatter.write_str("Timestamp in RFC3339 format")
127            }
128
129            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
130            where
131                E: de::Error,
132            {
133                value
134                    .parse::<prost_types::Duration>()
135                    .map(Into::into)
136                    .map_err(de::Error::custom)
137            }
138        }
139        deserializer.deserialize_str(DurationVisitor)
140    }
141}
142
143#[derive(Clone, PartialEq, Eq, ::prost::Message, schemars::JsonSchema)]
144pub struct Any {
145    /// A URL/resource name that uniquely identifies the type of the serialized
146    /// protocol buffer message. This string must contain at least
147    /// one "/" character. The last segment of the URL's path must represent
148    /// the fully qualified name of the type (as in
149    /// `path/google.protobuf.Duration`). The name should be in a canonical form
150    /// (e.g., leading "." is not accepted).
151    ///
152    /// In practice, teams usually precompile into the binary all types that they
153    /// expect it to use in the context of Any. However, for URLs which use the
154    /// scheme `http`, `https`, or no scheme, one can optionally set up a type
155    /// server that maps type URLs to message definitions as follows:
156    ///
157    /// * If no scheme is provided, `https` is assumed.
158    /// * An HTTP GET on the URL must yield a \[google.protobuf.Type][\]
159    ///   value in binary format, or produce an error.
160    /// * Applications are allowed to cache lookup results based on the
161    ///   URL, or have them precompiled into a binary to avoid any
162    ///   lookup. Therefore, binary compatibility needs to be preserved
163    ///   on changes to types. (Use versioned type names to manage
164    ///   breaking changes.)
165    ///
166    /// Note: this functionality is not currently available in the official
167    /// protobuf release, and it is not used for type URLs beginning with
168    /// type.googleapis.com.
169    ///
170    /// Schemes other than `http`, `https` (or the empty scheme) might be
171    /// used with implementation specific semantics.
172    ///
173    #[prost(string, tag = "1")]
174    pub type_url: ::prost::alloc::string::String,
175    /// Must be a valid serialized protocol buffer of the above specified type.
176    #[prost(bytes = "vec", tag = "2")]
177    pub value: ::prost::alloc::vec::Vec<u8>,
178}
179
180impl Serialize for Any {
181    fn serialize<S>(
182        &self,
183        serializer: S,
184    ) -> Result<<S as ::serde::Serializer>::Ok, <S as ::serde::Serializer>::Error>
185    where
186        S: ::serde::Serializer,
187    {
188        let mut map = serializer.serialize_map(Some(2))?;
189        map.serialize_entry("@type", &self.type_url)?;
190        map.serialize_entry("value", &Binary::from(self.value.clone()))?;
191        map.end()
192    }
193}
194
195impl<'de> Deserialize<'de> for Any {
196    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
197    where
198        D: serde::Deserializer<'de>,
199    {
200        // Get raw value from deserializer
201        let raw = match serde_cw_value::Value::deserialize(deserializer) {
202            Ok(raw) => raw,
203            Err(err) => {
204                return Err(err);
205            }
206        };
207
208        // Turn raw value into deserialize map value
209        let map = match raw.clone() {
210            serde_cw_value::Value::Map(m) => Ok(m),
211            _ => Err(serde::de::Error::custom("data must have map structure")),
212        }?;
213
214        // Get type url from map
215        let type_url = map
216            .get(&serde_cw_value::Value::String("@type".to_string()))
217            .map(|t| match t.to_owned() {
218                serde_cw_value::Value::String(s) => Ok(s),
219                _ => Err(serde::de::Error::custom("type_url must be String")),
220            })
221            .transpose()?
222            .unwrap_or_default();
223
224        // Get base64 encoded value from deserialize map
225        let value_in_base64 = map
226            .get(&serde_cw_value::Value::String("value".to_string()))
227            .map(|t| match t.to_owned() {
228                serde_cw_value::Value::String(s) => Ok(s),
229                _ => Err(serde::de::Error::custom("value must be String")),
230            })
231            .transpose()?
232            .unwrap_or_default();
233
234        // Convert base64 encoded value into vector
235        let value = match Binary::from_base64(&value_in_base64) {
236            Ok(v) => Ok(v),
237            _ => Err(serde::de::Error::custom("value must be base64 encoded")),
238        }?
239        .to_vec();
240
241        Ok(Any { type_url, value })
242    }
243}
244
245macro_rules! impl_prost_types_exact_conversion {
246    ($t:ident | $($arg:ident),*) => {
247        impl From<$t> for prost_types::$t {
248            fn from(src: $t) -> Self {
249                prost_types::$t {
250                    $(
251                        $arg: src.$arg,
252                    )*
253                }
254            }
255        }
256
257        impl From<prost_types::$t> for $t {
258            fn from(src: prost_types::$t) -> Self {
259                $t {
260                    $(
261                        $arg: src.$arg,
262                    )*
263                }
264            }
265        }
266    };
267}
268
269impl_prost_types_exact_conversion! { Timestamp | seconds, nanos }
270impl_prost_types_exact_conversion! { Duration | seconds, nanos }
271impl_prost_types_exact_conversion! { Any | type_url, value }
272
273impl From<cosmwasm_std::Coin> for crate::types::cosmos::base::v1beta1::Coin {
274    fn from(cosmwasm_std::Coin { denom, amount }: cosmwasm_std::Coin) -> Self {
275        crate::types::cosmos::base::v1beta1::Coin {
276            denom,
277            amount: amount.into(),
278        }
279    }
280}
281
282impl TryFrom<crate::types::cosmos::base::v1beta1::Coin> for cosmwasm_std::Coin {
283    type Error = cosmwasm_std::StdError;
284
285    fn try_from(
286        crate::types::cosmos::base::v1beta1::Coin { denom, amount }: crate::types::cosmos::base::v1beta1::Coin,
287    ) -> StdResult<Self> {
288        Ok(cosmwasm_std::Coin {
289            denom,
290            amount: amount.parse()?,
291        })
292    }
293}
294
295/// Convert a list of `Coin` from provenance proto generated `Coin` type to cosmwasm `Coin` type
296pub fn try_proto_to_cosmwasm_coins(
297    coins: impl IntoIterator<Item = crate::types::cosmos::base::v1beta1::Coin>,
298) -> StdResult<Vec<cosmwasm_std::Coin>> {
299    coins.into_iter().map(|c| c.try_into()).collect()
300}
301
302/// Convert a list of `Coin` from cosmwasm `Coin` type to proto generated `Coin` type
303pub fn cosmwasm_to_proto_coins(
304    coins: impl IntoIterator<Item = cosmwasm_std::Coin>,
305) -> Vec<crate::types::cosmos::base::v1beta1::Coin> {
306    coins.into_iter().map(|c| c.into()).collect()
307}
308
309#[cfg(test)]
310mod tests {
311    use cosmwasm_std::Uint128;
312
313    use super::*;
314
315    #[test]
316    fn test_coins_conversion() {
317        let coins = vec![
318            cosmwasm_std::Coin {
319                denom: "uatom".to_string(),
320                amount: Uint128::new(100),
321            },
322            cosmwasm_std::Coin {
323                denom: "nhash".to_string(),
324                amount: Uint128::new(200),
325            },
326        ];
327
328        let proto_coins = cosmwasm_to_proto_coins(coins.clone());
329        let cosmwasm_coins = try_proto_to_cosmwasm_coins(proto_coins).unwrap();
330
331        assert_eq!(coins, cosmwasm_coins);
332    }
333}