Skip to content

Commit

Permalink
Sylvia: Add storey/storage_plus tabs
Browse files Browse the repository at this point in the history
  • Loading branch information
jawoznia committed Jul 31, 2024
1 parent f3199a4 commit 41d7db8
Show file tree
Hide file tree
Showing 2 changed files with 187 additions and 10 deletions.
81 changes: 73 additions & 8 deletions src/pages/sylvia/basics/contract-structure.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,87 @@
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
#[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
#[cfg_attr(not(feature = "library"), sylvia::entry_points)]
#[contract]
Expand Down Expand Up @@ -53,12 +116,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
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 41d7db8

Please sign in to comment.