Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sylvia: Add storey/storage_plus tabs #122

Merged
merged 2 commits into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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