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}