diff --git a/src/pages/sylvia/basics/contract-structure.mdx b/src/pages/sylvia/basics/contract-structure.mdx index 7149b2fc..31f930ef 100644 --- a/src/pages/sylvia/basics/contract-structure.mdx +++ b/src/pages/sylvia/basics/contract-structure.mdx @@ -2,25 +2,109 @@ tags: ["sylvia"] --- +import { Callout, Tabs } from "nextra/components"; + # 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. + + + +```rust +use cw_storey::containers::Item; + +pub struct CounterContract { pub count: Item, } +``` + + + + ```rust use sylvia::cw_storage_plus::Item; -pub struct CounterContract { - pub count: Item, -} +pub struct CounterContract { pub count: Item, } ``` + + + In Sylvia we keep the state accessors as part of the contract definition. The accessors are -[`cw_storage_plus`](../../cw-storage-plus) primitives. +[`storey`](../../storey) or [`cw_storage_plus`](../../cw-storage-plus) primitives. Let's take a look at the behavior implementation. + + + +```rust +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 { + self.count + .access(&mut CwStorage(ctx.deps.storage)) + .set(&0)?; + Ok(Response::new()) + } + + #[sv::msg(exec)] + fn increment(&self, ctx: ExecCtx) -> StdResult { + 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 { + let count = self + .count + .access(&CwStorage(ctx.deps.storage)) + .get()? + .ok_or_else(|| StdError::generic_err("Count not instantiated yet"))?; + Ok(CountResponse { count }) + } +} +``` + + + + ```rust +use cosmwasm_schema::cw_serde; +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 { @@ -53,12 +137,14 @@ impl CounterContract { } ``` + + + In the first two lines, we see the usage of two macros: -- [`entry_points`](https://docs.rs/sylvia/latest/sylvia/attr.entry_points.html) - 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. +- [`entry_points`](../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 @@ -92,7 +178,7 @@ queries should never mutate the state. This is not the case for the [`ExecCtx`](https://docs.rs/sylvia/latest/sylvia/types/struct.ExecCtx.html) and `InstantiateCtx` which exposes the [`DepsMut`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/struct.DepsMut.html). -Fell free expanding the macro now and seeing what Sylvia generates. It might be overwhelming, as +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. diff --git a/src/pages/sylvia/basics/interoperability.mdx b/src/pages/sylvia/basics/interoperability.mdx index 0b55c7be..17c1cd10 100644 --- a/src/pages/sylvia/basics/interoperability.mdx +++ b/src/pages/sylvia/basics/interoperability.mdx @@ -25,7 +25,30 @@ call the or [`add_messages`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/struct.Response.html#method.add_messages). -```rust + + + +```rust {3, 8, 13} +#[sv::msg(exec)] +fn external_increment(&self, ctx: ExecCtx) -> StdResult { + 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)) +} +``` + + + + +```rust {3-4, 9} #[sv::msg(exec)] fn external_increment(&self, ctx: ExecCtx) -> StdResult { let remote = self.remote.load(ctx.deps.storage)?; @@ -38,13 +61,75 @@ fn external_increment(&self, ctx: ExecCtx) -> StdResult { } ``` + + + We can also use the generated [`WasmMsg`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/enum.WasmMsg.html) to construct the [`SubMsg`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/struct.SubMsg.html) and expect reply. Learn more about replies [here](../../core/entrypoints/reply). -```rust {1, 24-33, 36-45} + + + +```rust {1, 18-20, 26-36, 42-48} +const SUBMSG_ID: u64 = 1; + +pub struct ReplyContract { + remote: Item>, +} + +#[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 { + 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 { + 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 { + match reply.id { + SUBMSG_ID => { + // Your logic here + Ok(Response::new()) + } + _ => Err(StdError::generic_err("Invalid reply id")), + } + } +} +``` + + + + +```rust {1, 18-20, 25-34, 39-45} const SUBMSG_ID: u64 = 1; pub struct ReplyContract { @@ -94,12 +179,36 @@ impl ReplyContract { } ``` + + + Query messages can also be sent through the [`query_wasm_smart`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/struct.QuerierWrapper.html#method.query_wasm_smart) method. We can access the [`Deps`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/struct.Deps.html) through the [`QueryCtx`](../types/context). + + + +```rust {5-7} +#[sv::msg(query)] +fn external_count(&self, ctx: QueryCtx) -> StdResult { + 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 {}) +} +``` + + + + ```rust {5-7} #[sv::msg(query)] fn external_count(&self, ctx: QueryCtx) -> StdResult { @@ -111,6 +220,9 @@ fn external_count(&self, ctx: QueryCtx) -> StdResult { } ``` + + + 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](../macros/generated-types/message-types).