cosmwasm_std/
stdack.rs

1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3
4use crate::prelude::*;
5use crate::to_json_binary;
6use crate::Binary;
7
8/// This is a standard IBC acknowledgement type. IBC application are free
9/// to use any acknowledgement format they want. However, for compatibility
10/// purposes it is recommended to use this.
11///
12/// The original proto definition can be found at <https://github.com/cosmos/cosmos-sdk/blob/v0.42.0/proto/ibc/core/channel/v1/channel.proto#L141-L147>
13/// and <https://github.com/cosmos/ibc/tree/ed849c7bac/spec/core/ics-004-channel-and-packet-semantics#acknowledgement-envelope>.
14///
15/// In contrast to the original idea, [ICS-20](https://github.com/cosmos/ibc/tree/ed849c7bacf16204e9509f0f0df325391f3ce25c/spec/app/ics-020-fungible-token-transfer#technical-specification) and CosmWasm IBC protocols
16/// use JSON instead of a protobuf serialization.
17///
18/// For compatibility, we use the field name "result" for the success case in JSON.
19/// However, all Rust APIs use the term "success" for clarity and discriminability from [Result].
20///
21/// If ibc_receive_packet returns Err(), then x/wasm runtime will rollback the state and
22/// return an error message in this format.
23///
24/// ## Examples
25///
26/// For your convenience, there are success and error constructors.
27///
28/// ```
29/// use cosmwasm_std::StdAck;
30///
31/// let ack1 = StdAck::success(b"\x01"); // 0x01 is a FungibleTokenPacketSuccess from ICS-20.
32/// assert!(ack1.is_success());
33///
34/// let ack2 = StdAck::error("kaputt"); // Some free text error message
35/// assert!(ack2.is_error());
36/// ```
37#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
38#[serde(rename_all = "snake_case")]
39pub enum StdAck {
40    #[serde(rename = "result")]
41    Success(Binary),
42    Error(String),
43}
44
45impl StdAck {
46    /// Creates a success ack with the given data
47    pub fn success(data: impl Into<Binary>) -> Self {
48        StdAck::Success(data.into())
49    }
50
51    /// Creates an error ack
52    pub fn error(err: impl Into<String>) -> Self {
53        StdAck::Error(err.into())
54    }
55
56    #[must_use = "if you intended to assert that this is a success, consider `.unwrap()` instead"]
57    #[inline]
58    pub const fn is_success(&self) -> bool {
59        matches!(*self, StdAck::Success(_))
60    }
61
62    #[must_use = "if you intended to assert that this is an error, consider `.unwrap_err()` instead"]
63    #[inline]
64    pub const fn is_error(&self) -> bool {
65        !self.is_success()
66    }
67
68    /// Serialized the ack to binary using JSON. This used for setting the acknowledgement
69    /// field in IbcReceiveResponse.
70    ///
71    /// ## Examples
72    ///
73    /// Show how the acknowledgement looks on the write:
74    ///
75    /// ```
76    /// # use cosmwasm_std::StdAck;
77    /// let ack1 = StdAck::success(b"\x01"); // 0x01 is a FungibleTokenPacketSuccess from ICS-20.
78    /// assert_eq!(ack1.to_binary(), br#"{"result":"AQ=="}"#);
79    ///
80    /// let ack2 = StdAck::error("kaputt"); // Some free text error message
81    /// assert_eq!(ack2.to_binary(), br#"{"error":"kaputt"}"#);
82    /// ```
83    ///
84    /// Set acknowledgement field in `IbcReceiveResponse`:
85    ///
86    /// ```
87    /// use cosmwasm_std::{StdAck, IbcReceiveResponse};
88    ///
89    /// let ack = StdAck::success(b"\x01"); // 0x01 is a FungibleTokenPacketSuccess from ICS-20.
90    ///
91    /// let res: IbcReceiveResponse = IbcReceiveResponse::new(ack.to_binary());
92    /// let res: IbcReceiveResponse = IbcReceiveResponse::new(ack); // Does the same but consumes the instance
93    /// ```
94    pub fn to_binary(&self) -> Binary {
95        // We need a non-failing StdAck -> Binary conversion to allow using StdAck in
96        // `impl Into<Binary>` arguments.
97        // Pretty sure this cannot fail. If that changes we can create a non-failing implementation here.
98        to_json_binary(&self).unwrap()
99    }
100
101    pub fn unwrap(self) -> Binary {
102        match self {
103            StdAck::Success(data) => data,
104            StdAck::Error(err) => panic!("{}", err),
105        }
106    }
107
108    pub fn unwrap_err(self) -> String {
109        match self {
110            StdAck::Success(_) => panic!("not an error"),
111            StdAck::Error(err) => err,
112        }
113    }
114}
115
116impl From<StdAck> for Binary {
117    fn from(original: StdAck) -> Binary {
118        original.to_binary()
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125
126    #[test]
127    fn stdack_success_works() {
128        let success = StdAck::success(b"foo");
129        match success {
130            StdAck::Success(data) => assert_eq!(data, b"foo"),
131            StdAck::Error(_err) => panic!("must not be an error"),
132        }
133    }
134
135    #[test]
136    fn stdack_error_works() {
137        let err = StdAck::error("bar");
138        match err {
139            StdAck::Success(_data) => panic!("must not be a success"),
140            StdAck::Error(err) => assert_eq!(err, "bar"),
141        }
142    }
143
144    #[test]
145    fn stdack_is_success_is_error_work() {
146        let success = StdAck::success(b"foo");
147        let err = StdAck::error("bar");
148        // is_success
149        assert!(success.is_success());
150        assert!(!err.is_success());
151        // is_error
152        assert!(!success.is_error());
153        assert!(err.is_error());
154    }
155
156    #[test]
157    fn stdack_to_binary_works() {
158        let ack1 = StdAck::success(b"\x01");
159        assert_eq!(ack1.to_binary(), br#"{"result":"AQ=="}"#);
160
161        let ack2 = StdAck::error("kaputt");
162        assert_eq!(ack2.to_binary(), br#"{"error":"kaputt"}"#);
163    }
164}