Skip to content

Commit

Permalink
Sylvia: Add storey/storage_plus tabs (#122)
Browse files Browse the repository at this point in the history
  • Loading branch information
jawoznia authored Aug 6, 2024
2 parents f3199a4 + 6f6186b commit 47b7d5e
Show file tree
Hide file tree
Showing 2 changed files with 209 additions and 11 deletions.
104 changes: 95 additions & 9 deletions src/pages/sylvia/basics/contract-structure.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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.

<Tabs items={['Storey', 'StoragePlus']}>
<Tabs.Tab>

```rust
use cw_storey::containers::Item;

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

</Tabs.Tab>
<Tabs.Tab>

```rust
use sylvia::cw_storage_plus::Item;

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

</Tabs.Tab>
</Tabs>

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.

<Tabs items={['Storey', 'StoragePlus']}>
<Tabs.Tab>

```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<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 })
}
}
```

</Tabs.Tab>
<Tabs.Tab>

```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 {
Expand Down Expand Up @@ -53,12 +137,14 @@ impl CounterContract {
}
```

</Tabs.Tab>
</Tabs>

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
Expand Down Expand Up @@ -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.

Expand Down
116 changes: 114 additions & 2 deletions src/pages/sylvia/basics/interoperability.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,30 @@ call the
or
[`add_messages`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/struct.Response.html#method.add_messages).

```rust
<Tabs items={['Storey', 'StoragePlus']}>
<Tabs.Tab>

```rust {3, 8, 13}
#[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))
}
```

</Tabs.Tab>
<Tabs.Tab>

```rust {3-4, 9}
#[sv::msg(exec)]
fn external_increment(&self, ctx: ExecCtx) -> StdResult<Response> {
let remote = self.remote.load(ctx.deps.storage)?;
Expand All @@ -38,13 +61,75 @@ fn external_increment(&self, ctx: ExecCtx) -> StdResult<Response> {
}
```

</Tabs.Tab>
</Tabs>

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.

<Callout>Learn more about replies [here](../../core/entrypoints/reply).</Callout>

```rust {1, 24-33, 36-45}
<Tabs items={['Storey', 'StoragePlus']}>
<Tabs.Tab>

```rust {1, 18-20, 26-36, 42-48}
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")),
}
}
}
```

</Tabs.Tab>
<Tabs.Tab>

```rust {1, 18-20, 25-34, 39-45}
const SUBMSG_ID: u64 = 1;

pub struct ReplyContract {
Expand Down Expand Up @@ -94,12 +179,36 @@ impl ReplyContract {
}
```

</Tabs.Tab>
</Tabs>

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

<Tabs items={['Storey', 'StoragePlus']}>
<Tabs.Tab>

```rust {5-7}
#[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 {})
}
```

</Tabs.Tab>
<Tabs.Tab>

```rust {5-7}
#[sv::msg(query)]
fn external_count(&self, ctx: QueryCtx) -> StdResult<ExternalResponse> {
Expand All @@ -111,6 +220,9 @@ fn external_count(&self, ctx: QueryCtx) -> StdResult<ExternalResponse> {
}
```

</Tabs.Tab>
</Tabs>

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

Expand Down

0 comments on commit 47b7d5e

Please sign in to comment.