diff --git a/src/pages/sylvia/macros/generated-types/message-types.mdx b/src/pages/sylvia/macros/generated-types/message-types.mdx index 503b01cb..fbc1203f 100644 --- a/src/pages/sylvia/macros/generated-types/message-types.mdx +++ b/src/pages/sylvia/macros/generated-types/message-types.mdx @@ -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; diff --git a/src/pages/tutorial/setup-environment.mdx b/src/pages/tutorial/setup-environment.mdx index 95d1f6d6..f39af6e9 100644 --- a/src/pages/tutorial/setup-environment.mdx +++ b/src/pages/tutorial/setup-environment.mdx @@ -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: diff --git a/src/pages/tutorial/sylvia-contract/_meta.json b/src/pages/tutorial/sylvia-contract/_meta.json index 8f9dc55c..2d82bb42 100644 --- a/src/pages/tutorial/sylvia-contract/_meta.json +++ b/src/pages/tutorial/sylvia-contract/_meta.json @@ -1,3 +1,5 @@ { - "contract-creation": "Contract creation" + "contract-creation": "Contract creation", + "entry-points": "Entry points", + "first-messages": "First messages" } diff --git a/src/pages/tutorial/sylvia-contract/contract-creation.mdx b/src/pages/tutorial/sylvia-contract/contract-creation.mdx index 9e89ff86..de9f9259 100644 --- a/src/pages/tutorial/sylvia-contract/contract-creation.mdx +++ b/src/pages/tutorial/sylvia-contract/contract-creation.mdx @@ -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 @@ -48,7 +48,7 @@ sylvia = "1.3.1" [documentation](https://docs.rs/sylvia/latest/sylvia/#reexports). -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 diff --git a/src/pages/tutorial/sylvia-contract/entry-points.mdx b/src/pages/tutorial/sylvia-contract/entry-points.mdx new file mode 100644 index 00000000..d5379255 --- /dev/null +++ b/src/pages/tutorial/sylvia-contract/entry-points.mdx @@ -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 { + Ok(Response::default()) + } +} +``` + + + 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. + + +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. diff --git a/src/pages/tutorial/sylvia-contract/first-messages.mdx b/src/pages/tutorial/sylvia-contract/first-messages.mdx new file mode 100644 index 00000000..36bb501b --- /dev/null +++ b/src/pages/tutorial/sylvia-contract/first-messages.mdx @@ -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 { + 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! +```