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 tutoria first messages and entry points #209

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion src/pages/sylvia/macros/generated-types/message-types.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ be called depending on the enum variant. This is described in the example below

The following code:

```rust, template="sylvia-empty"
```rust template="sylvia-empty"
#[cw_serde]
pub struct SomeResponse;

Expand Down
6 changes: 3 additions & 3 deletions src/pages/tutorial/setup-environment.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,9 @@ All contracts (1) passed checks!

## Macro expansion

In VSCode you can hover over a macro like [`#[contract]`](../sylvia/macros/contract), do `shift+p`
and then type: `rust analyzer: Expand macro recursively`. This will open a window with a fully
expanded macro, which you can browse. In Vim you can consider installing the
In VSCode you can hover over a macro like [`#[contract]`](../sylvia/macros/contract), press
`shift+p` and then type: `rust analyzer: Expand macro recursively`. This will open a window with a
fully expanded macro, which you can browse. In Vim you can consider installing the
[rustaceanvim](https://github.com/mrcjkb/rustaceanvim) plugin. You can also use `cargo expand` tool
from CLI, like this:

Expand Down
4 changes: 3 additions & 1 deletion src/pages/tutorial/sylvia-contract/_meta.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
{
kulikthebird marked this conversation as resolved.
Show resolved Hide resolved
"contract-creation": "Contract creation"
"contract-creation": "Contract creation",
"entry-points": "Entry points",
"first-messages": "First messages"
}
4 changes: 2 additions & 2 deletions src/pages/tutorial/sylvia-contract/contract-creation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ cargo generate CosmWasm/sylvia-template

The [`sylvia-template`](https://github.com/CosmWasm/sylvia-template) will generate a lot of code for
you. Because this tutorial aims to guide you step-by-step through the process of creating your first
contract we will omit the use of the template and show you how to create it from the start.
contract we will omit the use of the template and show you how to create it from the scratch.

## New project

Expand Down Expand Up @@ -48,7 +48,7 @@ sylvia = "1.3.1"
[documentation](https://docs.rs/sylvia/latest/sylvia/#reexports).
</Callout>

As you can see, I added a `crate-type` field for the library section. Generating the
As you can see, we added a `crate-type` field for the library section. Generating the
[`cdylib`](https://doc.rust-lang.org/reference/linkage.html) is required to create a proper web
assembly binary. The downside of this is that such a library cannot be used as a dependency for
other Rust crates - for now, it is not needed, but later we will show how to approach reusing
Expand Down
88 changes: 88 additions & 0 deletions src/pages/tutorial/sylvia-contract/entry-points.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
---
tags: ["tutorial, sylvia"]
---

import { Callout } from "nextra/components";

# Entry points

Typical Rust application starts with the `fn main()` function called by the operating system. Smart
contracts are not significantly different. When the message is sent to the contract, a function
called [entry point](../../core/entrypoints) is executed. Unlike native applications, which have
only a single `main` entry point, smart contracts have a couple of them, each corresponding to
different message type.

To start, we will go with three basic entry points:

- [instantiate](../../core/entrypoints/instantiate) is called once per smart contract lifetime; you
can think about it as a constructor or initializer of a contract.
- [execute](../../core/entrypoints/execute) for handling messages which can modify contract state;
they are used to perform some actual actions.
- [query](../../core/entrypoints/query) for handling messages requesting some information from a
contract; unlike [execute](../../core/entrypoints/execute), they can never alter any contract
state, and are used in a similar manner to database queries.

## Generate entry points

Sylvia provides an attribute macro named [`entry_points`](../../sylvia/macros/entry-points). In most
cases, your entry point will just dispatch received messages to the handler, so it's not necessary
to manually create them, and we can rely on a macro to do that for us.

Let's add the [`entry_points`](../../sylvia/macros/entry-points) attribute macro to our contract:

```rust {3, 7} copy filename="src/contract.rs" template="sylvia-empty"
use sylvia::ctx::InstantiateCtx;
use sylvia::cw_std::{Response, StdResult};
use sylvia::{contract, entry_points};

pub struct CounterContract;

#[entry_points]
#[contract]
impl CounterContract {
pub const fn new() -> Self {
Self
}

#[sv::msg(instantiate)]
pub fn instantiate(&self, _ctx: InstantiateCtx) -> StdResult<Response> {
Ok(Response::default())
}
}
```

<Callout>
Note that [`entry_points`](../../sylvia/macros/entry-points) is added above the
[`contract`](../../sylvia/macros/contract). It is because
[`contract`](../../sylvia/macros/contract) removes attributes like
[`sv::msg(...)`](../../sylvia/macros/attributes/msg) on which both these macros rely. Always
remember to place [`entry_points`](../../sylvia/macros/entry-points) first.
</Callout>

Sylvia generates entry points with the [`entry_points`](../../sylvia/macros/entry-points) attribute
macro. Its purpose is to wrap the whole entry point to the form the Wasm runtime understands. The
proper Wasm entry points can use only basic types supported natively by Wasm specification, and Rust
structures and enums are not in this set. Working with such entry points would be overcomplicated,
so CosmWasm creators delivered the
[`entry_point`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/attr.entry_point.html) macro. It
creates the raw Wasm entry point, calling the decorated function internally and doing all the magic
required to build our high-level Rust arguments from arguments passed by Wasm runtime.

Now, when our contract has a proper entry point, let's build it and check if it's correctly defined:

```shell copy filename="TERMINAL"
cargo build --release --target wasm32-unknown-unknown --lib
```

```shell filename="TERMINAL"
Available capabilities: {"cosmwasm_1_3", "cosmwasm_2_0", "cosmwasm_1_2", "stargate", "iterator", "cosmwasm_1_1", "cosmwasm_1_4", "staking", "cosmwasm_2_1"}

target/wasm32-unknown-unknown/release/contract.wasm: pass

All contracts (1) passed checks!
```

## Next step

Well done! We have now a proper CosmWasm contract. Let's add some state to it, so it will actually
be able to do something.
88 changes: 88 additions & 0 deletions src/pages/tutorial/sylvia-contract/first-messages.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Generating first messages

We have set up our dependencies. Now let's use them to create simple messages.

## Creating an instantiation message

For this step we will create a new file:

- `src/contract.rs` - here, we will define our messages and behavior of the contract upon receiving
them

Add this module to `src/lib.rs`. You want it to be public, as users might want to get access to
types stored inside your contract.

```rust copy filename="src/lib.rs"
pub mod contract;
```

Now let's create an `instantiate` method for our contract. In `src/contract.rs`

```rust copy filename="src/contract.rs" template="sylvia-empty"
use sylvia::cw_std::{Response, StdResult};
use sylvia::contract;
use sylvia::ctx::InstantiateCtx;

pub struct CounterContract;

#[contract]
impl CounterContract {
pub const fn new() -> Self {
Self
}

#[sv::msg(instantiate)]
pub fn instantiate(&self, _ctx: InstantiateCtx) -> StdResult<Response> {
Ok(Response::default())
}
}
```

So what is going on here? First, we define the `CounterContract` struct. It is empty right now but
later when we learn about states, we will use its fields to store them. We mark the `impl` block
with [`contract`](../../sylvia/macros/contract) attribute macro. It will parse every method inside
the `impl` block marked with the [`sv::msg(...)`](../../sylvia/macros/attributes/msg) attribute and
create proper messages and utilities like
[MultiTest helpers](../../sylvia/macros/generated-types/multitest) for them. More on them later.

CosmWasm contract requires only the [instantiate](../../core/entrypoints/instantiate) entry point,
and it is mandatory to specify it for the [`contract`](../../sylvia/macros/contract) macro. We have
to provide it with the proper context type
[`InstantiateCtx`](https://docs.rs/sylvia/latest/sylvia/ctx/struct.InstantiateCtx.html).

Context gives us access to the blockchain state, information about our contract, and the sender of
the message. We return the
[`StdResult`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/type.StdResult.html) which uses
standard CosmWasm error
[`StdError`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/enum.StdError.html). It's generic over
[`Response`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/struct.Response.html). For now, we
will return the `default` value of it.

I recommend 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`](../../sylvia/macros/generated-types/message-types#contract-messages)
and its `impl` block.

## Next step

If we build our contract with command:

```shell copy filename="TERMINAL"
cargo build --release --target wasm32-unknown-unknown --lib
```

and then run:

```shell copy filename="TERMINAL"
cosmwasm-check target/wasm32-unknown-unknown/release/contract.wasm
```

The output should look like this:

```shell filename="TERMINAL"
Available capabilities: {"cosmwasm_1_3", "cosmwasm_2_0", "cosmwasm_1_2", "stargate", "iterator", "cosmwasm_1_1", "cosmwasm_1_4", "staking", "cosmwasm_2_1"}

target/wasm32-unknown-unknown/release/contract.wasm: pass

All contracts (1) passed checks!
```
Loading