secret_cosmwasm_std/results/cosmos_msg.rs
1use derivative::Derivative;
2use schemars::JsonSchema;
3use serde::{Deserialize, Serialize};
4use std::fmt;
5
6use crate::binary::Binary;
7use crate::coin::Coin;
8use crate::errors::StdResult;
9#[cfg(feature = "stargate")]
10use crate::ibc::IbcMsg;
11use crate::serde::to_binary;
12
13use super::Empty;
14
15/// Like CustomQuery for better type clarity.
16/// Also makes it shorter to use as a trait bound.
17pub trait CustomMsg: Serialize + Clone + fmt::Debug + PartialEq + JsonSchema {}
18
19impl CustomMsg for Empty {}
20
21#[non_exhaustive]
22#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
23#[serde(rename_all = "snake_case")]
24// See https://github.com/serde-rs/serde/issues/1296 why we cannot add De-Serialize trait bounds to T
25pub enum CosmosMsg<T = Empty> {
26 Bank(BankMsg),
27 // by default we use RawMsg, but a contract can override that
28 // to call into more app-specific code (whatever they define)
29 Custom(T),
30 #[cfg(feature = "staking")]
31 Staking(StakingMsg),
32 #[cfg(feature = "staking")]
33 Distribution(DistributionMsg),
34 /// A Stargate message encoded the same way as a protobuf [Any](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto).
35 /// This is the same structure as messages in `TxBody` from [ADR-020](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-020-protobuf-transaction-encoding.md)
36 #[cfg(feature = "stargate")]
37 Stargate {
38 /// this is the fully qualified msg path used for routing,
39 /// e.g. /cosmos.bank.v1beta1.MsgSend
40 /// NOTE: the type_url can be changed after a chain upgrade
41 type_url: String,
42 value: Binary,
43 },
44 #[cfg(feature = "stargate")]
45 Ibc(IbcMsg),
46 Wasm(WasmMsg),
47 #[cfg(feature = "stargate")]
48 Gov(GovMsg),
49 FinalizeTx(Empty),
50}
51
52impl<T> CosmosMsg<T> {
53 pub fn finalize_tx() -> Self {
54 CosmosMsg::FinalizeTx(Empty {})
55 }
56}
57
58/// The message types of the bank module.
59///
60/// See https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/bank/v1beta1/tx.proto
61#[non_exhaustive]
62#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
63#[serde(rename_all = "snake_case")]
64pub enum BankMsg {
65 /// Sends native tokens from the contract to the given address.
66 ///
67 /// This is translated to a [MsgSend](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/bank/v1beta1/tx.proto#L19-L28).
68 /// `from_address` is automatically filled with the current contract's address.
69 Send {
70 to_address: String,
71 amount: Vec<Coin>,
72 },
73 /// This will burn the given coins from the contract's account.
74 /// There is no Cosmos SDK message that performs this, but it can be done by calling the bank keeper.
75 /// Important if a contract controls significant token supply that must be retired.
76 Burn { amount: Vec<Coin> },
77}
78
79/// The message types of the staking module.
80///
81/// See https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto
82#[cfg(feature = "staking")]
83#[non_exhaustive]
84#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
85#[serde(rename_all = "snake_case")]
86pub enum StakingMsg {
87 /// This is translated to a [MsgDelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L81-L90).
88 /// `delegator_address` is automatically filled with the current contract's address.
89 Delegate { validator: String, amount: Coin },
90 /// This is translated to a [MsgUndelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L112-L121).
91 /// `delegator_address` is automatically filled with the current contract's address.
92 Undelegate { validator: String, amount: Coin },
93 /// This is translated to a [MsgBeginRedelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L95-L105).
94 /// `delegator_address` is automatically filled with the current contract's address.
95 Redelegate {
96 src_validator: String,
97 dst_validator: String,
98 amount: Coin,
99 },
100}
101
102/// The message types of the distribution module.
103///
104/// See https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto
105#[cfg(feature = "staking")]
106#[non_exhaustive]
107#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
108#[serde(rename_all = "snake_case")]
109pub enum DistributionMsg {
110 /// This is translated to a [MsgSetWithdrawAddress](https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#L29-L37).
111 /// `delegator_address` is automatically filled with the current contract's address.
112 SetWithdrawAddress {
113 /// The `withdraw_address`
114 address: String,
115 },
116 /// This is translated to a [[MsgWithdrawDelegatorReward](https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#L42-L50).
117 /// `delegator_address` is automatically filled with the current contract's address.
118 WithdrawDelegatorReward {
119 /// The `validator_address`
120 validator: String,
121 },
122}
123
124fn binary_to_string(data: &Binary, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
125 match std::str::from_utf8(data.as_slice()) {
126 Ok(s) => fmt.write_str(s),
127 Err(_) => write!(fmt, "{:?}", data),
128 }
129}
130
131/// The message types of the wasm module.
132///
133/// See https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto
134#[non_exhaustive]
135#[derive(Serialize, Deserialize, Clone, Derivative, PartialEq, Eq, JsonSchema)]
136#[derivative(Debug)]
137#[serde(rename_all = "snake_case")]
138pub enum WasmMsg {
139 /// Dispatches a call to another contract at a known address (with known ABI).
140 ///
141 /// This is translated to a [MsgExecuteContract](https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto#L68-L78).
142 /// `sender` is automatically filled with the current contract's address.
143 Execute {
144 contract_addr: String,
145 /// code_hash is the hex encoded hash of the code. This is used by Secret Network to harden against replaying the contract
146 /// It is used to bind the request to a destination contract in a stronger way than just the contract address which can be faked
147 code_hash: String,
148 /// msg is the json-encoded ExecuteMsg struct (as raw Binary)
149 #[derivative(Debug(format_with = "binary_to_string"))]
150 msg: Binary,
151 #[serde(rename = "send")]
152 funds: Vec<Coin>,
153 },
154 /// Instantiates a new contracts from previously uploaded Wasm code.
155 ///
156 /// This is translated to a [MsgInstantiateContract](https://github.com/CosmWasm/wasmd/blob/v0.16.0-alpha1/x/wasm/internal/types/tx.proto#L47-L61).
157 /// `sender` is automatically filled with the current contract's address.
158 Instantiate {
159 admin: Option<String>,
160 code_id: u64,
161 /// code_hash is the hex encoded hash of the code. This is used by Secret Network to harden against replaying the contract
162 /// It is used to bind the request to a destination contract in a stronger way than just the contract address which can be faked
163 code_hash: String,
164 /// msg is the JSON-encoded InstantiateMsg struct (as raw Binary)
165 #[derivative(Debug(format_with = "binary_to_string"))]
166 msg: Binary,
167 #[serde(rename = "send")]
168 funds: Vec<Coin>,
169 /// A human-readbale label for the contract, must be unique across all contracts
170 label: String,
171 },
172 /// Migrates a given contracts to use new wasm code. Passes a MigrateMsg to allow us to
173 /// customize behavior.
174 ///
175 /// Only the contract admin (as defined in wasmd), if any, is able to make this call.
176 ///
177 /// This is translated to a [MsgMigrateContract](https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto#L86-L96).
178 /// `sender` is automatically filled with the current contract's address.
179 Migrate {
180 contract_addr: String,
181 /// code_hash is the hex encoded hash of the **new** code. This is used by Secret Network to harden against replaying the contract
182 /// It is used to bind the request to a destination contract in a stronger way than just the contract address which can be faked
183 code_hash: String,
184 /// the code_id of the **new** logic to place in the given contract
185 code_id: u64,
186 /// msg is the json-encoded MigrateMsg struct that will be passed to the new code
187 #[derivative(Debug(format_with = "binary_to_string"))]
188 msg: Binary,
189 },
190 /// Sets a new admin (for migrate) on the given contract.
191 /// Fails if this contract is not currently admin of the target contract.
192 UpdateAdmin {
193 contract_addr: String,
194 admin: String,
195 },
196 /// Clears the admin on the given contract, so no more migration possible.
197 /// Fails if this contract is not currently admin of the target contract.
198 ClearAdmin { contract_addr: String },
199}
200
201#[cfg(feature = "stargate")]
202#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
203#[serde(rename_all = "snake_case")]
204pub enum GovMsg {
205 /// This maps directly to [MsgVote](https://github.com/cosmos/cosmos-sdk/blob/v0.42.5/proto/cosmos/gov/v1beta1/tx.proto#L46-L56) in the Cosmos SDK with voter set to the contract address.
206 Vote { proposal_id: u64, vote: VoteOption },
207}
208
209#[cfg(feature = "stargate")]
210#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
211#[serde(rename_all = "snake_case")]
212pub enum VoteOption {
213 Yes,
214 No,
215 Abstain,
216 NoWithVeto,
217}
218
219
220/// Shortcut helper as the construction of WasmMsg::Instantiate can be quite verbose in contract code.
221///
222/// When using this, `admin` is always unset. If you need more flexibility, create the message directly.
223pub fn wasm_instantiate(
224 code_id: u64,
225 code_hash: impl Into<String>,
226 msg: &impl Serialize,
227 funds: Vec<Coin>,
228 label: String,
229) -> StdResult<WasmMsg> {
230 let payload = to_binary(msg)?;
231 Ok(WasmMsg::Instantiate {
232 admin: None,
233 code_id,
234 code_hash: code_hash.into(),
235 msg: payload,
236 funds,
237 label,
238 })
239}
240
241/// Shortcut helper as the construction of WasmMsg::Instantiate can be quite verbose in contract code
242pub fn wasm_execute(
243 contract_addr: impl Into<String>,
244 code_hash: impl Into<String>,
245 msg: &impl Serialize,
246 funds: Vec<Coin>,
247) -> StdResult<WasmMsg> {
248 let payload = to_binary(msg)?;
249 Ok(WasmMsg::Execute {
250 contract_addr: contract_addr.into(),
251 code_hash: code_hash.into(),
252 msg: payload,
253 funds,
254 })
255}
256
257impl<T> From<BankMsg> for CosmosMsg<T> {
258 fn from(msg: BankMsg) -> Self {
259 CosmosMsg::Bank(msg)
260 }
261}
262
263#[cfg(feature = "staking")]
264impl<T> From<StakingMsg> for CosmosMsg<T> {
265 fn from(msg: StakingMsg) -> Self {
266 CosmosMsg::Staking(msg)
267 }
268}
269
270#[cfg(feature = "staking")]
271impl<T> From<DistributionMsg> for CosmosMsg<T> {
272 fn from(msg: DistributionMsg) -> Self {
273 CosmosMsg::Distribution(msg)
274 }
275}
276
277impl<T> From<WasmMsg> for CosmosMsg<T> {
278 fn from(msg: WasmMsg) -> Self {
279 CosmosMsg::Wasm(msg)
280 }
281}
282
283#[cfg(feature = "stargate")]
284impl<T> From<IbcMsg> for CosmosMsg<T> {
285 fn from(msg: IbcMsg) -> Self {
286 CosmosMsg::Ibc(msg)
287 }
288}
289
290#[cfg(feature = "stargate")]
291impl<T> From<GovMsg> for CosmosMsg<T> {
292 fn from(msg: GovMsg) -> Self {
293 CosmosMsg::Gov(msg)
294 }
295}
296
297#[cfg(test)]
298mod tests {
299 use super::*;
300 use crate::{coin, coins};
301
302 #[test]
303 fn from_bank_msg_works() {
304 let to_address = String::from("you");
305 let amount = coins(1015, "earth");
306 let bank = BankMsg::Send { to_address, amount };
307 let msg: CosmosMsg = bank.clone().into();
308 match msg {
309 CosmosMsg::Bank(msg) => assert_eq!(bank, msg),
310 _ => panic!("must encode in Bank variant"),
311 }
312 }
313
314 #[cosmwasm_schema::cw_serde]
315 enum ExecuteMsg {
316 Mint { coin: Coin },
317 }
318
319 #[test]
320 fn wasm_msg_debug_decodes_binary_string_when_possible() {
321 let msg = WasmMsg::Execute {
322 contract_addr: "joe".to_string(),
323 code_hash: "aaaa".to_string(),
324 msg: to_binary(&ExecuteMsg::Mint {
325 coin: coin(10, "BTC"),
326 })
327 .unwrap(),
328 funds: vec![],
329 };
330
331 assert_eq!(
332 format!("{:?}", msg),
333 "Execute { contract_addr: \"joe\", code_hash: \"aaaa\", msg: {\"mint\":{\"coin\":{\"denom\":\"BTC\",\"amount\":\"10\"}}}, funds: [] }"
334 );
335 }
336
337 #[test]
338 fn wasm_msg_debug_dumps_binary_when_not_utf8() {
339 let msg = WasmMsg::Execute {
340 contract_addr: "joe".to_string(),
341 code_hash: "aaaa".to_string(),
342 msg: Binary::from([0, 159, 146, 150]),
343 funds: vec![],
344 };
345
346 assert_eq!(
347 format!("{:?}", msg),
348 "Execute { contract_addr: \"joe\", code_hash: \"aaaa\", msg: Binary(009f9296), funds: [] }"
349 );
350 }
351}