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

Improve english & re-vamp whole book #47

Open
wants to merge 2 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
23 changes: 11 additions & 12 deletions src/README.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
# Introduction

This book is a guide for creating CosmWasm smart contracts. It will lead you
step by step, and explain relevant topics from the easiest to the trickier
ones.
step by step through the process and explain all of the relevant topics, from
the easiest to the trickiest.

The idea of the book is not only to tell you about smart contracts API but also
to show you how to do it in a clean and maintainable way. We will show you
patterns that CosmWasm creators established and encouraged you to use.
The idea of the book is not only to describe the API for smart contracts but also
to show you how to use them in a clean and maintainable way. We will show you the
patterns that CosmWasm creators have established and we encourage you to use as well.

## Prerequirements

This book explores CosmWasm smart contracts. It is not a Rust tutorial, and it
assumes basic Rust knowledge. As you will probably learn it alongside this
book, I strongly recommend grasping the language itself first. You can find
great resources to start with Rust on [Learn
Rust](https://www.rust-lang.org/learn) page.
This book explores CosmWasm smart contracts. It is not a Rust tutorial, and
assumes basic Rust knowledge. We strongly recommend ensuring you have a good
grasp of the language itself first. You can find great resources for starting
with Rust on [Learn Rust](https://www.rust-lang.org/learn) page.

## CosmWasm API documentation

This is the guide-like documentation. If you are looking for the API
documentation, you may be interested in checking one of the following:
This documentation is presented as a guide. If you are looking for the API
documentation, you may be interested in checking out one of the following:

- [cosmwasm-std](https://crates.io/crates/cosmwasm-std)
- [cw-storage-plus](https://crates.io/crates/cw-storage-plus)
Expand Down
7 changes: 3 additions & 4 deletions src/actor-model.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
# Actor model

This section describes the fundaments of CosmWasm smart contracts architecture, which determines how do they communicate
with each other. I want to go through this before teaching step by step how to create multiple contracts relating to each
other, to give you a grasp of what to expect. Don't worry if it will not be clear after the first read - I suggest going
through this chapter once now and maybe giving it another take in the future when you know the practical part of this.
This section describes the fundamentals of the architecture of CosmWasm smart contracts, which determines how they communicate
with each other. It is useful to go through this to get an idea of what to expect, before go through in detail how to create multiple contracts that relate to each other. Don't worry if it's not totally clear after the first read. We suggest going
through this chapter once now and maybe giving it another review once you are familiar with the practical part.

The whole thing described here is officially documented in the
[SEMANTICS.md](https://github.com/CosmWasm/cosmwasm/blob/main/SEMANTICS.md), of the `cosmwasm` repository.
229 changes: 110 additions & 119 deletions src/actor-model/actors-in-blockchain.md

Large diffs are not rendered by default.

415 changes: 197 additions & 218 deletions src/actor-model/contract-as-actor.md

Large diffs are not rendered by default.

431 changes: 198 additions & 233 deletions src/actor-model/idea.md

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions src/basics.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Basics

In this chapter, we will go through creating basic smart contracts step by step.
I will try to explain the core ideas behind CosmWasm and the typical contract structure.
In this chapter, we shall go through creating basic smart contracts step by step.
We shall try to explain the core ideas behind CosmWasm and the typical structure of
a contract.
36 changes: 17 additions & 19 deletions src/basics/building-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,31 @@

Now it is time to build our contract. We can use a traditional cargo build
pipeline for local testing purposes: `cargo build` for compiling it and `cargo
test` for running all tests (which we don't have yet, but we will work on that
test` for running all tests (which we don't have yet, but we'll work on that
soon).

However, we need to create a wasm binary to upload the contract to blockchain.
We can do it by passing an additional argument to the build command:
However, we need to create a Wasm binary to upload the contract to blockchain, which we can do by passing an additional argument to the build command:

```
$ cargo build --target wasm32-unknown-unknown --release
```

The `--target` argument tells cargo to perform cross-compilation for a given target instead of
building a native binary for an OS it is running on - in this case, `wasm32-unknown-unknown`,
which is a fancy name for Wasm target.

Additionally, I passed the `--release` argument to the command - it is not
required, but in most cases, debug information is not very useful while running
on-chain. It is crucial to reduce the uploaded binary size for gas cost
minimization. It is worth knowing that there is a [CosmWasm Rust
Optimizer](https://github.com/CosmWasm/rust-optimizer) tool that takes care of
building even smaller binaries. For production, all the contracts should be
compiled using this tool, but for learning purposes, it is not an essential
thing to do.
building a native binary for the OS it is running on - in this case, `wasm32-unknown-unknown`,
which is just a fancy name for a Wasm target.

We also passed the `--release` argument to the command - it is not strictly
required, but in most cases debug information is not very useful while running
on-chain. However, it is crucial to reduce the size of the uploaded binary in order to minimize
gas costs for execution. It is worth knowing that there is a [CosmWasm Rust
Optimizer](https://github.com/CosmWasm/rust-optimizer) tool that enables us to build even smaller
binaries. For production, all the contracts should be compiled using this tool, but for
learning purposes it is not essential.

## Aliasing build command

Now I can see you are disappointed in building your contracts with some overcomplicated command
instead of simple `cargo build`. Hopefully, it is not the case. The common practice is to alias
Now, you may be dismayed at the idea of having to building your contracts with some over-complicated command
instead of just the usual simple `cargo build`. Thankfully, this is not necessary. A common practice is to alias
the building command to make it as simple as building a native app.

Let's create the `.cargo/config` file in your contract project directory with the following content:
Expand All @@ -39,12 +37,12 @@ wasm = "build --target wasm32-unknown-unknown --release"
wasm-debug = "build --target wasm32-unknown-unknown"
```

Now, building your Wasm binary is as easy as executing `cargo wasm`. We also added the additional
`wasm-debug` command for rare cases when we want to build the wasm binary, including debug information.
Now building your Wasm binary is as easy as executing `cargo wasm`! We also added the additional
`wasm-debug` command for the rare cases when we want to build the wasm binary along with its debug information.

## Checking contract validity

When the contract is built, the last step is to ensure it is a valid CosmWasm contract is to call
When the contract is built, the last step to ensure it is a valid CosmWasm contract is to call
`cosmwasm-check` on it:

```
Expand Down
65 changes: 32 additions & 33 deletions src/basics/entry-points.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
# 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" is called. Unlike native applications, which have only a single
`main` entry point, smart contracts have a couple corresponding to different message types:
`instantiate`, `execute`, `query`, `sudo`, `migrate` and more.
A typical Rust application starts with a `fn main()` function called by the operating system.
Smart contracts are not very different. When the message is sent to the contract, a
function called an "entry point" is called. Unlike native applications, which have only a single
`main` entry point, smart contracts have a number of them, corresponding to different message
types: `instantiate`, `execute`, `query`, `sudo`, `migrate` and others.

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

* `instantiate` which is called once per smart contract lifetime - you can think about it as
* `instantiate` which is called once per smart contract lifetime. You can think of it as
a constructor or initializer of a contract.
* `execute` for handling messages which are able to modify contract state - they are used to
perform some actual actions.
* `query` for handling messages requesting some information from a contract; unlike `execute`,
* `execute` for handling messages which are able to modify the contract's state. They are used to
perform actions.
* `query` for handling messages requesting some information from a contract. Unlike `execute`,
they can never affect any contract state, and are used just like database queries.

Go to your `src/lib.rs` file, and start with an `instantiate` entry point:
Expand All @@ -33,46 +33,45 @@ pub fn instantiate(
}
```

In fact, `instantiate` is the only entry point required for a smart contract to be valid. It is not
very useful in this form, but it is a start. Let's take a closer look at the entry point structure.
`instantiate` is actually the only entry point required for a smart contract to be valid. The contract is of course not
particularly useful in this form, but it's a start! Let's take a closer look at the entry point structure.

First, we start with importing couple of types just for more consistent usage. Then we define our
entry point. The `instantiate` takes four arguments:
We start with importing a couple of types just for consistency, followed by defining our
entry point. The `instantiate` function takes four arguments:

* [`deps: DepsMut`](https://docs.rs/cosmwasm-std/1.0.0/cosmwasm_std/struct.DepsMut.html)
is a utility type for communicating with the outer world - it allows querying
and updating the contract state, querying other contracts state, and gives access to an `Api`
object with a couple of helper functions for dealing with CW addresses.
and updating the contract state, querying the state of other contracts, and also gives access to an `Api`
object with a couple of helper functions for dealing with CosmWasm addresses.
* [`env: Env`](https://docs.rs/cosmwasm-std/1.0.0/cosmwasm_std/struct.Env.html)
is an object representing the blockchains state when executing the message - the
chain height and id, current timestamp, and the called contract address.
chain height and id, current timestamp, and the address of the called contract.
* [`info: MessageInfo`](https://docs.rs/cosmwasm-std/1.0.0/cosmwasm_std/struct.MessageInfo.html)
contains metainformation about the message which triggered an execution -
an address that sends the message, and chain native tokens sent with the message.
contains meta-information about the message which triggered the execution -
the address that sent the message, and any chain native tokens sent with the message.
* [`msg: Empty`](https://docs.rs/cosmwasm-std/1.0.0/cosmwasm_std/struct.Empty.html)
is the message triggering execution itself - for now, it is `Empty` type that
represents `{}` JSON, but the type of this argument can be anything that is deserializable,
is the message triggering the execution itself. For now, it is just `Empty`
(represented by `{}` in JSON), but the type of this argument can be anything that is deserializable,
and we will pass more complex types here in the future.

If you are new to the blockchain, those arguments may not have much sense to you, but while
progressing with this guide, I will explain their usage one by one.
If you are new to the blockchain, those arguments may not make much sense to you yet but as we progress
through this guide we shall explain the usage of each in turn.

Notice an essential attribute decorating our entry point
[`#[entry_point]`](https://docs.rs/cosmwasm-std/1.0.0/cosmwasm_std/attr.entry_point.html). Its purpose is to
wrap the whole entry point to the form 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 rather overcomplicated, so CosmWasm
creators delivered the `entry_point` macro. It creates the raw Wasm entry point, calling the
wrap the whole entry point so that it is in a form that the Wasm runtime understands. The real Wasm entry points
can use only basic types supported natively by the Wasm specification, and Rust structures and enums
are not in this set. Working with such entry points would be unecessarily complicated, so CosmWasm's
creators created the `entry_point` 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.

The next thing to look at is the return type. I used
The next thing to look at is the return type. We used
[`StdResult<Response>`](https://docs.rs/cosmwasm-std/1.0.0/cosmwasm_std/type.StdResult.html) for this simple example,
which is an alias for `Result<Response, StdError>`. The return entry point type would always be a
[`Result`](https://doc.rust-lang.org/std/result/enum.Result.html) type, with some error type implementing
[`ToString`](https://doc.rust-lang.org/std/string/trait.ToString.html) trait and a well-defined type for success
which is an alias for `Result<Response, StdError>`. The return entry point type should always be a
[`Result`](https://doc.rust-lang.org/std/result/enum.Result.html) type, with some error type implementing the
[`ToString`](https://doc.rust-lang.org/std/string/trait.ToString.html) trait and a well-defined type for the success
case. For most entry points, an "Ok" case would be the
[`Response`](https://docs.rs/cosmwasm-std/1.0.0/cosmwasm_std/struct.Response.html) type that allows fitting the contract
into our actor model, which we will discuss very soon.
[`Response`](https://docs.rs/cosmwasm-std/1.0.0/cosmwasm_std/struct.Response.html) that allows the contract to fit into our "Actor Model", which we shall discuss very soon.

The body of the entry point is as simple as it could be - it always succeeds with a trivial empty response.
The body of the entry point is as simple as it can be - it always succeeds with a trivial empty response.
46 changes: 21 additions & 25 deletions src/basics/events.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
# Events attributes and data

The only way our contract can communicate to the world, for now, is by queries.
The only way our contract can communicate with the outside world, for now, is by means of queries.
Smart contracts are passive - they cannot invoke any action by themselves. They
can do it only as a reaction to a call. But if you tried playing with `wasmd`,
you know that execution on the blockchain can return some metadata.
can do so only in response to a call. However, if you have ever tried playing around with `wasmd`,
you'll know that execution on the blockchain can return some metadata.

There are two things the contract can return to the caller: events and data.
There are two things the contract can return to the caller: `event`s and `data`.
Events are something produced by almost every real-life smart contract. In
contrast, data is rarely used, designed for contract-to-contract communication.
contrast, `data` is designed for contract-to-contract communication and is much more rarely used, and it .

## Returning events

As an example, we would add an event `admin_added` emitted by our contract on the execution of
As an example, we shall add an event `admin_added`, which shall be emitted by our contract on the execution of
`AddMembers`:

```rust,noplayground
Expand Down Expand Up @@ -260,18 +260,16 @@ An event is built from two things: an event type provided in the
Attributes are added to an event with
the [`add_attributes`](https://docs.rs/cosmwasm-std/1.0.0/cosmwasm_std/struct.Event.html#method.add_attributes)
or the [`add_attribute`](https://docs.rs/cosmwasm-std/1.0.0/cosmwasm_std/struct.Event.html#method.add_attribute)
call. Attributes are key-value pairs. Because an event cannot contain any list, to achieve reporting
multiple similar actions taking place, we need to emit multiple small events instead of a collective one.
call. Attributes are key-value pairs. Because an event cannot contain a list, to achieve the reporting
of multiple similar actions taking place, we need to emit multiple small events rather than a single collective one.

Events are emitted by adding them to the response with
[`add_event`](https://docs.rs/cosmwasm-std/1.0.0/cosmwasm_std/struct.Response.html#method.add_event) or
[`add_events`](https://docs.rs/cosmwasm-std/1.0.0/cosmwasm_std/struct.Response.html#method.add_events) call.
Additionally, there is a possibility to add attributes directly to the response. It is just sugar. By default,
Additionally, it is possible to add attributes directly to the response. However, this is just sugar. By default,
every execution emits a standard "wasm" event. Adding attributes to the result adds them to the default event.

We can check if events are properly emitted by contract. It is not always done, as it is much of boilerplate in
test, but events are, generally, more like logs - not necessarily considered main contract logic. Let's now write
single test checking if execution emits events:
We can check if events are properly emitted by our contract. It's not always done, since there is a lot of boilerplate in the test and events are generally more like logs so not necessarily considered main contract logic. Let's now write a single test checking if execution emits events:

```rust,noplayground
# use crate::error::ContractError;
Expand Down Expand Up @@ -582,22 +580,20 @@ mod tests {
}
```

As you can see, testing events on a simple test made it clunky. First of all,
every string is heavily string-based - a lack of type control makes writing
such tests difficult. Also, even types are prefixed with "wasm-" - it may not
be a huge problem, but it doesn't clarify verification. But the problem is, how
layered events structure are, which makes verifying them tricky. Also, the
"wasm" event is particularly tricky, as it contains an implied attribute -
`_contract_addr` containing an address called a contract. My general rule is -
do not test emitted events unless some logic depends on them.
As you can see, testing events even in a simple test is clunky. First of all,
every check is heavily string-based - a lack of type control makes writing
such tests difficult. Also, event types are prefixed with "wasm-" - this may not
be a huge problem, but it certainly doesn't help clarify verification. However, the main problem lies in the layered events structure. This makes verifying them particularly tricky. Further, the
"wasm" event itself is also tricky, as it contains an implied attribute -
`_contract_addr` containing an address called a contract. Given these difficulties, the author follows a general approach of not testing emitted events unless some logic depends on them!

## Data

Besides events, any smart contract execution may produce a `data` object. In contrast to events, `data`
can be structured. It makes it a way better choice to perform any communication logic relies on. On the
other hand, it turns out it is very rarely helpful outside of contract-to-contract communication. Data
is always only one single object on the response, which is set using the
can be structured. This makes it a much better choice for performing any communication that the contract logic relies upon. On the
other hand, it turns out that it is very rarely helpful outside of contract-to-contract communication. Data
is always only one single object in the response, which is set using the
[`set_data`](https://docs.rs/cosmwasm-std/1.0.0/cosmwasm_std/struct.Response.html#method.set_data) function.
Because of its low usefulness in a single contract environment, we will not spend time on it right now - an
Because it is so rarely useful outside of a single contract environment, we will not spend time on it right now - an
example of it will be covered later when contract-to-contract communication will be discussed. Until then,
it is just helpful to know such an entity exists.
it is just helpful to bear in mind that such an entity exists.
Loading