diff --git a/CHANGELOG.md b/CHANGELOG.md index 3894d1f7c6..df7504f2a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,15 @@ and this project adheres to ## [Unreleased] +### Changed + +- cosmwasm-std: Change type of `Reply::result` from `ContractResult` to the new + `SubMsgResult`. Both types are equal when serialized but `ContractResult` is + documented to be the result of a contract execution, which is not the case + here. ([#1232]) + +[#1232]: https://github.com/CosmWasm/cosmwasm/pull/1232 + ## [1.0.0-beta5] - 2022-02-08 ### Changed diff --git a/MIGRATING.md b/MIGRATING.md index 9dd593df1d..5809c86727 100644 --- a/MIGRATING.md +++ b/MIGRATING.md @@ -33,6 +33,24 @@ major releases of `cosmwasm`. Note that you can also view the deps.storage.set(b"foo", b"bar"); ``` +- Replace `ContractResult` with `SubMsgResult` in `Reply` handling: + + ```diff + @@ -35,10 +35,10 @@ pub fn instantiate( + #[entry_point] + pub fn reply(deps: DepsMut, _env: Env, reply: Reply) -> StdResult { + match (reply.id, reply.result) { + - (RECEIVE_DISPATCH_ID, ContractResult::Err(err)) => { + + (RECEIVE_DISPATCH_ID, SubMsgResult::Err(err)) => { + Ok(Response::new().set_data(encode_ibc_error(err))) + } + - (INIT_CALLBACK_ID, ContractResult::Ok(response)) => handle_init_callback(deps, response), + + (INIT_CALLBACK_ID, SubMsgResult::Ok(response)) => handle_init_callback(deps, response), + _ => Err(StdError::generic_err("invalid reply id or result")), + } + } + ``` + ## 0.16 -> 1.0.0-beta - Update CosmWasm dependencies in Cargo.toml (skip the ones you don't use): diff --git a/contracts/ibc-reflect/schema/acknowledgement_msg_balances.json b/contracts/ibc-reflect/schema/acknowledgement_msg_balances.json index 6f1c3aa6e1..cf09b7c4c9 100644 --- a/contracts/ibc-reflect/schema/acknowledgement_msg_balances.json +++ b/contracts/ibc-reflect/schema/acknowledgement_msg_balances.json @@ -1,7 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "title": "AcknowledgementMsgBalances", - "description": "This is the final result type that is created and serialized in a contract for every init/execute/migrate call. The VM then deserializes this type to distinguish between successful and failed executions.\n\nWe use a custom type here instead of Rust's Result because we want to be able to define the serialization, which is a public interface. Every language that compiles to Wasm and runs in the ComsWasm VM needs to create the same JSON representation.\n\n# Examples\n\nSuccess:\n\n``` # use cosmwasm_std::{to_vec, ContractResult, Response}; let response: Response = Response::default(); let result: ContractResult = ContractResult::Ok(response); assert_eq!(to_vec(&result).unwrap(), br#\"{\"ok\":{\"messages\":[],\"attributes\":[],\"events\":[],\"data\":null}}\"#.to_vec()); ```\n\nFailure:\n\n``` # use cosmwasm_std::{to_vec, ContractResult, Response}; let error_msg = String::from(\"Something went wrong\"); let result: ContractResult = ContractResult::Err(error_msg); assert_eq!(to_vec(&result).unwrap(), br#\"{\"error\":\"Something went wrong\"}\"#.to_vec()); ```", + "description": "This is the final result type that is created and serialized in a contract for every init/execute/migrate call. The VM then deserializes this type to distinguish between successful and failed executions.\n\nWe use a custom type here instead of Rust's Result because we want to be able to define the serialization, which is a public interface. Every language that compiles to Wasm and runs in the ComsWasm VM needs to create the same JSON representation.\n\n# Examples\n\nSuccess:\n\n``` # use cosmwasm_std::{to_vec, ContractResult, Response}; let response: Response = Response::default(); let result: ContractResult = ContractResult::Ok(response); assert_eq!(to_vec(&result).unwrap(), br#\"{\"ok\":{\"messages\":[],\"attributes\":[],\"events\":[],\"data\":null}}\"#); ```\n\nFailure:\n\n``` # use cosmwasm_std::{to_vec, ContractResult, Response}; let error_msg = String::from(\"Something went wrong\"); let result: ContractResult = ContractResult::Err(error_msg); assert_eq!(to_vec(&result).unwrap(), br#\"{\"error\":\"Something went wrong\"}\"#); ```", "oneOf": [ { "type": "object", diff --git a/contracts/ibc-reflect/schema/acknowledgement_msg_dispatch.json b/contracts/ibc-reflect/schema/acknowledgement_msg_dispatch.json index 675ef12ee2..e89f54b65d 100644 --- a/contracts/ibc-reflect/schema/acknowledgement_msg_dispatch.json +++ b/contracts/ibc-reflect/schema/acknowledgement_msg_dispatch.json @@ -1,7 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "title": "AcknowledgementMsgDispatch", - "description": "This is the final result type that is created and serialized in a contract for every init/execute/migrate call. The VM then deserializes this type to distinguish between successful and failed executions.\n\nWe use a custom type here instead of Rust's Result because we want to be able to define the serialization, which is a public interface. Every language that compiles to Wasm and runs in the ComsWasm VM needs to create the same JSON representation.\n\n# Examples\n\nSuccess:\n\n``` # use cosmwasm_std::{to_vec, ContractResult, Response}; let response: Response = Response::default(); let result: ContractResult = ContractResult::Ok(response); assert_eq!(to_vec(&result).unwrap(), br#\"{\"ok\":{\"messages\":[],\"attributes\":[],\"events\":[],\"data\":null}}\"#.to_vec()); ```\n\nFailure:\n\n``` # use cosmwasm_std::{to_vec, ContractResult, Response}; let error_msg = String::from(\"Something went wrong\"); let result: ContractResult = ContractResult::Err(error_msg); assert_eq!(to_vec(&result).unwrap(), br#\"{\"error\":\"Something went wrong\"}\"#.to_vec()); ```", + "description": "This is the final result type that is created and serialized in a contract for every init/execute/migrate call. The VM then deserializes this type to distinguish between successful and failed executions.\n\nWe use a custom type here instead of Rust's Result because we want to be able to define the serialization, which is a public interface. Every language that compiles to Wasm and runs in the ComsWasm VM needs to create the same JSON representation.\n\n# Examples\n\nSuccess:\n\n``` # use cosmwasm_std::{to_vec, ContractResult, Response}; let response: Response = Response::default(); let result: ContractResult = ContractResult::Ok(response); assert_eq!(to_vec(&result).unwrap(), br#\"{\"ok\":{\"messages\":[],\"attributes\":[],\"events\":[],\"data\":null}}\"#); ```\n\nFailure:\n\n``` # use cosmwasm_std::{to_vec, ContractResult, Response}; let error_msg = String::from(\"Something went wrong\"); let result: ContractResult = ContractResult::Err(error_msg); assert_eq!(to_vec(&result).unwrap(), br#\"{\"error\":\"Something went wrong\"}\"#); ```", "oneOf": [ { "type": "object", diff --git a/contracts/ibc-reflect/schema/acknowledgement_msg_who_am_i.json b/contracts/ibc-reflect/schema/acknowledgement_msg_who_am_i.json index 16a1f0239b..bfbabf683c 100644 --- a/contracts/ibc-reflect/schema/acknowledgement_msg_who_am_i.json +++ b/contracts/ibc-reflect/schema/acknowledgement_msg_who_am_i.json @@ -1,7 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "title": "AcknowledgementMsgWhoAmI", - "description": "This is the final result type that is created and serialized in a contract for every init/execute/migrate call. The VM then deserializes this type to distinguish between successful and failed executions.\n\nWe use a custom type here instead of Rust's Result because we want to be able to define the serialization, which is a public interface. Every language that compiles to Wasm and runs in the ComsWasm VM needs to create the same JSON representation.\n\n# Examples\n\nSuccess:\n\n``` # use cosmwasm_std::{to_vec, ContractResult, Response}; let response: Response = Response::default(); let result: ContractResult = ContractResult::Ok(response); assert_eq!(to_vec(&result).unwrap(), br#\"{\"ok\":{\"messages\":[],\"attributes\":[],\"events\":[],\"data\":null}}\"#.to_vec()); ```\n\nFailure:\n\n``` # use cosmwasm_std::{to_vec, ContractResult, Response}; let error_msg = String::from(\"Something went wrong\"); let result: ContractResult = ContractResult::Err(error_msg); assert_eq!(to_vec(&result).unwrap(), br#\"{\"error\":\"Something went wrong\"}\"#.to_vec()); ```", + "description": "This is the final result type that is created and serialized in a contract for every init/execute/migrate call. The VM then deserializes this type to distinguish between successful and failed executions.\n\nWe use a custom type here instead of Rust's Result because we want to be able to define the serialization, which is a public interface. Every language that compiles to Wasm and runs in the ComsWasm VM needs to create the same JSON representation.\n\n# Examples\n\nSuccess:\n\n``` # use cosmwasm_std::{to_vec, ContractResult, Response}; let response: Response = Response::default(); let result: ContractResult = ContractResult::Ok(response); assert_eq!(to_vec(&result).unwrap(), br#\"{\"ok\":{\"messages\":[],\"attributes\":[],\"events\":[],\"data\":null}}\"#); ```\n\nFailure:\n\n``` # use cosmwasm_std::{to_vec, ContractResult, Response}; let error_msg = String::from(\"Something went wrong\"); let result: ContractResult = ContractResult::Err(error_msg); assert_eq!(to_vec(&result).unwrap(), br#\"{\"error\":\"Something went wrong\"}\"#); ```", "oneOf": [ { "type": "object", diff --git a/contracts/ibc-reflect/src/contract.rs b/contracts/ibc-reflect/src/contract.rs index bde021febc..7447e3f883 100644 --- a/contracts/ibc-reflect/src/contract.rs +++ b/contracts/ibc-reflect/src/contract.rs @@ -1,9 +1,9 @@ use cosmwasm_std::{ - entry_point, from_slice, to_binary, wasm_execute, BankMsg, Binary, ContractResult, CosmosMsg, - Deps, DepsMut, Empty, Env, Event, IbcBasicResponse, IbcChannelCloseMsg, IbcChannelConnectMsg, + entry_point, from_slice, to_binary, wasm_execute, BankMsg, Binary, CosmosMsg, Deps, DepsMut, + Empty, Env, Event, IbcBasicResponse, IbcChannelCloseMsg, IbcChannelConnectMsg, IbcChannelOpenMsg, IbcOrder, IbcPacketAckMsg, IbcPacketReceiveMsg, IbcPacketTimeoutMsg, IbcReceiveResponse, MessageInfo, Order, QueryResponse, Reply, Response, StdError, StdResult, - SubMsg, SubMsgExecutionResponse, WasmMsg, + SubMsg, SubMsgExecutionResponse, SubMsgResult, WasmMsg, }; use crate::msg::{ @@ -35,10 +35,10 @@ pub fn instantiate( #[entry_point] pub fn reply(deps: DepsMut, _env: Env, reply: Reply) -> StdResult { match (reply.id, reply.result) { - (RECEIVE_DISPATCH_ID, ContractResult::Err(err)) => { + (RECEIVE_DISPATCH_ID, SubMsgResult::Err(err)) => { Ok(Response::new().set_data(encode_ibc_error(err))) } - (INIT_CALLBACK_ID, ContractResult::Ok(response)) => handle_init_callback(deps, response), + (INIT_CALLBACK_ID, SubMsgResult::Ok(response)) => handle_init_callback(deps, response), _ => Err(StdError::generic_err("invalid reply id or result")), } } @@ -387,7 +387,7 @@ mod tests { // fake a reply and ensure this works let response = Reply { id, - result: ContractResult::Ok(SubMsgExecutionResponse { + result: SubMsgResult::Ok(SubMsgExecutionResponse { events: fake_events(&account), data: None, }), @@ -462,7 +462,7 @@ mod tests { // fake a reply and ensure this works let response = Reply { id, - result: ContractResult::Ok(SubMsgExecutionResponse { + result: SubMsgResult::Ok(SubMsgExecutionResponse { events: fake_events(REFLECT_ADDR), data: None, }), diff --git a/contracts/ibc-reflect/tests/integration.rs b/contracts/ibc-reflect/tests/integration.rs index 62a86eb2b5..29a2a22a0a 100644 --- a/contracts/ibc-reflect/tests/integration.rs +++ b/contracts/ibc-reflect/tests/integration.rs @@ -23,7 +23,7 @@ use cosmwasm_std::testing::{ }; use cosmwasm_std::{ attr, coins, BankMsg, ContractResult, CosmosMsg, Event, IbcBasicResponse, IbcOrder, - IbcReceiveResponse, Reply, Response, SubMsgExecutionResponse, WasmMsg, + IbcReceiveResponse, Reply, Response, SubMsgExecutionResponse, SubMsgResult, WasmMsg, }; use cosmwasm_vm::testing::{ ibc_channel_connect, ibc_channel_open, ibc_packet_receive, instantiate, mock_env, mock_info, @@ -95,7 +95,7 @@ fn connect( // fake a reply and ensure this works let response = Reply { id, - result: ContractResult::Ok(SubMsgExecutionResponse { + result: SubMsgResult::Ok(SubMsgExecutionResponse { events: fake_events(&account), data: None, }), @@ -171,7 +171,7 @@ fn proper_handshake_flow() { // we get the callback from reflect let response = Reply { id, - result: ContractResult::Ok(SubMsgExecutionResponse { + result: SubMsgResult::Ok(SubMsgExecutionResponse { events: fake_events(REFLECT_ADDR), data: None, }), diff --git a/contracts/reflect/src/contract.rs b/contracts/reflect/src/contract.rs index a51413ae29..dc1e7c3e24 100644 --- a/contracts/reflect/src/contract.rs +++ b/contracts/reflect/src/contract.rs @@ -177,8 +177,8 @@ mod tests { use crate::testing::mock_dependencies_with_custom_querier; use cosmwasm_std::testing::{mock_env, mock_info, MOCK_CONTRACT_ADDR}; use cosmwasm_std::{ - coin, coins, from_binary, AllBalanceResponse, BankMsg, BankQuery, Binary, ContractResult, - Event, StakingMsg, StdError, SubMsgExecutionResponse, + coin, coins, from_binary, AllBalanceResponse, BankMsg, BankQuery, Binary, Event, + StakingMsg, StdError, SubMsgExecutionResponse, SubMsgResult, }; #[test] @@ -435,7 +435,7 @@ mod tests { let id = 123u64; let data = Binary::from(b"foobar"); let events = vec![Event::new("message").add_attribute("signer", "caller-addr")]; - let result = ContractResult::Ok(SubMsgExecutionResponse { + let result = SubMsgResult::Ok(SubMsgExecutionResponse { events: events.clone(), data: Some(data.clone()), }); diff --git a/contracts/reflect/tests/integration.rs b/contracts/reflect/tests/integration.rs index 90b6d9d538..a448250542 100644 --- a/contracts/reflect/tests/integration.rs +++ b/contracts/reflect/tests/integration.rs @@ -19,7 +19,7 @@ use cosmwasm_std::{ coin, coins, from_binary, BankMsg, Binary, Coin, ContractResult, Event, Reply, Response, - StakingMsg, SubMsg, SubMsgExecutionResponse, SystemResult, + StakingMsg, SubMsg, SubMsgExecutionResponse, SubMsgResult, SystemResult, }; use cosmwasm_vm::{ testing::{ @@ -226,7 +226,7 @@ fn reply_and_query() { let id = 123u64; let data = Binary::from(b"foobar"); let events = vec![Event::new("message").add_attribute("signer", "caller-addr")]; - let result = ContractResult::Ok(SubMsgExecutionResponse { + let result = SubMsgResult::Ok(SubMsgExecutionResponse { events: events.clone(), data: Some(data.clone()), }); diff --git a/packages/std/src/lib.rs b/packages/std/src/lib.rs index 8fee4f3282..858effaa7c 100644 --- a/packages/std/src/lib.rs +++ b/packages/std/src/lib.rs @@ -60,7 +60,7 @@ pub use crate::query::{ChannelResponse, IbcQuery, ListChannelsResponse, PortIdRe pub use crate::results::{ attr, wasm_execute, wasm_instantiate, Attribute, BankMsg, ContractResult, CosmosMsg, CustomMsg, Empty, Event, QueryResponse, Reply, ReplyOn, Response, SubMsg, SubMsgExecutionResponse, - SystemResult, WasmMsg, + SubMsgResult, SystemResult, WasmMsg, }; #[cfg(feature = "staking")] pub use crate::results::{DistributionMsg, StakingMsg}; diff --git a/packages/std/src/results/contract_result.rs b/packages/std/src/results/contract_result.rs index afaaed2ad1..778db581b8 100644 --- a/packages/std/src/results/contract_result.rs +++ b/packages/std/src/results/contract_result.rs @@ -18,7 +18,7 @@ use std::fmt; /// # use cosmwasm_std::{to_vec, ContractResult, Response}; /// let response: Response = Response::default(); /// let result: ContractResult = ContractResult::Ok(response); -/// assert_eq!(to_vec(&result).unwrap(), br#"{"ok":{"messages":[],"attributes":[],"events":[],"data":null}}"#.to_vec()); +/// assert_eq!(to_vec(&result).unwrap(), br#"{"ok":{"messages":[],"attributes":[],"events":[],"data":null}}"#); /// ``` /// /// Failure: @@ -27,7 +27,7 @@ use std::fmt; /// # use cosmwasm_std::{to_vec, ContractResult, Response}; /// let error_msg = String::from("Something went wrong"); /// let result: ContractResult = ContractResult::Err(error_msg); -/// assert_eq!(to_vec(&result).unwrap(), br#"{"error":"Something went wrong"}"#.to_vec()); +/// assert_eq!(to_vec(&result).unwrap(), br#"{"error":"Something went wrong"}"#); /// ``` #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] #[serde(rename_all = "snake_case")] @@ -101,7 +101,7 @@ mod tests { let result: ContractResult = ContractResult::Ok(Response::default()); assert_eq!( to_vec(&result).unwrap(), - br#"{"ok":{"messages":[],"attributes":[],"events":[],"data":null}}"#.to_vec() + br#"{"ok":{"messages":[],"attributes":[],"events":[],"data":null}}"# ); let result: ContractResult = ContractResult::Err("broken".to_string()); diff --git a/packages/std/src/results/mod.rs b/packages/std/src/results/mod.rs index 2bd4663fb3..a208279ac6 100644 --- a/packages/std/src/results/mod.rs +++ b/packages/std/src/results/mod.rs @@ -19,5 +19,5 @@ pub use empty::Empty; pub use events::{attr, Attribute, Event}; pub use query::QueryResponse; pub use response::Response; -pub use submessages::{Reply, ReplyOn, SubMsg, SubMsgExecutionResponse}; +pub use submessages::{Reply, ReplyOn, SubMsg, SubMsgExecutionResponse, SubMsgResult}; pub use system_result::SystemResult; diff --git a/packages/std/src/results/response.rs b/packages/std/src/results/response.rs index a86498a295..6d876cec3c 100644 --- a/packages/std/src/results/response.rs +++ b/packages/std/src/results/response.rs @@ -210,7 +210,7 @@ mod tests { use super::super::BankMsg; use super::*; use crate::results::submessages::{ReplyOn, UNUSED_MSG_ID}; - use crate::{coins, from_slice, to_vec}; + use crate::{coins, from_slice, to_vec, ContractResult}; #[test] fn can_serialize_and_deserialize_init_response() { @@ -248,4 +248,20 @@ mod tests { let deserialized: Response = from_slice(&serialized).expect("decode contract result"); assert_eq!(deserialized, original); } + + #[test] + fn contract_result_is_ok_works() { + let success = ContractResult::<()>::Ok(()); + let failure = ContractResult::<()>::Err("broken".to_string()); + assert!(success.is_ok()); + assert!(!failure.is_ok()); + } + + #[test] + fn contract_result_is_err_works() { + let success = ContractResult::<()>::Ok(()); + let failure = ContractResult::<()>::Err("broken".to_string()); + assert!(failure.is_err()); + assert!(!success.is_err()); + } } diff --git a/packages/std/src/results/submessages.rs b/packages/std/src/results/submessages.rs index ef3d8d5681..34f0220b5e 100644 --- a/packages/std/src/results/submessages.rs +++ b/packages/std/src/results/submessages.rs @@ -1,7 +1,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use crate::{Binary, ContractResult}; +use crate::Binary; use super::{CosmosMsg, Empty, Event}; @@ -100,12 +100,232 @@ pub struct Reply { /// The ID that the contract set when emitting the `SubMsg`. /// Use this to identify which submessage triggered the `reply`. pub id: u64, - pub result: ContractResult, + pub result: SubMsgResult, } -/// The information we get back from a successful sub-call, with full sdk events +/// This is the result type that is returned from a sub message execution. +/// +/// We use a custom type here instead of Rust's Result because we want to be able to +/// define the serialization, which is a public interface. Every language that compiles +/// to Wasm and runs in the ComsWasm VM needs to create the same JSON representation. +/// +/// Until version 1.0.0-beta5, `ContractResult` was used instead +/// of this type. Once serialized, the two types are the same. However, in the Rust type +/// system we want different types for clarity and documenation reasons. +/// +/// # Examples +/// +/// Success: +/// +/// ``` +/// # use cosmwasm_std::{to_vec, Binary, Event, SubMsgExecutionResponse, SubMsgResult}; +/// let response = SubMsgExecutionResponse { +/// data: Some(Binary::from_base64("MTIzCg==").unwrap()), +/// events: vec![Event::new("wasm").add_attribute("fo", "ba")], +/// }; +/// let result: SubMsgResult = SubMsgResult::Ok(response); +/// assert_eq!(to_vec(&result).unwrap(), br#"{"ok":{"events":[{"type":"wasm","attributes":[{"key":"fo","value":"ba"}]}],"data":"MTIzCg=="}}"#); +/// ``` +/// +/// Failure: +/// +/// ``` +/// # use cosmwasm_std::{to_vec, SubMsgResult, Response}; +/// let error_msg = String::from("Something went wrong"); +/// let result = SubMsgResult::Err(error_msg); +/// assert_eq!(to_vec(&result).unwrap(), br#"{"error":"Something went wrong"}"#); +/// ``` +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum SubMsgResult { + Ok(SubMsgExecutionResponse), + /// An error type that every custom error created by contract developers can be converted to. + /// This could potientially have more structure, but String is the easiest. + #[serde(rename = "error")] + Err(String), +} + +// Implementations here mimic the Result API and should be implemented via a conversion to Result +// to ensure API consistency +impl SubMsgResult { + /// Converts a `SubMsgResult` to a `Result` as a convenient way + /// to access the full Result API. + pub fn into_result(self) -> Result { + Result::::from(self) + } + + pub fn unwrap(self) -> SubMsgExecutionResponse { + self.into_result().unwrap() + } + + pub fn unwrap_err(self) -> String { + self.into_result().unwrap_err() + } + + pub fn is_ok(&self) -> bool { + matches!(self, SubMsgResult::Ok(_)) + } + + pub fn is_err(&self) -> bool { + matches!(self, SubMsgResult::Err(_)) + } +} + +impl From> for SubMsgResult { + fn from(original: Result) -> SubMsgResult { + match original { + Ok(value) => SubMsgResult::Ok(value), + Err(err) => SubMsgResult::Err(err.to_string()), + } + } +} + +impl From for Result { + fn from(original: SubMsgResult) -> Result { + match original { + SubMsgResult::Ok(value) => Ok(value), + SubMsgResult::Err(err) => Err(err), + } + } +} + +/// The information we get back from a successful sub message execution, +/// with full Cosmos SDK events. #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct SubMsgExecutionResponse { pub events: Vec, pub data: Option, } + +#[cfg(test)] +mod tests { + use super::*; + use crate::{from_slice, to_vec, StdError, StdResult}; + + #[test] + fn sub_msg_result_serialization_works() { + let result = SubMsgResult::Ok(SubMsgExecutionResponse { + data: None, + events: vec![], + }); + assert_eq!( + &to_vec(&result).unwrap(), + br#"{"ok":{"events":[],"data":null}}"# + ); + + let result = SubMsgResult::Ok(SubMsgExecutionResponse { + data: Some(Binary::from_base64("MTIzCg==").unwrap()), + events: vec![Event::new("wasm").add_attribute("fo", "ba")], + }); + assert_eq!( + &to_vec(&result).unwrap(), + br#"{"ok":{"events":[{"type":"wasm","attributes":[{"key":"fo","value":"ba"}]}],"data":"MTIzCg=="}}"# + ); + + let result: SubMsgResult = SubMsgResult::Err("broken".to_string()); + assert_eq!(&to_vec(&result).unwrap(), b"{\"error\":\"broken\"}"); + } + + #[test] + fn sub_msg_result_deserialization_works() { + let result: SubMsgResult = from_slice(br#"{"ok":{"events":[],"data":null}}"#).unwrap(); + assert_eq!( + result, + SubMsgResult::Ok(SubMsgExecutionResponse { + events: vec![], + data: None, + }) + ); + + let result: SubMsgResult = from_slice( + br#"{"ok":{"events":[{"type":"wasm","attributes":[{"key":"fo","value":"ba"}]}],"data":"MTIzCg=="}}"#).unwrap(); + assert_eq!( + result, + SubMsgResult::Ok(SubMsgExecutionResponse { + data: Some(Binary::from_base64("MTIzCg==").unwrap()), + events: vec![Event::new("wasm").add_attribute("fo", "ba")], + }) + ); + + let result: SubMsgResult = from_slice(br#"{"error":"broken"}"#).unwrap(); + assert_eq!(result, SubMsgResult::Err("broken".to_string())); + + // fails for additional attributes + let parse: StdResult = from_slice(br#"{"unrelated":321,"error":"broken"}"#); + match parse.unwrap_err() { + StdError::ParseErr { .. } => {} + err => panic!("Unexpected error: {:?}", err), + } + let parse: StdResult = from_slice(br#"{"error":"broken","unrelated":321}"#); + match parse.unwrap_err() { + StdError::ParseErr { .. } => {} + err => panic!("Unexpected error: {:?}", err), + } + } + + #[test] + fn sub_msg_result_is_ok_works() { + let success = SubMsgResult::Ok(SubMsgExecutionResponse { + data: Some(Binary::from_base64("MTIzCg==").unwrap()), + events: vec![Event::new("wasm").add_attribute("fo", "ba")], + }); + let failure = SubMsgResult::Err("broken".to_string()); + assert!(success.is_ok()); + assert!(!failure.is_ok()); + } + + #[test] + fn sub_msg_result_is_err_works() { + let success = SubMsgResult::Ok(SubMsgExecutionResponse { + data: Some(Binary::from_base64("MTIzCg==").unwrap()), + events: vec![Event::new("wasm").add_attribute("fo", "ba")], + }); + let failure = SubMsgResult::Err("broken".to_string()); + assert!(failure.is_err()); + assert!(!success.is_err()); + } + + #[test] + fn sub_msg_result_can_convert_from_core_result() { + let original: Result = Ok(SubMsgExecutionResponse { + data: Some(Binary::from_base64("MTIzCg==").unwrap()), + events: vec![], + }); + let converted: SubMsgResult = original.into(); + assert_eq!( + converted, + SubMsgResult::Ok(SubMsgExecutionResponse { + data: Some(Binary::from_base64("MTIzCg==").unwrap()), + events: vec![], + }) + ); + + let original: Result = + Err(StdError::generic_err("broken")); + let converted: SubMsgResult = original.into(); + assert_eq!( + converted, + SubMsgResult::Err("Generic error: broken".to_string()) + ); + } + + #[test] + fn sub_msg_result_can_convert_to_core_result() { + let original = SubMsgResult::Ok(SubMsgExecutionResponse { + data: Some(Binary::from_base64("MTIzCg==").unwrap()), + events: vec![], + }); + let converted: Result = original.into(); + assert_eq!( + converted, + Ok(SubMsgExecutionResponse { + data: Some(Binary::from_base64("MTIzCg==").unwrap()), + events: vec![], + }) + ); + + let original = SubMsgResult::Err("went wrong".to_string()); + let converted: Result = original.into(); + assert_eq!(converted, Err("went wrong".to_string())); + } +} diff --git a/packages/std/src/results/system_result.rs b/packages/std/src/results/system_result.rs index 8fd844c5b8..5a5ee4d525 100644 --- a/packages/std/src/results/system_result.rs +++ b/packages/std/src/results/system_result.rs @@ -18,7 +18,7 @@ use super::super::errors::SystemError; /// # use cosmwasm_std::{to_vec, Binary, ContractResult, SystemResult}; /// let data = Binary::from(b"hello, world"); /// let result = SystemResult::Ok(ContractResult::Ok(data)); -/// assert_eq!(to_vec(&result).unwrap(), br#"{"ok":{"ok":"aGVsbG8sIHdvcmxk"}}"#.to_vec()); +/// assert_eq!(to_vec(&result).unwrap(), br#"{"ok":{"ok":"aGVsbG8sIHdvcmxk"}}"#); /// ``` /// /// Failure: @@ -27,7 +27,7 @@ use super::super::errors::SystemError; /// # use cosmwasm_std::{to_vec, Binary, ContractResult, SystemResult, SystemError}; /// let error = SystemError::Unknown {}; /// let result: SystemResult = SystemResult::Err(error); -/// assert_eq!(to_vec(&result).unwrap(), br#"{"error":{"unknown":{}}}"#.to_vec()); +/// assert_eq!(to_vec(&result).unwrap(), br#"{"error":{"unknown":{}}}"#); /// ``` #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] #[serde(rename_all = "snake_case")] diff --git a/packages/vm/src/calls.rs b/packages/vm/src/calls.rs index 0d2aca7475..7a3c7a8c2f 100644 --- a/packages/vm/src/calls.rs +++ b/packages/vm/src/calls.rs @@ -682,6 +682,7 @@ mod tests { }; use cosmwasm_std::{ Empty, Event, IbcAcknowledgement, IbcOrder, Reply, ReplyOn, SubMsgExecutionResponse, + SubMsgResult, }; static CONTRACT: &[u8] = include_bytes!("../testdata/ibc_reflect.wasm"); const IBC_VERSION: &str = "ibc-reflect-v1"; @@ -726,7 +727,7 @@ mod tests { // which creates a reflect account. here we get the callback let response = Reply { id, - result: ContractResult::Ok(SubMsgExecutionResponse { + result: SubMsgResult::Ok(SubMsgExecutionResponse { events: vec![event], data: None, }),