Interoperability

Interoperability

💡
Sylvia contracts are fully interoperable with classical CosmWasm contracts.

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.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.

💡
Learn more about replies here.
const SUBMSG_ID: u64 = 1;
 
pub struct ReplyContract {
    remote: Item<Remote<'static, Contract>>,
}
 
#[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()
            .contract_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, &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;
 
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.