1use ::serde::{ser, Deserialize, Deserializer, Serialize, Serializer};
2use chrono::{DateTime, NaiveDateTime, Utc};
3use cosmwasm_std::StdResult;
4use serde::de;
5use serde::de::Visitor;
6
7use std::fmt;
8use std::str::FromStr;
9
10use prost::Message;
11
12#[derive(Clone, PartialEq, Eq, ::prost::Message, schemars::JsonSchema)]
13pub struct Timestamp {
14 #[prost(int64, tag = "1")]
18 pub seconds: i64,
19 #[prost(int32, tag = "2")]
24 pub nanos: i32,
25}
26
27impl Serialize for Timestamp {
28 fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
29 where
30 S: Serializer,
31 {
32 let mut ts = prost_types::Timestamp {
33 seconds: self.seconds,
34 nanos: self.nanos,
35 };
36 ts.normalize();
37 let dt = NaiveDateTime::from_timestamp_opt(ts.seconds, ts.nanos as u32)
38 .expect("invalid or out-of-range datetime");
39 let dt: DateTime<Utc> = DateTime::from_naive_utc_and_offset(dt, Utc);
40 serializer.serialize_str(format!("{:?}", dt).as_str())
41 }
42}
43
44impl<'de> Deserialize<'de> for Timestamp {
45 fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
46 where
47 D: Deserializer<'de>,
48 {
49 struct TimestampVisitor;
50
51 impl<'de> Visitor<'de> for TimestampVisitor {
52 type Value = Timestamp;
53
54 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
55 formatter.write_str("Timestamp in RFC3339 format")
56 }
57
58 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
59 where
60 E: de::Error,
61 {
62 let utc: DateTime<Utc> = chrono::DateTime::from_str(value).map_err(|err| {
63 serde::de::Error::custom(format!(
64 "Failed to parse {} as datetime: {:?}",
65 value, err
66 ))
67 })?;
68 let ts = Timestamp::from(utc);
69 Ok(ts)
70 }
71 }
72 deserializer.deserialize_str(TimestampVisitor)
73 }
74}
75
76impl From<DateTime<Utc>> for Timestamp {
77 fn from(dt: DateTime<Utc>) -> Self {
78 Timestamp {
79 seconds: dt.timestamp(),
80 nanos: dt.timestamp_subsec_nanos() as i32,
81 }
82 }
83}
84#[derive(Clone, PartialEq, Eq, ::prost::Message, schemars::JsonSchema)]
85pub struct Duration {
86 #[prost(int64, tag = "1")]
90 pub seconds: i64,
91 #[prost(int32, tag = "2")]
98 pub nanos: i32,
99}
100
101impl Serialize for Duration {
102 fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
103 where
104 S: Serializer,
105 {
106 let mut d = prost_types::Duration::from(self.to_owned());
107 d.normalize();
108
109 serializer.serialize_str(d.to_string().as_str())
110 }
111}
112
113impl<'de> Deserialize<'de> for Duration {
114 fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
115 where
116 D: Deserializer<'de>,
117 {
118 struct DurationVisitor;
119
120 impl<'de> Visitor<'de> for DurationVisitor {
121 type Value = Duration;
122
123 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
124 formatter.write_str("Timestamp in RFC3339 format")
125 }
126
127 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
128 where
129 E: de::Error,
130 {
131 value
132 .parse::<prost_types::Duration>()
133 .map(Into::into)
134 .map_err(de::Error::custom)
135 }
136 }
137 deserializer.deserialize_str(DurationVisitor)
138 }
139}
140
141#[derive(Clone, PartialEq, Eq, ::prost::Message, schemars::JsonSchema)]
142pub struct Any {
143 #[prost(string, tag = "1")]
172 pub type_url: ::prost::alloc::string::String,
173 #[prost(bytes = "vec", tag = "2")]
175 pub value: ::prost::alloc::vec::Vec<u8>,
176}
177
178macro_rules! expand_as_any {
179 ($($ty:path,)*) => {
180
181 impl Serialize for Any {
183 fn serialize<S>(
184 &self,
185 serializer: S,
186 ) -> Result<<S as ::serde::Serializer>::Ok, <S as ::serde::Serializer>::Error>
187 where
188 S: ::serde::Serializer,
189 {
190 $(
191 if self.type_url == <$ty>::TYPE_URL {
192 let value: Result<$ty, <S as ::serde::Serializer>::Error> =
193 prost::Message::decode(self.value.as_slice()).map_err(ser::Error::custom);
194
195 if let Ok(value) = value {
196 return value.serialize(serializer);
197 }
198 }
199 )*
200
201 Err(serde::ser::Error::custom(
202 "data did not match any type that supports serialization as `Any`",
203 ))
204 }
205 }
206
207 impl<'de> Deserialize<'de> for Any {
208 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
209 where
210 D: serde::Deserializer<'de>,
211 {
212 let value = match serde_cw_value::Value::deserialize(deserializer) {
213 Ok(value) => value,
214 Err(err) => {
215 return Err(err);
216 }
217 };
218
219 let type_url = if let serde_cw_value::Value::Map(m) = value.clone() {
221 m.get(&serde_cw_value::Value::String("@type".to_string()))
222 .map(|t| match t.to_owned() {
223 serde_cw_value::Value::String(s) => Ok(s),
224 _ => Err(serde::de::Error::custom("type_url must be String")),
225 })
226 .transpose()
227 } else {
228 Err(serde::de::Error::custom("data must have map structure"))
229 }?;
230
231 match type_url {
232 Some(t) => {
234 $(
235 if t == <$ty>::TYPE_URL {
236 return <$ty>::deserialize(
237 serde_cw_value::ValueDeserializer::<serde_cw_value::DeserializerError>::new(
238 value.clone(),
239 ),
240 )
241 .map(|v| Any {
242 type_url: <$ty>::TYPE_URL.to_string(),
243 value: v.encode_to_vec(),
244 })
245 .map_err(serde::de::Error::custom);
246 }
247 )*
248 }
249 None => {
251 $(
252 if let Ok(v) = <$ty>::deserialize(
253 serde_cw_value::ValueDeserializer::<serde_cw_value::DeserializerError>::new(
254 value.clone(),
255 ),
256 ) {
257 return Ok(Any {
258 type_url: <$ty>::TYPE_URL.to_string(),
259 value: v.encode_to_vec(),
260 });
261 }
262 )*
263 }
264 };
265
266 Err(serde::de::Error::custom(
267 "data did not match any type that supports deserialization as `Any`",
268 ))
269 }
270 }
271
272 $(
273 impl TryFrom<Any> for $ty {
274 type Error = prost::DecodeError;
275
276 fn try_from(value: Any) -> Result<Self, Self::Error> {
277 prost::Message::decode(value.value.as_slice())
278 }
279 }
280 )*
281 };
282}
283
284expand_as_any!(
289 crate::types::osmosis::gamm::v1beta1::Pool,
291 crate::types::osmosis::gamm::poolmodels::stableswap::v1beta1::Pool,
292 crate::types::osmosis::concentratedliquidity::v1beta1::Pool,
293 crate::types::osmosis::cosmwasmpool::v1beta1::CosmWasmPool,
294 crate::types::osmosis::gamm::v1beta1::PoolParams,
296 crate::types::osmosis::gamm::poolmodels::stableswap::v1beta1::PoolParams,
297 crate::types::cosmos::auth::v1beta1::BaseAccount,
299 crate::types::cosmos::auth::v1beta1::ModuleAccount,
300 crate::types::cosmos::crypto::secp256k1::PubKey,
304 crate::types::cosmos::crypto::secp256r1::PubKey,
305 crate::types::cosmos::crypto::ed25519::PubKey,
306);
307
308macro_rules! impl_prost_types_exact_conversion {
309 ($t:ident | $($arg:ident),*) => {
310 impl From<$t> for prost_types::$t {
311 fn from(src: $t) -> Self {
312 prost_types::$t {
313 $(
314 $arg: src.$arg,
315 )*
316 }
317 }
318 }
319
320 impl From<prost_types::$t> for $t {
321 fn from(src: prost_types::$t) -> Self {
322 $t {
323 $(
324 $arg: src.$arg,
325 )*
326 }
327 }
328 }
329 };
330}
331
332impl_prost_types_exact_conversion! { Timestamp | seconds, nanos }
333impl_prost_types_exact_conversion! { Duration | seconds, nanos }
334impl_prost_types_exact_conversion! { Any | type_url, value }
335
336impl From<cosmwasm_std::Coin> for crate::types::cosmos::base::v1beta1::Coin {
337 fn from(cosmwasm_std::Coin { denom, amount }: cosmwasm_std::Coin) -> Self {
338 crate::types::cosmos::base::v1beta1::Coin {
339 denom,
340 amount: amount.into(),
341 }
342 }
343}
344
345impl TryFrom<crate::types::cosmos::base::v1beta1::Coin> for cosmwasm_std::Coin {
346 type Error = cosmwasm_std::StdError;
347
348 fn try_from(
349 crate::types::cosmos::base::v1beta1::Coin { denom, amount }: crate::types::cosmos::base::v1beta1::Coin,
350 ) -> cosmwasm_std::StdResult<Self> {
351 Ok(cosmwasm_std::Coin {
352 denom,
353 amount: amount.parse()?,
354 })
355 }
356}
357
358pub fn try_proto_to_cosmwasm_coins(
360 coins: impl IntoIterator<Item = crate::types::cosmos::base::v1beta1::Coin>,
361) -> StdResult<Vec<cosmwasm_std::Coin>> {
362 coins.into_iter().map(|c| c.try_into()).collect()
363}
364
365pub fn cosmwasm_to_proto_coins(
367 coins: impl IntoIterator<Item = cosmwasm_std::Coin>,
368) -> Vec<crate::types::cosmos::base::v1beta1::Coin> {
369 coins.into_iter().map(|c| c.into()).collect()
370}
371
372#[cfg(test)]
373mod tests {
374 use cosmwasm_std::Uint128;
375
376 use super::*;
377
378 #[test]
379 fn test_coins_conversion() {
380 let coins = vec![
381 cosmwasm_std::Coin {
382 denom: "uatom".to_string(),
383 amount: Uint128::new(100),
384 },
385 cosmwasm_std::Coin {
386 denom: "uosmo".to_string(),
387 amount: Uint128::new(200),
388 },
389 ];
390
391 let proto_coins = cosmwasm_to_proto_coins(coins.clone());
392 let cosmwasm_coins = try_proto_to_cosmwasm_coins(proto_coins).unwrap();
393
394 assert_eq!(coins, cosmwasm_coins);
395 }
396}