cosmwasm_std/
stdack.rs

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