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}