Generated message types
Sylvia macros generate CosmWasm messages from methods marked with sv::msg
attributes.
Messages are generated either as struct, for instantiate and migrate, or enum in case of the rest of the types.
Each message's implementation contains a dispatch
method. Calling the msg.dispatch(...)
method
on a struct message forwards the message's fields to the single method as arguments, i.e.
structure's fields become the method's arguments. For the enum messages, the respective method will
be called depending on the enum variant. This is described in the example below
Contract messages
The following code:
#[cw_serde]
pub struct SomeResponse;
pub struct Contract;
#[contract]
impl Contract {
pub const fn new() -> Self {
Self
}
#[sv::msg(instantiate)]
fn instantiate(&self, ctx: InstantiateCtx, mutable: bool) -> StdResult<Response> {
Ok(Response::new())
}
#[sv::msg(exec)]
fn some_exec(&self, ctx: ExecCtx, addr: String) -> StdResult<Response> {
Ok(Response::new())
}
#[sv::msg(query)]
fn some_query(&self, ctx: QueryCtx, addr: String) -> StdResult<SomeResponse> {
Ok(SomeResponse)
}
#[sv::msg(sudo)]
fn some_sudo(&self, ctx: SudoCtx, addr: String) -> StdResult<Response> {
Ok(Response::new())
}
#[sv::msg(migrate)]
fn some_migrate(&self, ctx: MigrateCtx, addr: String) -> StdResult<Response> {
Ok(Response::new())
}
}
generates the following messages for every respective entrypoint:
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(
sylvia::serde::Serialize,
sylvia::serde::Deserialize,
Clone,
Debug,
PartialEq,
sylvia::schemars::JsonSchema,
)]
#[serde(rename_all = "snake_case")]
pub struct InstantiateMsg {
pub mutable: bool,
}
impl InstantiateMsg {
pub fn new(mutable: bool) -> Self {
Self { mutable }
}
pub fn dispatch(
self,
contract: &CounterContract,
ctx: (
sylvia::cw_std::DepsMut<sylvia::cw_std::Empty>,
sylvia::cw_std::Env,
sylvia::cw_std::MessageInfo,
),
) -> StdResult<Response> {
let Self { mutable } = self;
contract
.instantiate(Into::into(ctx), mutable)
.map_err(Into::into)
}
}
Notice that the method parameters are used as fields of appropriate messages and their variants.
The contract
macro also generates wrapper messages for exec, query and sudo. Their
goal is to wrap respective messages, like ExecMsg
, from both the contract and interfaces
implemented on it, which are used as the main messages of the contract.
Use ContractExecMsg
/ContractQueryMsg
/ContractSudoMsg
in hand made entry points and in
write_api!
(opens in a new tab) macro.
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(sylvia::serde::Serialize, Clone, Debug, PartialEq, sylvia::schemars::JsonSchema)]
#[serde(rename_all = "snake_case", untagged)]
pub enum ContractExecMsg {
Interface(<interface::sv::Api as sylvia::types::InterfaceApi>::Exec),
CounterContract(ExecMsg),
}
impl ContractExecMsg {
pub fn dispatch(
self,
contract: &CounterContract,
ctx: (
sylvia::cw_std::DepsMut<sylvia::cw_std::Empty>,
sylvia::cw_std::Env,
sylvia::cw_std::MessageInfo,
),
) -> std::result::Result<
sylvia::cw_std::Response<sylvia::cw_std::Empty>,
sylvia::cw_std::StdError,
> {
const _: () = {
let msgs: [&[&str]; 2usize] =
[&interface::sv::execute_messages(), &execute_messages()];
sylvia::utils::assert_no_intersection(msgs);
};
match self {
ContractExecMsg::Interface(msg) => msg.dispatch(contract, Into::into(ctx)),
ContractExecMsg::CounterContract(msg) => msg.dispatch(contract, ctx),
}
}
}
We mark the wrapper message with the
serde(untagged)
(opens in a new tab) attribute to drop the
wrapping variant names so that serialization matches one done for the regular CosmWasm messages.
The interface variants use <interface::sv::Api as sylvia::types::InterfaceApi>::Sudo
, which is an
accessor meant to simplify the API while using generics in the contract/interface.
Lastly, in dispatch, there is a const block. Its goal is to provide compile time validation that none of the messages overlap between a contract and interfaces.
Interface messages
The generation done by interface
macro is much simpler, as Sylvia generates just
three types of messages.
We will use the below example interface to generate the messages:
#[interface]
pub trait Interface {
type Error: From<StdError>;
#[sv::msg(exec)]
fn interface_exec(&self, ctx: ExecCtx, addr: String) -> Result<Response, Self::Error>;
#[sv::msg(query)]
fn interface_query(&self, ctx: QueryCtx, addr: String)
-> Result<SomeResponse, Self::Error>;
#[sv::msg(sudo)]
fn interface_sudo(&self, ctx: SudoCtx, addr: String) -> Result<Response, Self::Error>;
}
And the three generated messages:
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(
sylvia::serde::Serialize,
sylvia::serde::Deserialize,
Clone,
Debug,
PartialEq,
sylvia::schemars::JsonSchema,
)]
#[serde(rename_all = "snake_case")]
pub enum InterfaceExecMsg {
InterfaceExec { addr: String },
}
pub type ExecMsg = InterfaceExecMsg;
impl InterfaceExecMsg {
pub fn dispatch<ContractT>(
self,
contract: &ContractT,
ctx: (
sylvia::cw_std::DepsMut<sylvia::cw_std::Empty>,
sylvia::cw_std::Env,
sylvia::cw_std::MessageInfo,
),
) -> std::result::Result<sylvia::cw_std::Response<sylvia::cw_std::Empty>, ContractT::Error>
where
ContractT: Interface,
{
use InterfaceExecMsg::*;
match self {
InterfaceExec { addr: field1 } => contract
.interface_exec(Into::into(ctx), field1)
.map_err(Into::into),
}
}
pub fn interface_exec(addr: String) -> Self {
Self::InterfaceExec { addr }
}
}
Although interface messages are almost the same as contract ones, some slight differences exist.
Notice that the messages are prefixed with the interfaces name: InterfaceSudoMsg
, while in case of
the contract the name was just SudoMsg
. This prefix is required. Otherwise, ContractSudoMsg
variants names would overlap during the schema generation, and all but one would be lost. However, a
simplified alias is generated for them, so it's possible to access them like, e.g., SudoMsg
. The
same rule applies to the exec and query messages.
Since multiple contracts might use these messages, the dispatch
is generic over the ContractT
type implementing the interface. The error type returned in the dispatch
is the associated type
defined on the interface.