Repository: CosmWasm/cosmwasm
Pull request #968 (2021-06-21 08:55:58)

Custom events
Part of work for #551. This basically adds the `events` field to the contract result type.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b52691f78..7eda36afa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@ and this project adheres to
 ### Added
 
 - cosmwasm-std: Implement `Sub` and `SubAssign` for `Uint128`
+- cosmwasm-std: Implement custom events for contract execution results
 
 ### Removed
 
@@ -47,6 +48,8 @@ and this project adheres to
 - cosmwasm-std: Add `SubMsg` constructors: `::new()`, `::reply_on_error()`,
   `::reply_on_success()`, `::reply_always()`; Add `with_gas_limit` to add a gas
   limit to any those constructors ([#961]).
+- cosmwasm-std: Change `Event`'s constructor - it no longer takes a vector of
+  attributes and instead constructs an empty one
 
 [#961]: https://github.com/CosmWasm/cosmwasm/pull/961
 
diff --git a/contracts/burner/src/contract.rs b/contracts/burner/src/contract.rs
index 85b06cf3e..3ddebad83 100644
--- a/contracts/burner/src/contract.rs
+++ b/contracts/burner/src/contract.rs
@@ -42,6 +42,7 @@ pub fn migrate(deps: DepsMut, env: Env, msg: MigrateMsg) -> StdResult<Response>
     Ok(Response {
         messages: vec![SubMsg::new(send)],
         attributes: vec![attr("action", "burn"), attr("payout", msg.payout)],
+        events: vec![],
         data: Some(data_msg.into()),
     })
 }
diff --git a/contracts/hackatom/src/contract.rs b/contracts/hackatom/src/contract.rs
index 99763242c..eb1486ce2 100644
--- a/contracts/hackatom/src/contract.rs
+++ b/contracts/hackatom/src/contract.rs
@@ -2,7 +2,7 @@ use sha2::{Digest, Sha256};
 
 use cosmwasm_std::{
     entry_point, from_slice, to_binary, to_vec, Addr, AllBalanceResponse, Api, BankMsg,
-    CanonicalAddr, Deps, DepsMut, Env, MessageInfo, QueryRequest, QueryResponse, Response,
+    CanonicalAddr, Deps, DepsMut, Env, Event, MessageInfo, QueryRequest, QueryResponse, Response,
     StdError, StdResult, WasmQuery,
 };
 
@@ -93,6 +93,7 @@ fn do_release(deps: DepsMut, env: Env, info: MessageInfo) -> Result<Response, Ha
         let mut resp = Response::new();
         resp.add_attribute("action", "release");
         resp.add_attribute("destination", to_addr.clone());
+        resp.add_event(Event::new("hackatom").attr("action", "release"));
         resp.add_message(BankMsg::Send {
             to_address: to_addr.into(),
             amount: balance,
diff --git a/contracts/ibc-reflect/schema/acknowledgement_msg_balances.json b/contracts/ibc-reflect/schema/acknowledgement_msg_balances.json
index 6373205d7..55be66b88 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<Response> = ContractResult::Ok(response); assert_eq!(to_vec(&result).unwrap(), br#\"{\"ok\":{\"messages\":[],\"attributes\":[],\"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<Response> = 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<Response> = 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<Response> = ContractResult::Err(error_msg); assert_eq!(to_vec(&result).unwrap(), br#\"{\"error\":\"Something went wrong\"}\"#.to_vec()); ```",
   "anyOf": [
     {
       "type": "object",
diff --git a/contracts/ibc-reflect/schema/acknowledgement_msg_dispatch.json b/contracts/ibc-reflect/schema/acknowledgement_msg_dispatch.json
index 45ad1902d..8bbdc8a39 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<Response> = ContractResult::Ok(response); assert_eq!(to_vec(&result).unwrap(), br#\"{\"ok\":{\"messages\":[],\"attributes\":[],\"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<Response> = 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<Response> = 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<Response> = ContractResult::Err(error_msg); assert_eq!(to_vec(&result).unwrap(), br#\"{\"error\":\"Something went wrong\"}\"#.to_vec()); ```",
   "anyOf": [
     {
       "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 01e4f720f..8ab6cd727 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<Response> = ContractResult::Ok(response); assert_eq!(to_vec(&result).unwrap(), br#\"{\"ok\":{\"messages\":[],\"attributes\":[],\"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<Response> = 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<Response> = 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<Response> = ContractResult::Err(error_msg); assert_eq!(to_vec(&result).unwrap(), br#\"{\"error\":\"Something went wrong\"}\"#.to_vec()); ```",
   "anyOf": [
     {
       "type": "object",
diff --git a/contracts/ibc-reflect/src/contract.rs b/contracts/ibc-reflect/src/contract.rs
index 100864fd3..834eda140 100644
--- a/contracts/ibc-reflect/src/contract.rs
+++ b/contracts/ibc-reflect/src/contract.rs
@@ -1,8 +1,8 @@
 use cosmwasm_std::{
     attr, entry_point, from_slice, to_binary, wasm_execute, BankMsg, Binary, ContractResult,
     CosmosMsg, Deps, DepsMut, Empty, Env, Event, IbcAcknowledgement, IbcBasicResponse, IbcChannel,
-    IbcOrder, IbcPacket, IbcReceiveResponse, MessageInfo, Order, QueryResponse, Reply,
-    Response, StdError, StdResult, SubMsg, SubcallResponse, WasmMsg,
+    IbcOrder, IbcPacket, IbcReceiveResponse, MessageInfo, Order, QueryResponse, Reply, Response,
+    StdError, StdResult, SubMsg, SubcallResponse, WasmMsg,
 };
 
 use crate::msg::{
diff --git a/contracts/reflect/schema/response_for__custom_msg.json b/contracts/reflect/schema/response_for__custom_msg.json
index 78164f89f..5ed40b1c9 100644
--- a/contracts/reflect/schema/response_for__custom_msg.json
+++ b/contracts/reflect/schema/response_for__custom_msg.json
@@ -1,10 +1,11 @@
 {
   "$schema": "http://json-schema.org/draft-07/schema#",
   "title": "Response_for_CustomMsg",
-  "description": "A response of a contract entry point, such as `instantiate`, `execute` or `migrate`.\n\nThis type can be constructed directly at the end of the call. Alternatively a mutable response instance can be created early in the contract's logic and incrementally be updated.\n\n## Examples\n\nDirect:\n\n``` # use cosmwasm_std::{Binary, DepsMut, Env, MessageInfo}; # type InstantiateMsg = (); # use cosmwasm_std::{attr, Response, StdResult};\n\npub fn instantiate( deps: DepsMut, _env: Env, _info: MessageInfo, msg: InstantiateMsg, ) -> StdResult<Response> { // ...\n\nOk(Response { messages: vec![], attributes: vec![attr(\"action\", \"instantiate\")], data: None, }) } ```\n\nMutating:\n\n``` # use cosmwasm_std::{coins, BankMsg, Binary, DepsMut, Env, MessageInfo, SubMsg}; # type InstantiateMsg = (); # type MyError = (); # use cosmwasm_std::Response;\n\npub fn instantiate( deps: DepsMut, _env: Env, info: MessageInfo, msg: InstantiateMsg, ) -> Result<Response, MyError> { let mut response = Response::new(); // ... response.add_attribute(\"Let the\", \"hacking begin\"); // ... response.add_message(BankMsg::Send { to_address: String::from(\"recipient\"), amount: coins(128, \"uint\"), }); response.add_attribute(\"foo\", \"bar\"); // ... response.set_data(Binary::from(b\"the result data\")); Ok(response) } ```",
+  "description": "A response of a contract entry point, such as `instantiate`, `execute` or `migrate`.\n\nThis type can be constructed directly at the end of the call. Alternatively a mutable response instance can be created early in the contract's logic and incrementally be updated.\n\n## Examples\n\nDirect:\n\n``` # use cosmwasm_std::{Binary, DepsMut, Env, MessageInfo}; # type InstantiateMsg = (); # use cosmwasm_std::{attr, Response, StdResult};\n\npub fn instantiate( deps: DepsMut, _env: Env, _info: MessageInfo, msg: InstantiateMsg, ) -> StdResult<Response> { // ...\n\nOk(Response { messages: vec![], attributes: vec![attr(\"action\", \"instantiate\")], events: vec![], data: None, }) } ```\n\nMutating:\n\n``` # use cosmwasm_std::{coins, BankMsg, Binary, DepsMut, Env, MessageInfo, SubMsg}; # type InstantiateMsg = (); # type MyError = (); # use cosmwasm_std::Response;\n\npub fn instantiate( deps: DepsMut, _env: Env, info: MessageInfo, msg: InstantiateMsg, ) -> Result<Response, MyError> { let mut response = Response::new(); // ... response.add_attribute(\"Let the\", \"hacking begin\"); // ... response.add_message(BankMsg::Send { to_address: String::from(\"recipient\"), amount: coins(128, \"uint\"), }); response.add_attribute(\"foo\", \"bar\"); // ... response.set_data(Binary::from(b\"the result data\")); Ok(response) } ```",
   "type": "object",
   "required": [
     "attributes",
+    "events",
     "messages"
   ],
   "properties": {
@@ -25,6 +26,12 @@
         }
       ]
     },
+    "events": {
+      "type": "array",
+      "items": {
+        "$ref": "#/definitions/Event"
+      }
+    },
     "messages": {
       "description": "Optional list of messages to pass. These will be executed in order. If the ReplyOn variant matches the result (Always, Success on Ok, Error on Err), the runtime will invoke this contract's `reply` entry point after execution. Otherwise, they act like \"fire and forget\". Use `SubMsg::new` to create messages with the older \"fire and forget\" semantics.",
       "type": "array",
@@ -305,6 +312,26 @@
         }
       ]
     },
+    "Event": {
+      "description": "A full Cosmos SDK event as documented in https://docs.cosmos.network/v0.42/core/events.html.\n\nThis version uses string attributes (similar to https://github.com/cosmos/cosmos-sdk/blob/v0.42.5/proto/cosmos/base/abci/v1beta1/abci.proto#L56-L70), which then get magically converted to bytes for Tendermint somewhere between the Rust-Go interface, JSON deserialization and the `NewEvent` call in Cosmos SDK.",
+      "type": "object",
+      "required": [
+        "attributes",
+        "type"
+      ],
+      "properties": {
+        "attributes": {
+          "type": "array",
+          "items": {
+            "$ref": "#/definitions/Attribute"
+          }
+        },
+        "type": {
+          "description": "The event type. This is renamed to \"kind\" because \"type\" is reserved in Rust. This sucks, we know.",
+          "type": "string"
+        }
+      }
+    },
     "IbcMsg": {
       "description": "These are messages in the IBC lifecycle. Only usable by IBC-enabled contracts (contracts that directly speak the IBC protocol via 6 entry points)",
       "anyOf": [
diff --git a/contracts/reflect/src/contract.rs b/contracts/reflect/src/contract.rs
index 186a01b51..2ab0f14f7 100644
--- a/contracts/reflect/src/contract.rs
+++ b/contracts/reflect/src/contract.rs
@@ -57,6 +57,7 @@ pub fn try_reflect(
     let res = Response {
         messages,
         attributes: vec![attr("action", "reflect")],
+        events: vec![],
         data: None,
     };
     Ok(res)
@@ -82,6 +83,7 @@ pub fn try_reflect_subcall(
     let res = Response {
         messages: msgs,
         attributes: vec![attr("action", "reflect_subcall")],
+        events: vec![],
         data: None,
     };
     Ok(res)
@@ -434,7 +436,7 @@ mod tests {
 
         let id = 123u64;
         let data = Binary::from(b"foobar");
-        let events = vec![Event::new("message", vec![attr("signer", "caller-addr")])];
+        let events = vec![Event::new("message").attr("signer", "caller-addr")];
         let result = ContractResult::Ok(SubcallResponse {
             events: events.clone(),
             data: Some(data.clone()),
diff --git a/packages/std/src/results/attribute.rs b/packages/std/src/results/attribute.rs
index d0f343e5a..67b41a3b2 100644
--- a/packages/std/src/results/attribute.rs
+++ b/packages/std/src/results/attribute.rs
@@ -9,7 +9,7 @@ pub struct Attribute {
 }
 
 /// Creates a new Attribute.
-pub fn attr<K: ToString, V: ToString>(key: K, value: V) -> Attribute {
+pub fn attr(key: impl ToString, value: impl ToString) -> Attribute {
     Attribute {
         key: key.to_string(),
         value: value.to_string(),
diff --git a/packages/std/src/results/contract_result.rs b/packages/std/src/results/contract_result.rs
index ef0cfdf20..afaaed2ad 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<Response> = ContractResult::Ok(response);
-/// assert_eq!(to_vec(&result).unwrap(), br#"{"ok":{"messages":[],"attributes":[],"data":null}}"#.to_vec());
+/// assert_eq!(to_vec(&result).unwrap(), br#"{"ok":{"messages":[],"attributes":[],"events":[],"data":null}}"#.to_vec());
 /// ```
 ///
 /// Failure:
@@ -101,7 +101,7 @@ mod tests {
         let result: ContractResult<Response> = ContractResult::Ok(Response::default());
         assert_eq!(
             to_vec(&result).unwrap(),
-            br#"{"ok":{"messages":[],"attributes":[],"data":null}}"#.to_vec()
+            br#"{"ok":{"messages":[],"attributes":[],"events":[],"data":null}}"#.to_vec()
         );
 
         let result: ContractResult<Response> = ContractResult::Err("broken".to_string());
@@ -117,7 +117,8 @@ mod tests {
         assert_eq!(result, ContractResult::Ok("foo".to_string()));
 
         let result: ContractResult<Response> =
-            from_slice(br#"{"ok":{"messages":[],"attributes":[],"data":null}}"#).unwrap();
+            from_slice(br#"{"ok":{"messages":[],"attributes":[],"events":[],"data":null}}"#)
+                .unwrap();
         assert_eq!(result, ContractResult::Ok(Response::default()));
 
         let result: ContractResult<Response> = from_slice(br#"{"error":"broken"}"#).unwrap();
diff --git a/packages/std/src/results/response.rs b/packages/std/src/results/response.rs
index 3abeed7f2..64272b27e 100644
--- a/packages/std/src/results/response.rs
+++ b/packages/std/src/results/response.rs
@@ -2,7 +2,7 @@ use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
 use std::fmt;
 
-use crate::Binary;
+use crate::{Binary, Event};
 
 use super::{Attribute, CosmosMsg, Empty, SubMsg};
 
@@ -33,6 +33,7 @@ use super::{Attribute, CosmosMsg, Empty, SubMsg};
 ///     Ok(Response {
 ///         messages: vec![],
 ///         attributes: vec![attr("action", "instantiate")],
+///         events: vec![],
 ///         data: None,
 ///     })
 /// }
@@ -80,6 +81,7 @@ where
     pub messages: Vec<SubMsg<T>>,
     /// The attributes that will be emitted as part of a "wasm" event
     pub attributes: Vec<Attribute>,
+    pub events: Vec<Event>,
     pub data: Option<Binary>,
 }
 
@@ -91,6 +93,7 @@ where
         Response {
             messages: vec![],
             attributes: vec![],
+            events: vec![],
             data: None,
         }
     }
@@ -104,6 +107,7 @@ where
         Self::default()
     }
 
+    /// Add an attribute included in the main `wasm` event.
     pub fn add_attribute<K: Into<String>, V: Into<String>>(&mut self, key: K, value: V) {
         self.attributes.push(Attribute {
             key: key.into(),
@@ -123,6 +127,15 @@ where
         self.messages.push(msg);
     }
 
+    /// Adds an extra event to the response, separate from the main `wasm` event
+    /// that is always created.
+    ///
+    /// The `wasm-` prefix will be appended by the runtime to the provided type
+    /// of event.
+    pub fn add_event(&mut self, event: Event) {
+        self.events.push(event);
+    }
+
     pub fn set_data<U: Into<Binary>>(&mut self, data: U) {
         self.data = Some(data.into());
     }
@@ -164,6 +177,7 @@ mod tests {
                 key: "action".to_string(),
                 value: "release".to_string(),
             }],
+            events: vec![],
             data: Some(Binary::from([0xAA, 0xBB])),
         };
         let serialized = to_vec(&original).expect("encode contract result");
diff --git a/packages/std/src/results/subcall.rs b/packages/std/src/results/subcall.rs
index 549e3f54c..df073e011 100644
--- a/packages/std/src/results/subcall.rs
+++ b/packages/std/src/results/subcall.rs
@@ -133,10 +133,37 @@ pub struct Event {
 }
 
 impl Event {
-    pub fn new(kind: &str, attributes: Vec<Attribute>) -> Self {
+    /// Create a new event with the given type and an empty list of attributes.
+    pub fn new(kind: impl Into<String>) -> Self {
         Event {
-            kind: kind.to_string(),
-            attributes,
+            kind: kind.into(),
+            attributes: Vec::with_capacity(10),
         }
     }
+
+    /// Add an attribute to the event.
+    pub fn attr(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
+        self.attributes.push(Attribute {
+            key: key.into(),
+            value: value.into(),
+        });
+        self
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::attr;
+
+    #[test]
+    fn event_construction() {
+        let event_direct = Event {
+            kind: "test".to_string(),
+            attributes: vec![attr("foo", "bar"), attr("bar", "baz")],
+        };
+        let event_builder = Event::new("test").attr("foo", "bar").attr("bar", "baz");
+
+        assert_eq!(event_direct, event_builder);
+    }
 }
diff --git a/packages/vm/src/instance.rs b/packages/vm/src/instance.rs
index b5530be71..e2e7e3539 100644
--- a/packages/vm/src/instance.rs
+++ b/packages/vm/src/instance.rs
@@ -606,7 +606,7 @@ mod tests {
 
         let report2 = instance.create_gas_report();
         assert_eq!(report2.used_externally, 73);
-        assert_eq!(report2.used_internally, 38550);
+        assert_eq!(report2.used_internally, 38880);
         assert_eq!(report2.limit, LIMIT);
         assert_eq!(
             report2.remaining,
@@ -805,7 +805,7 @@ mod singlepass_tests {
             .unwrap();
 
         let init_used = orig_gas - instance.get_gas_left();
-        assert_eq!(init_used, 38623);
+        assert_eq!(init_used, 38953);
     }
 
     #[test]
@@ -828,7 +828,7 @@ mod singlepass_tests {
             .unwrap();
 
         let execute_used = gas_before_execute - instance.get_gas_left();
-        assert_eq!(execute_used, 159004);
+        assert_eq!(execute_used, 159317);
     }
 
     #[test]


Test patch:
diff --git a/contracts/reflect/tests/integration.rs b/contracts/reflect/tests/integration.rs
index f2f8f940b..2f4005f59 100644
--- a/contracts/reflect/tests/integration.rs
+++ b/contracts/reflect/tests/integration.rs
@@ -18,7 +18,7 @@
 //! 4. Anywhere you see query(&deps, ...) you must replace it with query(&mut deps, ...)
 
 use cosmwasm_std::{
-    attr, coin, coins, from_binary, BankMsg, Binary, Coin, ContractResult, Event, Reply, Response,
+    coin, coins, from_binary, BankMsg, Binary, Coin, ContractResult, Event, Reply, Response,
     StakingMsg, SubMsg, SubcallResponse, SystemResult,
 };
 use cosmwasm_vm::{
@@ -225,7 +225,7 @@ fn reply_and_query() {
 
     let id = 123u64;
     let data = Binary::from(b"foobar");
-    let events = vec![Event::new("message", vec![attr("signer", "caller-addr")])];
+    let events = vec![Event::new("message").attr("signer", "caller-addr")];
     let result = ContractResult::Ok(SubcallResponse {
         events: events.clone(),
         data: Some(data.clone()),