Contract structure

Contract structure

Sylvia contracts are designed using the actor model. An actor is a contract struct that can store a state and define a behavior.

use cw_storey::containers::Item;

pub struct CounterContract { pub count: Item<u64>, }

In Sylvia we keep the state accessors as part of the contract definition. The accessors are storey or cw_storage_plus primitives.

Let's take a look at the behavior implementation.

use cosmwasm_schema::cw_serde;
use cw_storey::CwStorage;
use sylvia::contract;
use sylvia::cw_std::{Response, StdError, StdResult};
use sylvia::types::{ExecCtx, InstantiateCtx, QueryCtx};

#[cw_serde]
pub struct CountResponse {
    pub count: u64,
}

#[cfg_attr(not(feature = "library"), sylvia::entry_points)]
#[contract]
impl CounterContract {
    pub const fn new() -> Self {
        Self {
            count: Item::new(0),
        }
    }

    #[sv::msg(instantiate)]
    fn instantiate(&self, ctx: InstantiateCtx) -> StdResult<Response> {
        self.count
            .access(&mut CwStorage(ctx.deps.storage))
            .set(&0)?;
        Ok(Response::new())
    }

    #[sv::msg(exec)]
    fn increment(&self, ctx: ExecCtx) -> StdResult<Response> {
        let mut storage = CwStorage(ctx.deps.storage);
        let mut accessor = self.count.access(&mut storage);
        let count = accessor
            .get()?
            .ok_or_else(|| StdError::generic_err("Count not instantiated yet"))?;
        accessor.set(&(count + 1))?;

        Ok(Response::new())
    }

    #[sv::msg(query)]
    fn count(&self, ctx: QueryCtx) -> StdResult<CountResponse> {
        let count = self
            .count
            .access(&CwStorage(ctx.deps.storage))
            .get()?
            .ok_or_else(|| StdError::generic_err("Count not instantiated yet"))?;
        Ok(CountResponse { count })
    }
}

In the first two lines, we see the usage of two macros:

  • entry_points - Generates entry points of the contract. By default it will generate instantiate, execute and query entry points. The other ones, migrate, reply, and sudo, are generated if a behavior related to them is defined in the impl block.

    This macro is wrapped in cfg_attr statement to be compiled only if library feature flag is not enabled. This way, other users who might want to use this contract in theirs won't get an entry point collision.

  • contract - Parses every method inside the impl block marked with the [sv::msg(...)] attribute and create proper messages and utilities like helpers for MultiTest.

This simple example also has the sv::msg(...) attributes. Sylvia macros distinguish the if message should be generated from the marked method and of what type.

CosmWasm contract requires the instantiate message, and it is mandatory to specify it for the contract macro. We have to provide it with the proper context type: InstantiateCtx (opens in a new tab). Another mandatory method is the new, as contract fields are out of scope for the contract macro, and otherwise we wouldn't be able to create the contract object in message dispatching.

Context gives us access to the blockchain state, information about our contract, and the sender of the message. We return the StdResult (opens in a new tab) which uses standard CosmWasm error StdError (opens in a new tab). It's generic over Response (opens in a new tab).

The template contract also contains a query and an exec messages. Each type of message in CosmWasm supports different contexts. F.e. the QueryCtx exposes to the user an immutable Deps (opens in a new tab) as by design, queries should never mutate the state. This is not the case for the ExecCtx and InstantiateCtx which exposes the DepsMut (opens in a new tab).

Feel free expanding the macro now and seeing what Sylvia generates. It might be overwhelming, as there will be a lot of things generated that seem not relevant to our code, so for the bare minimum, check the InstantiateMsg and its impl block.

Sylvia doesn't generate anything magical, but regular CosmWasm contract types customized based on the provided methods and attributes. This means that the Sylvia contract is fully interoperational with the standard CosmWasm contract.