cosmwasm_std/results/
submessages.rs

1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3
4use crate::prelude::*;
5use crate::Binary;
6
7use super::{CosmosMsg, Empty, Event};
8
9/// Use this to define when the contract gets a response callback.
10/// If you only need it for errors or success you can select just those in order
11/// to save gas.
12#[derive(
13    Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema, cw_schema::Schemaifier,
14)]
15#[serde(rename_all = "snake_case")]
16pub enum ReplyOn {
17    /// Always perform a callback after SubMsg is processed
18    Always,
19    /// Only callback if SubMsg returned an error, no callback on success case
20    Error,
21    /// Only callback if SubMsg was successful, no callback on error case
22    Success,
23    /// Never make a callback - this is like the original CosmosMsg semantics
24    Never,
25}
26
27/// A submessage that will guarantee a `reply` call on success or error, depending on
28/// the `reply_on` setting. If you do not need to process the result, use regular messages instead.
29///
30/// Note: On error the submessage execution will revert any partial state changes due to this message,
31/// but not revert any state changes in the calling contract. If this is required, it must be done
32/// manually in the `reply` entry point.
33#[derive(
34    Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema, cw_schema::Schemaifier,
35)]
36pub struct SubMsg<T = Empty> {
37    /// An arbitrary ID chosen by the contract.
38    /// This is typically used to match `Reply`s in the `reply` entry point to the submessage.
39    pub id: u64,
40    /// Some arbitrary data that the contract can set in an application specific way.
41    /// This is just passed into the `reply` entry point and is not stored to state.
42    /// Any encoding can be used. If `id` is used to identify a particular action,
43    /// the encoding can also be different for each of those actions since you can match `id`
44    /// first and then start processing the `payload`.
45    ///
46    /// The environment restricts the length of this field in order to avoid abuse. The limit
47    /// is environment specific and can change over time. The initial default is 128 KiB.
48    ///
49    /// Unset/nil/null cannot be differentiated from empty data.
50    ///
51    /// On chains running CosmWasm 1.x this field will be ignored.
52    #[serde(default)]
53    pub payload: Binary,
54    pub msg: CosmosMsg<T>,
55    /// Gas limit measured in [Cosmos SDK gas](https://github.com/CosmWasm/cosmwasm/blob/main/docs/GAS.md).
56    ///
57    /// Setting this to `None` means unlimited. Then the submessage execution can consume all gas of the
58    /// current execution context.
59    pub gas_limit: Option<u64>,
60    pub reply_on: ReplyOn,
61}
62
63/// This is used for cases when we use ReplyOn::Never and the id doesn't matter
64pub const UNUSED_MSG_ID: u64 = 0;
65
66impl<T> SubMsg<T> {
67    /// Creates a "fire and forget" message with the pre-0.14 semantics.
68    /// Since this is just an alias for [`SubMsg::reply_never`] it is somewhat recommended
69    /// to use the latter in order to make the behaviour more explicit in the caller code.
70    /// But that's up to you for now.
71    ///
72    /// By default, the submessage's gas limit will be unlimited. Use [`SubMsg::with_gas_limit`] to change it.
73    /// Setting `payload` is not advised as this will never be used.
74    pub fn new(msg: impl Into<CosmosMsg<T>>) -> Self {
75        Self::reply_never(msg)
76    }
77
78    /// Creates a `SubMsg` that will provide a `reply` with the given `id` if the message returns `Ok`.
79    ///
80    /// By default, the submessage's `payload` will be empty and the gas limit will be unlimited. Use
81    /// [`SubMsg::with_payload`] and [`SubMsg::with_gas_limit`] to change those.
82    pub fn reply_on_success(msg: impl Into<CosmosMsg<T>>, id: u64) -> Self {
83        Self::reply_on(msg.into(), id, ReplyOn::Success)
84    }
85
86    /// Creates a `SubMsg` that will provide a `reply` with the given `id` if the message returns `Err`.
87    ///
88    /// By default, the submessage's `payload` will be empty and the gas limit will be unlimited. Use
89    /// [`SubMsg::with_payload`] and [`SubMsg::with_gas_limit`] to change those.
90    pub fn reply_on_error(msg: impl Into<CosmosMsg<T>>, id: u64) -> Self {
91        Self::reply_on(msg.into(), id, ReplyOn::Error)
92    }
93
94    /// Create a `SubMsg` that will always provide a `reply` with the given `id`.
95    ///
96    /// By default, the submessage's `payload` will be empty and the gas limit will be unlimited. Use
97    /// [`SubMsg::with_payload`] and [`SubMsg::with_gas_limit`] to change those.
98    pub fn reply_always(msg: impl Into<CosmosMsg<T>>, id: u64) -> Self {
99        Self::reply_on(msg.into(), id, ReplyOn::Always)
100    }
101
102    /// Create a `SubMsg` that will never `reply`. This is equivalent to standard message semantics.
103    ///
104    /// By default, the submessage's gas limit will be unlimited. Use [`SubMsg::with_gas_limit`] to change it.
105    /// Setting `payload` is not advised as this will never be used.
106    pub fn reply_never(msg: impl Into<CosmosMsg<T>>) -> Self {
107        Self::reply_on(msg.into(), UNUSED_MSG_ID, ReplyOn::Never)
108    }
109
110    /// Add a gas limit to the submessage.
111    /// This gas limit measured in [Cosmos SDK gas](https://github.com/CosmWasm/cosmwasm/blob/main/docs/GAS.md).
112    ///
113    /// ## Examples
114    ///
115    /// ```
116    /// # use cosmwasm_std::{coins, BankMsg, ReplyOn, SubMsg};
117    /// # let msg = BankMsg::Send { to_address: String::from("you"), amount: coins(1015, "earth") };
118    /// let sub_msg: SubMsg = SubMsg::reply_always(msg, 1234).with_gas_limit(60_000);
119    /// assert_eq!(sub_msg.id, 1234);
120    /// assert_eq!(sub_msg.gas_limit, Some(60_000));
121    /// assert_eq!(sub_msg.reply_on, ReplyOn::Always);
122    /// ```
123    pub fn with_gas_limit(mut self, limit: u64) -> Self {
124        self.gas_limit = Some(limit);
125        self
126    }
127
128    /// Add a payload to the submessage.
129    ///
130    /// ## Examples
131    ///
132    /// ```
133    /// # use cosmwasm_std::{coins, BankMsg, Binary, ReplyOn, SubMsg};
134    /// # let msg = BankMsg::Send { to_address: String::from("you"), amount: coins(1015, "earth") };
135    /// let sub_msg: SubMsg = SubMsg::reply_always(msg, 1234)
136    ///     .with_payload(vec![1, 2, 3, 4]);
137    /// assert_eq!(sub_msg.id, 1234);
138    /// assert_eq!(sub_msg.payload, Binary::new(vec![1, 2, 3, 4]));
139    /// assert_eq!(sub_msg.reply_on, ReplyOn::Always);
140    /// ```
141    pub fn with_payload(mut self, payload: impl Into<Binary>) -> Self {
142        self.payload = payload.into();
143        self
144    }
145
146    fn reply_on(msg: CosmosMsg<T>, id: u64, reply_on: ReplyOn) -> Self {
147        SubMsg {
148            id,
149            payload: Default::default(),
150            msg,
151            reply_on,
152            gas_limit: None,
153        }
154    }
155
156    /// Convert this [`SubMsg<T>`] to a [`SubMsg<U>`] with a different generic type.
157    /// This allows easier interactions between code written for a specific chain and
158    /// code written for multiple chains.
159    /// If this is a [`CosmosMsg::Custom`] submessage, the function returns `None`.
160    pub fn change_custom<U>(self) -> Option<SubMsg<U>> {
161        Some(SubMsg {
162            id: self.id,
163            payload: self.payload,
164            msg: self.msg.change_custom::<U>()?,
165            gas_limit: self.gas_limit,
166            reply_on: self.reply_on,
167        })
168    }
169}
170
171/// The result object returned to `reply`. We always get the ID from the submessage
172/// back and then must handle success and error cases ourselves.
173#[derive(
174    Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema, cw_schema::Schemaifier,
175)]
176pub struct Reply {
177    /// The ID that the contract set when emitting the `SubMsg`.
178    /// Use this to identify which submessage triggered the `reply`.
179    pub id: u64,
180    /// Some arbitrary data that the contract set when emitting the `SubMsg`.
181    /// This is just passed into the `reply` entry point and is not stored to state.
182    ///
183    /// Unset/nil/null cannot be differentiated from empty data.
184    ///
185    /// On chains running CosmWasm 1.x this field is never filled.
186    #[serde(default)]
187    pub payload: Binary,
188    /// The amount of gas used by the submessage,
189    /// measured in [Cosmos SDK gas](https://github.com/CosmWasm/cosmwasm/blob/main/docs/GAS.md).
190    ///
191    /// This only contains a useful value on chains running CosmWasm 2.0 or higher.
192    /// On older chains, this field is always 0.
193    #[serde(default)]
194    pub gas_used: u64,
195    pub result: SubMsgResult,
196}
197
198/// This is the result type that is returned from a sub message execution.
199///
200/// We use a custom type here instead of Rust's Result because we want to be able to
201/// define the serialization, which is a public interface. Every language that compiles
202/// to Wasm and runs in the ComsWasm VM needs to create the same JSON representation.
203///
204/// Until version 1.0.0-beta5, `ContractResult<SubMsgResponse>` was used instead
205/// of this type. Once serialized, the two types are the same. However, in the Rust type
206/// system we want different types for clarity and documentation reasons.
207///
208/// # Examples
209///
210/// Success:
211///
212/// ```
213/// # use cosmwasm_std::{to_json_string, Binary, Event, SubMsgResponse, SubMsgResult};
214/// #[allow(deprecated)]
215/// let response = SubMsgResponse {
216///     data: Some(Binary::from_base64("MTIzCg==").unwrap()),
217///     events: vec![Event::new("wasm").add_attribute("foo", "bar")],
218///     msg_responses: vec![],
219/// };
220/// let result: SubMsgResult = SubMsgResult::Ok(response);
221/// assert_eq!(
222///     to_json_string(&result).unwrap(),
223///     r#"{"ok":{"events":[{"type":"wasm","attributes":[{"key":"foo","value":"bar"}]}],"data":"MTIzCg==","msg_responses":[]}}"#,
224/// );
225/// ```
226///
227/// Failure:
228///
229/// ```
230/// # use cosmwasm_std::{to_json_string, SubMsgResult, Response};
231/// let error_msg = String::from("Something went wrong");
232/// let result = SubMsgResult::Err(error_msg);
233/// assert_eq!(to_json_string(&result).unwrap(), r#"{"error":"Something went wrong"}"#);
234/// ```
235#[derive(
236    Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema, cw_schema::Schemaifier,
237)]
238#[serde(rename_all = "snake_case")]
239pub enum SubMsgResult {
240    Ok(SubMsgResponse),
241    /// An error type that every custom error created by contract developers can be converted to.
242    /// This could potentially have more structure, but String is the easiest.
243    #[serde(rename = "error")]
244    Err(String),
245}
246
247// Implementations here mimic the Result API and should be implemented via a conversion to Result
248// to ensure API consistency
249impl SubMsgResult {
250    /// Converts a `SubMsgResult<S>` to a `Result<S, String>` as a convenient way
251    /// to access the full Result API.
252    pub fn into_result(self) -> Result<SubMsgResponse, String> {
253        Result::<SubMsgResponse, String>::from(self)
254    }
255
256    pub fn unwrap(self) -> SubMsgResponse {
257        self.into_result().unwrap()
258    }
259
260    pub fn unwrap_err(self) -> String {
261        self.into_result().unwrap_err()
262    }
263
264    pub fn is_ok(&self) -> bool {
265        matches!(self, SubMsgResult::Ok(_))
266    }
267
268    pub fn is_err(&self) -> bool {
269        matches!(self, SubMsgResult::Err(_))
270    }
271}
272
273impl<E: ToString> From<Result<SubMsgResponse, E>> for SubMsgResult {
274    fn from(original: Result<SubMsgResponse, E>) -> SubMsgResult {
275        match original {
276            Ok(value) => SubMsgResult::Ok(value),
277            Err(err) => SubMsgResult::Err(err.to_string()),
278        }
279    }
280}
281
282impl From<SubMsgResult> for Result<SubMsgResponse, String> {
283    fn from(original: SubMsgResult) -> Result<SubMsgResponse, String> {
284        match original {
285            SubMsgResult::Ok(value) => Ok(value),
286            SubMsgResult::Err(err) => Err(err),
287        }
288    }
289}
290
291/// The information we get back from a successful sub message execution
292#[derive(
293    Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema, cw_schema::Schemaifier,
294)]
295pub struct SubMsgResponse {
296    /// The Cosmos SDK events emitted by the submessage.
297    ///
298    /// This is only filled if the submessage was itself a [`crate::WasmMsg`].
299    /// The reason for this is that events are [not part of consensus] and therefore not guaranteed to be deterministic,
300    /// so the VM only returns events of wasm messages, which we know are deterministic.
301    ///
302    /// [not part of consensus]: https://github.com/tendermint/tendermint/blob/eed27addecb339cfaeba8fda774e6ab37cdb3774/spec/abci/abci.md#events
303    pub events: Vec<Event>,
304    #[deprecated = "Deprecated in the Cosmos SDK in favor of msg_responses. If your chain is running on CosmWasm 2.0 or higher, msg_responses will be filled. For older versions, the data field is still needed since msg_responses is empty in those cases."]
305    pub data: Option<Binary>,
306    /// The responses from the messages emitted by the submessage.
307    /// In most cases, this is equivalent to the Cosmos SDK's [MsgResponses], which usually contains a [single message].
308    /// However, wasmd allows chains to translate a single contract message into multiple SDK messages.
309    /// In that case all the MsgResponses from each are concatenated into this flattened `Vec`.
310    ///
311    /// [MsgResponses]: https://github.com/cosmos/cosmos-sdk/blob/316750cc8cd8b3296fa233f4da2e39cbcdc34517/proto/cosmos/base/abci/v1beta1/abci.proto#L106-L109
312    /// [single message]: https://github.com/cosmos/cosmos-sdk/blob/v0.50.4/baseapp/baseapp.go#L1020-L1023
313    #[serde(default)]
314    pub msg_responses: Vec<MsgResponse>,
315}
316
317#[derive(
318    Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema, cw_schema::Schemaifier,
319)]
320pub struct MsgResponse {
321    pub type_url: String,
322    pub value: Binary,
323}
324
325#[cfg(test)]
326#[allow(deprecated)]
327mod tests {
328    use super::*;
329    use crate::{
330        coins, errors::ErrorKind, from_json, to_json_vec, Attribute, BankMsg, StdError, StdResult,
331    };
332
333    #[test]
334    fn sub_msg_new_works() {
335        let msg = BankMsg::Send {
336            to_address: String::from("you"),
337            amount: coins(1015, "earth"),
338        };
339        let sub_msg: SubMsg = SubMsg::new(msg.clone());
340        // id and payload don't matter since there is no reply
341        assert_eq!(sub_msg.reply_on, ReplyOn::Never);
342        assert_eq!(sub_msg.gas_limit, None);
343        assert_eq!(sub_msg.msg, CosmosMsg::from(msg));
344    }
345
346    #[test]
347    fn sub_msg_reply_never_works() {
348        let msg = BankMsg::Send {
349            to_address: String::from("you"),
350            amount: coins(1015, "earth"),
351        };
352        let sub_msg: SubMsg = SubMsg::reply_never(msg.clone());
353        // id and payload don't matter since there is no reply
354        assert_eq!(sub_msg.reply_on, ReplyOn::Never);
355        assert_eq!(sub_msg.gas_limit, None);
356        assert_eq!(sub_msg.msg, CosmosMsg::from(msg));
357    }
358
359    #[test]
360    fn sub_msg_reply_always_works() {
361        let msg = BankMsg::Send {
362            to_address: String::from("you"),
363            amount: coins(1015, "earth"),
364        };
365        let sub_msg: SubMsg = SubMsg::reply_always(msg.clone(), 54);
366        assert_eq!(sub_msg.id, 54);
367        assert_eq!(sub_msg.payload, Binary::default());
368        assert_eq!(sub_msg.reply_on, ReplyOn::Always);
369        assert_eq!(sub_msg.gas_limit, None);
370        assert_eq!(sub_msg.msg, CosmosMsg::from(msg));
371    }
372
373    #[test]
374    fn sub_msg_with_gas_limit_works() {
375        let msg = BankMsg::Send {
376            to_address: String::from("you"),
377            amount: coins(1015, "earth"),
378        };
379        let sub_msg: SubMsg = SubMsg::reply_never(msg);
380        assert_eq!(sub_msg.gas_limit, None);
381        let sub_msg = sub_msg.with_gas_limit(20);
382        assert_eq!(sub_msg.gas_limit, Some(20));
383    }
384
385    #[test]
386    fn sub_msg_with_payload_works() {
387        let msg = BankMsg::Send {
388            to_address: String::from("you"),
389            amount: coins(1015, "earth"),
390        };
391        let sub_msg: SubMsg = SubMsg::reply_never(msg);
392        assert_eq!(sub_msg.payload, Binary::default());
393        let sub_msg = sub_msg.with_payload(vec![0xAA, 3, 5, 1, 2]);
394        assert_eq!(sub_msg.payload, Binary::new(vec![0xAA, 3, 5, 1, 2]));
395    }
396
397    #[test]
398    fn sub_msg_result_serialization_works() {
399        let result = SubMsgResult::Ok(SubMsgResponse {
400            data: None,
401            msg_responses: vec![],
402            events: vec![],
403        });
404        assert_eq!(
405            &to_json_vec(&result).unwrap(),
406            br#"{"ok":{"events":[],"data":null,"msg_responses":[]}}"#
407        );
408
409        let result = SubMsgResult::Ok(SubMsgResponse {
410            data: Some(Binary::from_base64("MTIzCg==").unwrap()),
411            msg_responses: vec![MsgResponse {
412                type_url: "URL".to_string(),
413                value: Binary::from_base64("MTIzCg==").unwrap(),
414            }],
415            events: vec![Event::new("wasm").add_attribute("foo", "bar")],
416        });
417        println!("{}", &crate::to_json_string(&result).unwrap());
418        assert_eq!(
419            &to_json_vec(&result).unwrap(),
420            br#"{"ok":{"events":[{"type":"wasm","attributes":[{"key":"foo","value":"bar"}]}],"data":"MTIzCg==","msg_responses":[{"type_url":"URL","value":"MTIzCg=="}]}}"#
421        );
422
423        let result: SubMsgResult = SubMsgResult::Err("broken".to_string());
424        assert_eq!(&to_json_vec(&result).unwrap(), b"{\"error\":\"broken\"}");
425    }
426
427    #[test]
428    fn sub_msg_result_deserialization_works() {
429        // should work without `msg_responses`
430        let result: SubMsgResult = from_json(br#"{"ok":{"events":[]}}"#).unwrap();
431        assert_eq!(
432            result,
433            SubMsgResult::Ok(SubMsgResponse {
434                events: vec![],
435                data: None,
436                msg_responses: vec![]
437            })
438        );
439
440        // should work with `data` and no `msg_responses`
441        // this is the case for pre-2.0 CosmWasm chains
442        let result: SubMsgResult = from_json(br#"{"ok":{"events":[],"data":"aGk="}}"#).unwrap();
443        assert_eq!(
444            result,
445            SubMsgResult::Ok(SubMsgResponse {
446                events: vec![],
447                data: Some(Binary::from_base64("aGk=").unwrap()),
448                msg_responses: vec![]
449            })
450        );
451
452        let result: SubMsgResult = from_json(
453            br#"{"ok":{"events":[{"type":"wasm","attributes":[{"key":"foo","value":"bar"}]}],"data":"MTIzCg==",
454            "msg_responses":[{"type_url":"URL","value":"MTIzCg=="}]}}"#).unwrap();
455        assert_eq!(
456            result,
457            SubMsgResult::Ok(SubMsgResponse {
458                data: Some(Binary::from_base64("MTIzCg==").unwrap()),
459                msg_responses: vec![MsgResponse {
460                    type_url: "URL".to_string(),
461                    value: Binary::from_base64("MTIzCg==").unwrap(),
462                }],
463                events: vec![Event::new("wasm").add_attribute("foo", "bar")],
464            })
465        );
466
467        let result: SubMsgResult = from_json(br#"{"error":"broken"}"#).unwrap();
468        assert_eq!(result, SubMsgResult::Err("broken".to_string()));
469
470        // fails for additional attributes
471        let parse: StdResult<SubMsgResult> = from_json(br#"{"unrelated":321,"error":"broken"}"#);
472        match parse.unwrap_err().kind() {
473            ErrorKind::Serialization => {}
474            err => panic!("Unexpected error: {err:?}"),
475        }
476        let parse: StdResult<SubMsgResult> = from_json(br#"{"error":"broken","unrelated":321}"#);
477        match parse.unwrap_err().kind() {
478            ErrorKind::Serialization => {}
479            err => panic!("Unexpected error: {err:?}"),
480        }
481    }
482
483    #[test]
484    fn sub_msg_result_unwrap_works() {
485        let response = SubMsgResponse {
486            data: Some(Binary::from_base64("MTIzCg==").unwrap()),
487            msg_responses: vec![MsgResponse {
488                type_url: "URL".to_string(),
489                value: Binary::from_base64("MTIzCg==").unwrap(),
490            }],
491            events: vec![Event::new("wasm").add_attribute("foo", "bar")],
492        };
493        let success = SubMsgResult::Ok(response.clone());
494        assert_eq!(success.unwrap(), response);
495    }
496
497    #[test]
498    #[should_panic]
499    fn sub_msg_result_unwrap_panicks_for_err() {
500        let failure = SubMsgResult::Err("broken".to_string());
501        let _ = failure.unwrap();
502    }
503
504    #[test]
505    fn sub_msg_result_unwrap_err_works() {
506        let failure = SubMsgResult::Err("broken".to_string());
507        assert_eq!(failure.unwrap_err(), "broken");
508    }
509
510    #[test]
511    #[should_panic]
512    fn sub_msg_result_unwrap_err_panics_for_ok() {
513        let response = SubMsgResponse {
514            data: Some(Binary::from_base64("MTIzCg==").unwrap()),
515            events: vec![Event::new("wasm").add_attribute("foo", "bar")],
516            msg_responses: vec![],
517        };
518        let success = SubMsgResult::Ok(response);
519        let _ = success.unwrap_err();
520    }
521
522    #[test]
523    fn sub_msg_result_is_ok_works() {
524        let success = SubMsgResult::Ok(SubMsgResponse {
525            data: Some(Binary::from_base64("MTIzCg==").unwrap()),
526            events: vec![Event::new("wasm").add_attribute("foo", "bar")],
527            msg_responses: vec![],
528        });
529        let failure = SubMsgResult::Err("broken".to_string());
530        assert!(success.is_ok());
531        assert!(!failure.is_ok());
532    }
533
534    #[test]
535    fn sub_msg_result_is_err_works() {
536        let success = SubMsgResult::Ok(SubMsgResponse {
537            data: Some(Binary::from_base64("MTIzCg==").unwrap()),
538            events: vec![Event::new("wasm").add_attribute("foo", "bar")],
539            msg_responses: vec![],
540        });
541        let failure = SubMsgResult::Err("broken".to_string());
542        assert!(failure.is_err());
543        assert!(!success.is_err());
544    }
545
546    #[test]
547    fn sub_msg_result_can_convert_from_core_result() {
548        let original: Result<SubMsgResponse, StdError> = Ok(SubMsgResponse {
549            data: Some(Binary::from_base64("MTIzCg==").unwrap()),
550            events: vec![],
551            msg_responses: vec![],
552        });
553        let converted: SubMsgResult = original.into();
554        assert_eq!(
555            converted,
556            SubMsgResult::Ok(SubMsgResponse {
557                data: Some(Binary::from_base64("MTIzCg==").unwrap()),
558                events: vec![],
559                msg_responses: vec![],
560            })
561        );
562
563        let original: Result<SubMsgResponse, StdError> = Err(StdError::msg("broken"));
564        let converted: SubMsgResult = original.into();
565        assert_eq!(
566            converted,
567            SubMsgResult::Err("kind: Other, error: broken".to_string())
568        );
569    }
570
571    #[test]
572    fn sub_msg_result_can_convert_to_core_result() {
573        let original = SubMsgResult::Ok(SubMsgResponse {
574            data: Some(Binary::from_base64("MTIzCg==").unwrap()),
575            events: vec![],
576            msg_responses: vec![],
577        });
578        let converted: Result<SubMsgResponse, String> = original.into();
579        assert_eq!(
580            converted,
581            Ok(SubMsgResponse {
582                data: Some(Binary::from_base64("MTIzCg==").unwrap()),
583                events: vec![],
584                msg_responses: vec![],
585            })
586        );
587
588        let original = SubMsgResult::Err("went wrong".to_string());
589        let converted: Result<SubMsgResponse, String> = original.into();
590        assert_eq!(converted, Err("went wrong".to_string()));
591    }
592
593    #[test]
594    fn reply_deserialization_works() {
595        // 1.x reply without payload (from https://github.com/CosmWasm/cosmwasm/issues/1909)
596        let reply: Reply = from_json(r#"{"gas_used":4312324,"id":75,"result":{"ok":{"events":[{"type":"hi","attributes":[{"key":"si","value":"claro"}]}],"data":"PwCqXKs="}}}"#).unwrap();
597        assert_eq!(
598            reply,
599            Reply {
600                id: 75,
601                payload: Binary::default(),
602                gas_used: 4312324,
603                result: SubMsgResult::Ok(SubMsgResponse {
604                    data: Some(Binary::from_base64("PwCqXKs=").unwrap()),
605                    events: vec![Event {
606                        ty: "hi".to_string(),
607                        attributes: vec![Attribute {
608                            key: "si".to_string(),
609                            value: "claro".to_string(),
610                        }]
611                    }],
612                    msg_responses: vec![],
613                })
614            }
615        );
616
617        // with payload (manually added to the above test)
618        let reply: Reply = from_json(r#"{"gas_used":4312324,"id":75,"payload":"3NxjC5U=","result":{"ok":{"events":[{"type":"hi","attributes":[{"key":"si","value":"claro"}]}],"data":"PwCqXKs="}}}"#).unwrap();
619        assert_eq!(
620            reply,
621            Reply {
622                id: 75,
623                payload: Binary::from_base64("3NxjC5U=").unwrap(),
624                gas_used: 4312324,
625                result: SubMsgResult::Ok(SubMsgResponse {
626                    data: Some(Binary::from_base64("PwCqXKs=").unwrap()),
627                    events: vec![Event {
628                        ty: "hi".to_string(),
629                        attributes: vec![Attribute {
630                            key: "si".to_string(),
631                            value: "claro".to_string(),
632                        }]
633                    }],
634                    msg_responses: vec![],
635                })
636            }
637        );
638    }
639
640    #[test]
641    fn reply_serialization_cosmwasm_1() {
642        // json coming from wasmvm 1.5.0
643        let json = r#"{"id":1234,"result":{"ok":{"events":[{"type":"message","attributes":[{"key":"signer","value":"caller-addr"}]}],"data":"Zm9vYmFy"}}}"#;
644
645        let reply: Reply = from_json(json).unwrap();
646        assert_eq!(reply.id, 1234);
647        assert_eq!(reply.payload, Binary::default());
648        assert_eq!(
649            reply.result,
650            SubMsgResult::Ok(SubMsgResponse {
651                data: Some(Binary::from_base64("Zm9vYmFy").unwrap()),
652                events: vec![Event {
653                    ty: "message".to_string(),
654                    attributes: vec![Attribute {
655                        key: "signer".to_string(),
656                        value: "caller-addr".to_string()
657                    }]
658                }],
659                msg_responses: vec![]
660            })
661        );
662        assert_eq!(reply.gas_used, 0);
663    }
664}