use derivative::Derivative;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::fmt;
use crate::binary::Binary;
use crate::coins::Coin;
use crate::errors::StdResult;
#[cfg(feature = "stargate")]
use crate::ibc::IbcMsg;
use crate::serde::to_binary;
use super::Empty;
pub trait CustomMsg: Serialize + Clone + fmt::Debug + PartialEq + JsonSchema {}
impl CustomMsg for Empty {}
#[non_exhaustive]
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum CosmosMsg<T = Empty> {
Bank(BankMsg),
Custom(T),
#[cfg(feature = "staking")]
Staking(StakingMsg),
#[cfg(feature = "staking")]
Distribution(DistributionMsg),
#[cfg(feature = "stargate")]
Stargate {
type_url: String,
value: Binary,
},
#[cfg(feature = "stargate")]
Ibc(IbcMsg),
Wasm(WasmMsg),
#[cfg(feature = "stargate")]
Gov(GovMsg),
}
#[non_exhaustive]
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum BankMsg {
Send {
to_address: String,
amount: Vec<Coin>,
},
Burn { amount: Vec<Coin> },
}
#[cfg(feature = "staking")]
#[non_exhaustive]
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum StakingMsg {
Delegate { validator: String, amount: Coin },
Undelegate { validator: String, amount: Coin },
Redelegate {
src_validator: String,
dst_validator: String,
amount: Coin,
},
}
#[cfg(feature = "staking")]
#[non_exhaustive]
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum DistributionMsg {
SetWithdrawAddress {
address: String,
},
WithdrawDelegatorReward {
validator: String,
},
}
fn binary_to_string(data: &Binary, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
match std::str::from_utf8(data.as_slice()) {
Ok(s) => fmt.write_str(s),
Err(_) => write!(fmt, "{:?}", data),
}
}
#[non_exhaustive]
#[derive(Serialize, Deserialize, Clone, Derivative, PartialEq, Eq, JsonSchema)]
#[derivative(Debug)]
#[serde(rename_all = "snake_case")]
pub enum WasmMsg {
Execute {
contract_addr: String,
#[derivative(Debug(format_with = "binary_to_string"))]
msg: Binary,
funds: Vec<Coin>,
},
Instantiate {
admin: Option<String>,
code_id: u64,
#[derivative(Debug(format_with = "binary_to_string"))]
msg: Binary,
funds: Vec<Coin>,
label: String,
},
Migrate {
contract_addr: String,
new_code_id: u64,
#[derivative(Debug(format_with = "binary_to_string"))]
msg: Binary,
},
UpdateAdmin {
contract_addr: String,
admin: String,
},
ClearAdmin { contract_addr: String },
}
#[cfg(feature = "stargate")]
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum GovMsg {
Vote { proposal_id: u64, vote: VoteOption },
}
#[cfg(feature = "stargate")]
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum VoteOption {
Yes,
No,
Abstain,
NoWithVeto,
}
pub fn wasm_instantiate(
code_id: u64,
msg: &impl Serialize,
funds: Vec<Coin>,
label: String,
) -> StdResult<WasmMsg> {
let payload = to_binary(msg)?;
Ok(WasmMsg::Instantiate {
admin: None,
code_id,
msg: payload,
funds,
label,
})
}
pub fn wasm_execute(
contract_addr: impl Into<String>,
msg: &impl Serialize,
funds: Vec<Coin>,
) -> StdResult<WasmMsg> {
let payload = to_binary(msg)?;
Ok(WasmMsg::Execute {
contract_addr: contract_addr.into(),
msg: payload,
funds,
})
}
impl<T> From<BankMsg> for CosmosMsg<T> {
fn from(msg: BankMsg) -> Self {
CosmosMsg::Bank(msg)
}
}
#[cfg(feature = "staking")]
impl<T> From<StakingMsg> for CosmosMsg<T> {
fn from(msg: StakingMsg) -> Self {
CosmosMsg::Staking(msg)
}
}
#[cfg(feature = "staking")]
impl<T> From<DistributionMsg> for CosmosMsg<T> {
fn from(msg: DistributionMsg) -> Self {
CosmosMsg::Distribution(msg)
}
}
impl<T> From<WasmMsg> for CosmosMsg<T> {
fn from(msg: WasmMsg) -> Self {
CosmosMsg::Wasm(msg)
}
}
#[cfg(feature = "stargate")]
impl<T> From<IbcMsg> for CosmosMsg<T> {
fn from(msg: IbcMsg) -> Self {
CosmosMsg::Ibc(msg)
}
}
#[cfg(feature = "stargate")]
impl<T> From<GovMsg> for CosmosMsg<T> {
fn from(msg: GovMsg) -> Self {
CosmosMsg::Gov(msg)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{coin, coins};
#[test]
fn from_bank_msg_works() {
let to_address = String::from("you");
let amount = coins(1015, "earth");
let bank = BankMsg::Send { to_address, amount };
let msg: CosmosMsg = bank.clone().into();
match msg {
CosmosMsg::Bank(msg) => assert_eq!(bank, msg),
_ => panic!("must encode in Bank variant"),
}
}
#[cosmwasm_schema::cw_serde]
enum ExecuteMsg {
Mint { coin: Coin },
}
#[test]
fn wasm_msg_debug_decodes_binary_string_when_possible() {
let msg = WasmMsg::Execute {
contract_addr: "joe".to_string(),
msg: to_binary(&ExecuteMsg::Mint {
coin: coin(10, "BTC"),
})
.unwrap(),
funds: vec![],
};
assert_eq!(
format!("{:?}", msg),
"Execute { contract_addr: \"joe\", msg: {\"mint\":{\"coin\":{\"denom\":\"BTC\",\"amount\":\"10\"}}}, funds: [] }"
);
}
#[test]
fn wasm_msg_debug_dumps_binary_when_not_utf8() {
let msg = WasmMsg::Execute {
contract_addr: "joe".to_string(),
msg: Binary::from([0, 159, 146, 150]),
funds: vec![],
};
assert_eq!(
format!("{:?}", msg),
"Execute { contract_addr: \"joe\", msg: Binary(009f9296), funds: [] }"
);
}
}