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(
38    Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema, cw_schema::Schemaifier,
39)]
40#[serde(rename_all = "snake_case")]
41pub enum StdAck {
42    #[serde(rename = "result")]
43    Success(Binary),
44    Error(String),
45}
46
47impl StdAck {
48    /// Creates a success ack with the given data
49    pub fn success(data: impl Into<Binary>) -> Self {
50        StdAck::Success(data.into())
51    }
52
53    /// Creates an error ack
54    pub fn error(err: impl Into<String>) -> Self {
55        StdAck::Error(err.into())
56    }
57
58    #[must_use = "if you intended to assert that this is a success, consider `.unwrap()` instead"]
59    #[inline]
60    pub const fn is_success(&self) -> bool {
61        matches!(*self, StdAck::Success(_))
62    }
63
64    #[must_use = "if you intended to assert that this is an error, consider `.unwrap_err()` instead"]
65    #[inline]
66    pub const fn is_error(&self) -> bool {
67        !self.is_success()
68    }
69
70    /// Serialized the ack to binary using JSON. This used for setting the acknowledgement
71    /// field in IbcReceiveResponse.
72    ///
73    /// ## Examples
74    ///
75    /// Show how the acknowledgement looks on the write:
76    ///
77    /// ```
78    /// # use cosmwasm_std::StdAck;
79    /// let ack1 = StdAck::success(b"\x01"); // 0x01 is a FungibleTokenPacketSuccess from ICS-20.
80    /// assert_eq!(ack1.to_binary(), br#"{"result":"AQ=="}"#);
81    ///
82    /// let ack2 = StdAck::error("kaputt"); // Some free text error message
83    /// assert_eq!(ack2.to_binary(), br#"{"error":"kaputt"}"#);
84    /// ```
85    ///
86    /// Set acknowledgement field in `IbcReceiveResponse`:
87    ///
88    /// ```
89    /// use cosmwasm_std::{StdAck, IbcReceiveResponse};
90    ///
91    /// let ack = StdAck::success(b"\x01"); // 0x01 is a FungibleTokenPacketSuccess from ICS-20.
92    ///
93    /// let res: IbcReceiveResponse = IbcReceiveResponse::new(ack.to_binary());
94    /// let res: IbcReceiveResponse = IbcReceiveResponse::new(ack); // Does the same but consumes the instance
95    /// ```
96    pub fn to_binary(&self) -> Binary {
97        // We need a non-failing StdAck -> Binary conversion to allow using StdAck in
98        // `impl Into<Binary>` arguments.
99        // Pretty sure this cannot fail. If that changes we can create a non-failing implementation here.
100        to_json_binary(&self).unwrap()
101    }
102
103    pub fn unwrap(self) -> Binary {
104        match self {
105            StdAck::Success(data) => data,
106            StdAck::Error(err) => panic!("{}", err),
107        }
108    }
109
110    pub fn unwrap_err(self) -> String {
111        match self {
112            StdAck::Success(_) => panic!("not an error"),
113            StdAck::Error(err) => err,
114        }
115    }
116}
117
118impl From<StdAck> for Binary {
119    fn from(original: StdAck) -> Binary {
120        original.to_binary()
121    }
122}
123
124#[cfg(test)]
125mod tests {
126    use super::*;
127
128    #[test]
129    fn stdack_success_works() {
130        let success = StdAck::success(b"foo");
131        match success {
132            StdAck::Success(data) => assert_eq!(data, b"foo"),
133            StdAck::Error(_err) => panic!("must not be an error"),
134        }
135    }
136
137    #[test]
138    fn stdack_error_works() {
139        let err = StdAck::error("bar");
140        match err {
141            StdAck::Success(_data) => panic!("must not be a success"),
142            StdAck::Error(err) => assert_eq!(err, "bar"),
143        }
144    }
145
146    #[test]
147    fn stdack_is_success_is_error_work() {
148        let success = StdAck::success(b"foo");
149        let err = StdAck::error("bar");
150        // is_success
151        assert!(success.is_success());
152        assert!(!err.is_success());
153        // is_error
154        assert!(!success.is_error());
155        assert!(err.is_error());
156    }
157
158    #[test]
159    fn stdack_to_binary_works() {
160        let ack1 = StdAck::success(b"\x01");
161        assert_eq!(ack1.to_binary(), br#"{"result":"AQ=="}"#);
162
163        let ack2 = StdAck::error("kaputt");
164        assert_eq!(ack2.to_binary(), br#"{"error":"kaputt"}"#);
165    }
166}