Interoperability
Sylvia macros expand into a regular CosmWasm code. Because of that, we can test and communicate with Sylvia contracts like we would with any CosmWasm contract.
Sylvia exposes, however additional QoL utilities like Remote
and
MultiTest
helpers, which we recommend using alongside the Sylvia contracts.
Communication
We can send messages from Sylvia as we would from any CosmWasm contract.
Execute messages in Sylvia return the
Response
(opens in a new tab) on which we can
call the
add_message
(opens in a new tab)
or
add_messages
(opens in a new tab).
#[sv::msg(exec)]
fn external_increment(&self, ctx: ExecCtx) -> StdResult<Response> {
let remote = self
.remote
.access(&CwStorage(ctx.deps.storage))
.get()?
.ok_or_else(|| StdError::generic_err("Remote not instantiated"))?;
let msg = WasmMsg::Execute {
contract_addr: remote.as_ref().to_string(),
msg: to_json_binary(&ExternalExecMsg::Increment {})?,
funds: vec![],
};
Ok(Response::new().add_message(msg))
}
We can also use the generated
WasmMsg
(opens in a new tab) to construct the
SubMsg
(opens in a new tab) and expect reply.
use external_contract::sv::Executor;
use cw_storey::containers::Item;
const SUBMSG_ID: u64 = 1;
pub struct ReplyContract {
remote: Item<Remote<'static, ExternalContract>>,
}
#[entry_points]
#[contract]
impl ReplyContract {
pub fn new() -> Self {
Self {
remote: Item::new(0),
}
}
#[sv::msg(instantiate)]
fn instantiate(&self, ctx: InstantiateCtx, remote_addr: Addr) -> StdResult<Response> {
self.remote
.access(&mut CwStorage(ctx.deps.storage))
.set(&Remote::new(remote_addr))?;
Ok(Response::new())
}
#[sv::msg(exec)]
fn exec(&self, ctx: ExecCtx) -> StdResult<Response> {
let msg = self
.remote
.access(&mut CwStorage(ctx.deps.storage))
.get()?
.ok_or_else(|| StdError::generic_err("Remote not instantiated"))?
.executor()
.external_exec()?
.build();
let sub_msg = SubMsg::reply_on_success(msg, SUBMSG_ID);
let resp = Response::new().add_submessage(sub_msg);
Ok(resp)
}
#[sv::msg(reply)]
fn reply(&self, ctx: ReplyCtx, reply: Reply) -> StdResult<Response> {
match reply.id {
SUBMSG_ID => {
// Your logic here
Ok(Response::new())
}
_ => Err(StdError::generic_err("Invalid reply id")),
}
}
}
Query messages can also be sent through the
query_wasm_smart
(opens in a new tab)
method. We can access the
Deps
(opens in a new tab) through the
QueryCtx
.
#[sv::msg(query)]
fn external_count(&self, ctx: QueryCtx) -> StdResult<ExternalResponse> {
let remote = self
.remote
.access(&CwStorage(ctx.deps.storage))
.get()?
.ok_or_else(|| StdError::generic_err("Remote not instantiated"))?;
ctx.deps
.querier
.query_wasm_smart(remote.as_ref().to_string(), &ExternalQueryMsg::Count {})
}
As you see, we can send messages from the Sylvia contract as we would in case of a CosmWasm contract. You can check generated messages here.
Although we could send messages to Sylvia contract in the same way, we recommend using the
ExecutorBuilder
and
BoundQuerier
which wraps construction of the messages.
You can learn more about these helpers in the communication
section.
Testing
We test Sylvia contract with MultiTest the same way we would test the classical CosmWasm contracts,
except we use the sylvia::App
in place of
cw_multi_test::App
(opens in a new tab). This
type provides all the API as the MultiTest counterpart, exposing the underlying object but adding
support for using the Sylvia-generated helpers. It can also be used to simulate the execution of
standard CosmWasm contracts as well
use sylvia::multitest::App;
use sylvia::cw_multi_test::BasicMtApp;
let app = App::<BasicMtApp<Empty, Empty>>::default();
We must provide the full type for the App
, as Rust cannot deduce it
here.
We can access the underlying
cw_multi_test::App
(opens in a new tab) via
app_mut
(opens in a new tab) to
store_code
(opens in a new tab)
of the CosmWasm contract.
fn cosmwasm_contract() -> Box<dyn Contract<Empty>> { ... }
let cosmwasm_code = app.app_mut().store_code(cosmwasm_contract());
To instantiate the CosmWasm contract, we will also use the
app_mut
(opens in a new tab).
let cosmwasm_contract = app
.app_mut()
.instantiate_contract(
cosmwasm_code,
owner.clone(),
&InstantiateMsg {},
&[],
"cosmwasm_contract",
None,
)
.unwrap();
After that testing will be the same as with any CosmWasm and Sylvia contract. Check out the
documentation about testing Sylvia's contract here
and about testing
CosmWasm contracts here
.