use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::fmt;
use crate::coins::Coin;
use crate::encoding::Binary;
use crate::errors::StdResult;
use crate::types::{HumanAddr, Never};
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum CosmosMsg<T = Never>
where
T: Clone + fmt::Debug + PartialEq + JsonSchema,
{
Bank(BankMsg),
Custom(T),
Staking(StakingMsg),
Wasm(WasmMsg),
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum BankMsg {
Send {
from_address: HumanAddr,
to_address: HumanAddr,
amount: Vec<Coin>,
},
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum StakingMsg {
Delegate {
validator: HumanAddr,
amount: Coin,
},
Undelegate {
validator: HumanAddr,
amount: Coin,
},
Withdraw {
validator: HumanAddr,
recipient: Option<HumanAddr>,
},
Redelegate {
src_validator: HumanAddr,
dst_validator: HumanAddr,
amount: Coin,
},
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum WasmMsg {
Execute {
contract_addr: HumanAddr,
msg: Binary,
send: Vec<Coin>,
},
Instantiate {
code_id: u64,
msg: Binary,
send: Vec<Coin>,
label: Option<String>,
},
}
impl<T: Clone + fmt::Debug + PartialEq + JsonSchema> From<BankMsg> for CosmosMsg<T> {
fn from(msg: BankMsg) -> Self {
CosmosMsg::Bank(msg)
}
}
#[cfg(feature = "staking")]
impl<T: Clone + fmt::Debug + PartialEq + JsonSchema> From<StakingMsg> for CosmosMsg<T> {
fn from(msg: StakingMsg) -> Self {
CosmosMsg::Staking(msg)
}
}
impl<T: Clone + fmt::Debug + PartialEq + JsonSchema> From<WasmMsg> for CosmosMsg<T> {
fn from(msg: WasmMsg) -> Self {
CosmosMsg::Wasm(msg)
}
}
#[derive(Serialize, Deserialize, Clone, Default, Debug, PartialEq, JsonSchema)]
pub struct LogAttribute {
pub key: String,
pub value: String,
}
pub fn log(key: &str, value: &str) -> LogAttribute {
LogAttribute {
key: key.to_string(),
value: value.to_string(),
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct InitResponse<T = Never>
where
T: Clone + fmt::Debug + PartialEq + JsonSchema,
{
pub messages: Vec<CosmosMsg<T>>,
pub log: Vec<LogAttribute>,
pub data: Option<Binary>,
}
pub type InitResult<U = Never> = StdResult<InitResponse<U>>;
impl<T> Default for InitResponse<T>
where
T: Clone + fmt::Debug + PartialEq + JsonSchema,
{
fn default() -> Self {
InitResponse {
messages: vec![],
log: vec![],
data: None,
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct HandleResponse<T = Never>
where
T: Clone + fmt::Debug + PartialEq + JsonSchema,
{
pub messages: Vec<CosmosMsg<T>>,
pub log: Vec<LogAttribute>,
pub data: Option<Binary>,
}
pub type HandleResult<U = Never> = StdResult<HandleResponse<U>>;
impl<T> Default for HandleResponse<T>
where
T: Clone + fmt::Debug + PartialEq + JsonSchema,
{
fn default() -> Self {
HandleResponse {
messages: vec![],
log: vec![],
data: None,
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::errors::StdError;
use crate::{coins, from_slice, to_vec};
#[test]
fn can_deser_error_result() {
let fail = InitResult::Err(StdError::Unauthorized { backtrace: None });
let bin = to_vec(&fail).expect("encode contract result");
println!("error: {}", std::str::from_utf8(&bin).unwrap());
let back: InitResult = from_slice(&bin).expect("decode contract result");
assert_eq!(fail, back);
}
#[test]
fn can_deser_ok_result() {
let send = InitResult::Ok(InitResponse {
messages: vec![BankMsg::Send {
from_address: HumanAddr("me".to_string()),
to_address: HumanAddr("you".to_string()),
amount: coins(1015, "earth"),
}
.into()],
log: vec![LogAttribute {
key: "action".to_string(),
value: "release".to_string(),
}],
data: None,
});
let bin = to_vec(&send).expect("encode contract result");
println!("ok: {}", std::str::from_utf8(&bin).unwrap());
let back: InitResult = from_slice(&bin).expect("decode contract result");
assert_eq!(send, back);
}
#[test]
fn msg_from_works() {
let from_address = HumanAddr("me".to_string());
let to_address = HumanAddr("you".to_string());
let amount = coins(1015, "earth");
let bank = BankMsg::Send {
from_address,
to_address,
amount,
};
let msg: CosmosMsg = bank.clone().into();
match msg {
CosmosMsg::Bank(msg) => assert_eq!(bank, msg),
_ => panic!("must encode in Bank variant"),
}
}
}