Skip to content

Commit 8133151

Browse files
committed
Create dedicated SubMsgResult
1 parent 0a1bb08 commit 8133151

File tree

8 files changed

+220
-21
lines changed

8 files changed

+220
-21
lines changed

contracts/ibc-reflect/src/contract.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use cosmwasm_std::{
2-
entry_point, from_slice, to_binary, wasm_execute, BankMsg, Binary, ContractResult, CosmosMsg,
3-
Deps, DepsMut, Empty, Env, Event, IbcBasicResponse, IbcChannelCloseMsg, IbcChannelConnectMsg,
2+
entry_point, from_slice, to_binary, wasm_execute, BankMsg, Binary, CosmosMsg, Deps, DepsMut,
3+
Empty, Env, Event, IbcBasicResponse, IbcChannelCloseMsg, IbcChannelConnectMsg,
44
IbcChannelOpenMsg, IbcOrder, IbcPacketAckMsg, IbcPacketReceiveMsg, IbcPacketTimeoutMsg,
55
IbcReceiveResponse, MessageInfo, Order, QueryResponse, Reply, Response, StdError, StdResult,
6-
SubMsg, SubMsgExecutionResponse, WasmMsg,
6+
SubMsg, SubMsgExecutionResponse, SubMsgResult, WasmMsg,
77
};
88

99
use crate::msg::{
@@ -35,10 +35,10 @@ pub fn instantiate(
3535
#[entry_point]
3636
pub fn reply(deps: DepsMut, _env: Env, reply: Reply) -> StdResult<Response> {
3737
match (reply.id, reply.result) {
38-
(RECEIVE_DISPATCH_ID, ContractResult::Err(err)) => {
38+
(RECEIVE_DISPATCH_ID, SubMsgResult::Err(err)) => {
3939
Ok(Response::new().set_data(encode_ibc_error(err)))
4040
}
41-
(INIT_CALLBACK_ID, ContractResult::Ok(response)) => handle_init_callback(deps, response),
41+
(INIT_CALLBACK_ID, SubMsgResult::Ok(response)) => handle_init_callback(deps, response),
4242
_ => Err(StdError::generic_err("invalid reply id or result")),
4343
}
4444
}
@@ -387,7 +387,7 @@ mod tests {
387387
// fake a reply and ensure this works
388388
let response = Reply {
389389
id,
390-
result: ContractResult::Ok(SubMsgExecutionResponse {
390+
result: SubMsgResult::Ok(SubMsgExecutionResponse {
391391
events: fake_events(&account),
392392
data: None,
393393
}),
@@ -462,7 +462,7 @@ mod tests {
462462
// fake a reply and ensure this works
463463
let response = Reply {
464464
id,
465-
result: ContractResult::Ok(SubMsgExecutionResponse {
465+
result: SubMsgResult::Ok(SubMsgExecutionResponse {
466466
events: fake_events(REFLECT_ADDR),
467467
data: None,
468468
}),

contracts/ibc-reflect/tests/integration.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use cosmwasm_std::testing::{
2323
};
2424
use cosmwasm_std::{
2525
attr, coins, BankMsg, ContractResult, CosmosMsg, Event, IbcBasicResponse, IbcOrder,
26-
IbcReceiveResponse, Reply, Response, SubMsgExecutionResponse, WasmMsg,
26+
IbcReceiveResponse, Reply, Response, SubMsgExecutionResponse, SubMsgResult, WasmMsg,
2727
};
2828
use cosmwasm_vm::testing::{
2929
ibc_channel_connect, ibc_channel_open, ibc_packet_receive, instantiate, mock_env, mock_info,
@@ -95,7 +95,7 @@ fn connect(
9595
// fake a reply and ensure this works
9696
let response = Reply {
9797
id,
98-
result: ContractResult::Ok(SubMsgExecutionResponse {
98+
result: SubMsgResult::Ok(SubMsgExecutionResponse {
9999
events: fake_events(&account),
100100
data: None,
101101
}),
@@ -171,7 +171,7 @@ fn proper_handshake_flow() {
171171
// we get the callback from reflect
172172
let response = Reply {
173173
id,
174-
result: ContractResult::Ok(SubMsgExecutionResponse {
174+
result: SubMsgResult::Ok(SubMsgExecutionResponse {
175175
events: fake_events(REFLECT_ADDR),
176176
data: None,
177177
}),

contracts/reflect/src/contract.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -177,8 +177,8 @@ mod tests {
177177
use crate::testing::mock_dependencies_with_custom_querier;
178178
use cosmwasm_std::testing::{mock_env, mock_info, MOCK_CONTRACT_ADDR};
179179
use cosmwasm_std::{
180-
coin, coins, from_binary, AllBalanceResponse, BankMsg, BankQuery, Binary, ContractResult,
181-
Event, StakingMsg, StdError, SubMsgExecutionResponse,
180+
coin, coins, from_binary, AllBalanceResponse, BankMsg, BankQuery, Binary, Event,
181+
StakingMsg, StdError, SubMsgExecutionResponse, SubMsgResult,
182182
};
183183

184184
#[test]
@@ -435,7 +435,7 @@ mod tests {
435435
let id = 123u64;
436436
let data = Binary::from(b"foobar");
437437
let events = vec![Event::new("message").add_attribute("signer", "caller-addr")];
438-
let result = ContractResult::Ok(SubMsgExecutionResponse {
438+
let result = SubMsgResult::Ok(SubMsgExecutionResponse {
439439
events: events.clone(),
440440
data: Some(data.clone()),
441441
});

contracts/reflect/tests/integration.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
2020
use cosmwasm_std::{
2121
coin, coins, from_binary, BankMsg, Binary, Coin, ContractResult, Event, Reply, Response,
22-
StakingMsg, SubMsg, SubMsgExecutionResponse, SystemResult,
22+
StakingMsg, SubMsg, SubMsgExecutionResponse, SubMsgResult, SystemResult,
2323
};
2424
use cosmwasm_vm::{
2525
testing::{
@@ -226,7 +226,7 @@ fn reply_and_query() {
226226
let id = 123u64;
227227
let data = Binary::from(b"foobar");
228228
let events = vec![Event::new("message").add_attribute("signer", "caller-addr")];
229-
let result = ContractResult::Ok(SubMsgExecutionResponse {
229+
let result = SubMsgResult::Ok(SubMsgExecutionResponse {
230230
events: events.clone(),
231231
data: Some(data.clone()),
232232
});

packages/std/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ pub use crate::query::{ChannelResponse, IbcQuery, ListChannelsResponse, PortIdRe
6060
pub use crate::results::{
6161
attr, wasm_execute, wasm_instantiate, Attribute, BankMsg, ContractResult, CosmosMsg, CustomMsg,
6262
Empty, Event, QueryResponse, Reply, ReplyOn, Response, SubMsg, SubMsgExecutionResponse,
63-
SystemResult, WasmMsg,
63+
SubMsgResult, SystemResult, WasmMsg,
6464
};
6565
#[cfg(feature = "staking")]
6666
pub use crate::results::{DistributionMsg, StakingMsg};

packages/std/src/results/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,5 @@ pub use empty::Empty;
1919
pub use events::{attr, Attribute, Event};
2020
pub use query::QueryResponse;
2121
pub use response::Response;
22-
pub use submessages::{Reply, ReplyOn, SubMsg, SubMsgExecutionResponse};
22+
pub use submessages::{Reply, ReplyOn, SubMsg, SubMsgExecutionResponse, SubMsgResult};
2323
pub use system_result::SystemResult;

packages/std/src/results/submessages.rs

Lines changed: 201 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use schemars::JsonSchema;
22
use serde::{Deserialize, Serialize};
33

4-
use crate::{Binary, ContractResult};
4+
use crate::Binary;
55

66
use super::{CosmosMsg, Empty, Event};
77

@@ -100,12 +100,210 @@ pub struct Reply {
100100
/// The ID that the contract set when emitting the `SubMsg`.
101101
/// Use this to identify which submessage triggered the `reply`.
102102
pub id: u64,
103-
pub result: ContractResult<SubMsgExecutionResponse>,
103+
pub result: SubMsgResult,
104104
}
105105

106-
/// The information we get back from a successful sub-call, with full sdk events
106+
/// This is the result type that is returned from a sub message execution.
107+
///
108+
/// We use a custom type here instead of Rust's Result because we want to be able to
109+
/// define the serialization, which is a public interface. Every language that compiles
110+
/// to Wasm and runs in the ComsWasm VM needs to create the same JSON representation.
111+
///
112+
/// Until version 1.0.0-beta5, `ContractResult<SubMsgExecutionResponse>` was used instead
113+
/// of this type. Once serialized, the two types are the same. However, in the Rust type
114+
/// system we want different types for clarity and documenation reasons.
115+
///
116+
/// # Examples
117+
///
118+
/// Success:
119+
///
120+
/// ```
121+
/// # use cosmwasm_std::{to_vec, Binary, Event, SubMsgExecutionResponse, SubMsgResult};
122+
/// let response = SubMsgExecutionResponse {
123+
/// data: Some(Binary::from_base64("MTIzCg==").unwrap()),
124+
/// events: vec![Event::new("wasm").add_attribute("fo", "ba")],
125+
/// };
126+
/// let result: SubMsgResult = SubMsgResult::Ok(response);
127+
/// assert_eq!(to_vec(&result).unwrap(), br#"{"ok":{"events":[{"type":"wasm","attributes":[{"key":"fo","value":"ba"}]}],"data":"MTIzCg=="}}"#);
128+
/// ```
129+
///
130+
/// Failure:
131+
///
132+
/// ```
133+
/// # use cosmwasm_std::{to_vec, SubMsgResult, Response};
134+
/// let error_msg = String::from("Something went wrong");
135+
/// let result = SubMsgResult::Err(error_msg);
136+
/// assert_eq!(to_vec(&result).unwrap(), br#"{"error":"Something went wrong"}"#);
137+
/// ```
138+
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
139+
#[serde(rename_all = "snake_case")]
140+
pub enum SubMsgResult {
141+
Ok(SubMsgExecutionResponse),
142+
/// An error type that every custom error created by contract developers can be converted to.
143+
/// This could potientially have more structure, but String is the easiest.
144+
#[serde(rename = "error")]
145+
Err(String),
146+
}
147+
148+
// Implementations here mimic the Result API and should be implemented via a conversion to Result
149+
// to ensure API consistency
150+
impl SubMsgResult {
151+
/// Converts a `SubMsgResult<S>` to a `Result<S, String>` as a convenient way
152+
/// to access the full Result API.
153+
pub fn into_result(self) -> Result<SubMsgExecutionResponse, String> {
154+
Result::<SubMsgExecutionResponse, String>::from(self)
155+
}
156+
157+
pub fn unwrap(self) -> SubMsgExecutionResponse {
158+
self.into_result().unwrap()
159+
}
160+
161+
pub fn unwrap_err(self) -> String {
162+
self.into_result().unwrap_err()
163+
}
164+
165+
pub fn is_ok(&self) -> bool {
166+
matches!(self, SubMsgResult::Ok(_))
167+
}
168+
169+
pub fn is_err(&self) -> bool {
170+
matches!(self, SubMsgResult::Err(_))
171+
}
172+
}
173+
174+
impl<E: ToString> From<Result<SubMsgExecutionResponse, E>> for SubMsgResult {
175+
fn from(original: Result<SubMsgExecutionResponse, E>) -> SubMsgResult {
176+
match original {
177+
Ok(value) => SubMsgResult::Ok(value),
178+
Err(err) => SubMsgResult::Err(err.to_string()),
179+
}
180+
}
181+
}
182+
183+
impl From<SubMsgResult> for Result<SubMsgExecutionResponse, String> {
184+
fn from(original: SubMsgResult) -> Result<SubMsgExecutionResponse, String> {
185+
match original {
186+
SubMsgResult::Ok(value) => Ok(value),
187+
SubMsgResult::Err(err) => Err(err),
188+
}
189+
}
190+
}
191+
192+
/// The information we get back from a successful sub message execution,
193+
/// with full Cosmos SDK events.
107194
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
108195
pub struct SubMsgExecutionResponse {
109196
pub events: Vec<Event>,
110197
pub data: Option<Binary>,
111198
}
199+
200+
#[cfg(test)]
201+
mod tests {
202+
use super::*;
203+
use crate::{from_slice, to_vec, StdError, StdResult};
204+
205+
#[test]
206+
fn sub_msg_result_serialization_works() {
207+
let result = SubMsgResult::Ok(SubMsgExecutionResponse {
208+
data: None,
209+
events: vec![],
210+
});
211+
assert_eq!(
212+
&to_vec(&result).unwrap(),
213+
br#"{"ok":{"events":[],"data":null}}"#
214+
);
215+
216+
let result = SubMsgResult::Ok(SubMsgExecutionResponse {
217+
data: Some(Binary::from_base64("MTIzCg==").unwrap()),
218+
events: vec![Event::new("wasm").add_attribute("fo", "ba")],
219+
});
220+
assert_eq!(
221+
&to_vec(&result).unwrap(),
222+
br#"{"ok":{"events":[{"type":"wasm","attributes":[{"key":"fo","value":"ba"}]}],"data":"MTIzCg=="}}"#
223+
);
224+
225+
let result: SubMsgResult = SubMsgResult::Err("broken".to_string());
226+
assert_eq!(&to_vec(&result).unwrap(), b"{\"error\":\"broken\"}");
227+
}
228+
229+
#[test]
230+
fn sub_msg_result_deserialization_works() {
231+
let result: SubMsgResult = from_slice(br#"{"ok":{"events":[],"data":null}}"#).unwrap();
232+
assert_eq!(
233+
result,
234+
SubMsgResult::Ok(SubMsgExecutionResponse {
235+
events: vec![],
236+
data: None,
237+
})
238+
);
239+
240+
let result: SubMsgResult = from_slice(
241+
br#"{"ok":{"events":[{"type":"wasm","attributes":[{"key":"fo","value":"ba"}]}],"data":"MTIzCg=="}}"#).unwrap();
242+
assert_eq!(
243+
result,
244+
SubMsgResult::Ok(SubMsgExecutionResponse {
245+
data: Some(Binary::from_base64("MTIzCg==").unwrap()),
246+
events: vec![Event::new("wasm").add_attribute("fo", "ba")],
247+
})
248+
);
249+
250+
let result: SubMsgResult = from_slice(br#"{"error":"broken"}"#).unwrap();
251+
assert_eq!(result, SubMsgResult::Err("broken".to_string()));
252+
253+
// fails for additional attributes
254+
let parse: StdResult<SubMsgResult> = from_slice(br#"{"unrelated":321,"error":"broken"}"#);
255+
match parse.unwrap_err() {
256+
StdError::ParseErr { .. } => {}
257+
err => panic!("Unexpected error: {:?}", err),
258+
}
259+
let parse: StdResult<SubMsgResult> = from_slice(br#"{"error":"broken","unrelated":321}"#);
260+
match parse.unwrap_err() {
261+
StdError::ParseErr { .. } => {}
262+
err => panic!("Unexpected error: {:?}", err),
263+
}
264+
}
265+
266+
#[test]
267+
fn sub_msg_result_can_convert_from_core_result() {
268+
let original: Result<SubMsgExecutionResponse, StdError> = Ok(SubMsgExecutionResponse {
269+
data: Some(Binary::from_base64("MTIzCg==").unwrap()),
270+
events: vec![],
271+
});
272+
let converted: SubMsgResult = original.into();
273+
assert_eq!(
274+
converted,
275+
SubMsgResult::Ok(SubMsgExecutionResponse {
276+
data: Some(Binary::from_base64("MTIzCg==").unwrap()),
277+
events: vec![],
278+
})
279+
);
280+
281+
let original: Result<SubMsgExecutionResponse, StdError> =
282+
Err(StdError::generic_err("broken"));
283+
let converted: SubMsgResult = original.into();
284+
assert_eq!(
285+
converted,
286+
SubMsgResult::Err("Generic error: broken".to_string())
287+
);
288+
}
289+
290+
#[test]
291+
fn sub_msg_result_can_convert_to_core_result() {
292+
let original = SubMsgResult::Ok(SubMsgExecutionResponse {
293+
data: Some(Binary::from_base64("MTIzCg==").unwrap()),
294+
events: vec![],
295+
});
296+
let converted: Result<SubMsgExecutionResponse, String> = original.into();
297+
assert_eq!(
298+
converted,
299+
Ok(SubMsgExecutionResponse {
300+
data: Some(Binary::from_base64("MTIzCg==").unwrap()),
301+
events: vec![],
302+
})
303+
);
304+
305+
let original = SubMsgResult::Err("went wrong".to_string());
306+
let converted: Result<SubMsgExecutionResponse, String> = original.into();
307+
assert_eq!(converted, Err("went wrong".to_string()));
308+
}
309+
}

packages/vm/src/calls.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -682,6 +682,7 @@ mod tests {
682682
};
683683
use cosmwasm_std::{
684684
Empty, Event, IbcAcknowledgement, IbcOrder, Reply, ReplyOn, SubMsgExecutionResponse,
685+
SubMsgResult,
685686
};
686687
static CONTRACT: &[u8] = include_bytes!("../testdata/ibc_reflect.wasm");
687688
const IBC_VERSION: &str = "ibc-reflect-v1";
@@ -726,7 +727,7 @@ mod tests {
726727
// which creates a reflect account. here we get the callback
727728
let response = Reply {
728729
id,
729-
result: ContractResult::Ok(SubMsgExecutionResponse {
730+
result: SubMsgResult::Ok(SubMsgExecutionResponse {
730731
events: vec![event],
731732
data: None,
732733
}),

0 commit comments

Comments
 (0)