Getting Started

In this section we will walk through writing the hello C++ example in Rust. In this example you will learn how to setup and optimize a basic smart contract, accept an input, and print to the console.

Creating the project

First, let's create a new project with Cargo and change directories:

cargo +nightly new hello --lib
cd hello

You should now have a directory that looks like this:

src/
  lib.rs
Cargo.toml

Configuring Cargo

The Cargo.toml file is used by Rust to manage dependencies and other configuration options. If you open this file now it should look similar to this:

[package]
name = "hello"
version = "0.1.0"
authors = []
edition = "2018"

[dependencies]

Let's change this to add eosio as a dependency, and change the crate type so that Rust generates a .wasm file:

[package]
name = "hello"
version = "0.1.0"
authors = []
edition = "2018"

[lib]
crate-type = ["cdylib"]

[dependencies]
eosio = "0.2"

Generating and Optimizing a WASM File

At this point we can compile our project to produce a .wasm file:

cargo build --release --target=wasm32-unknown-unknown

You should now see a generated file at target/wasm32-unknown-unknown/release/hello.wasm:

$ ls -lh target/wasm32-unknown-unknown/release | grep wasm
-rwxr-xr-x 2 sagan sagan 1.9M Oct 25 16:34 hello.wasm

This file will be huge at almost 2MB! But we can significantly reduce file size by enabling link-time optimization. Add this to the bottom of our Cargo.toml file:

[profile.release]
lto = true

Now if we rebuild the project we should see a much smaller .wasm file:

$ cargo build --release --target=wasm32-unknown-unknown
$ ls -lh target/wasm32-unknown-unknown/release | grep wasm
-rwxr-xr-x 2 sagan sagan  52K Oct 25 16:48 hello_world.wasm

That's better, but 52KB is still heavy for an empty smart contract. Luckily we can use wasm-gc and wasm-opt to reduce the file size even further:

wasm-gc target/wasm32-unknown-unknown/release/hello.wasm hello_gc.wasm
wasm-opt hello_gc.wasm --output hello_gc_opt.wasm -Oz
$ ls -lh | grep wasm
-rw-r--r-- 1 sagan sagan  109 Oct 25 16:57 hello_gc_opt.wasm
-rw-r--r-- 1 sagan sagan  116 Oct 25 16:56 hello_gc.wasm

By using wasm-gc and wasm-opt we are able to get the file size down to just over 100 bytes! But this is before we've added any code. Realistically you can expect simple contracts to be under 15KB.

Writing the Smart Contract

Now that we know how to prepare the .wasm file, let's start coding. Open up src/lib.rs and replace its contents with this:


# #![allow(unused_variables)]
#fn main() {
use eosio::*;                       // Include everything from the eosio crate

#[eosio_action]                     // Mark this function as an action
fn hi(name: AccountName) {
    eosio_print!("Hello, ", name);  // Print to the console
}

eosio_abi!(hi);                     // Create the 'apply' function
#}

See the API documentation for more details on what this code is doing.

Let's recompile our project and minify the the WASM file again:

cargo build --release --target=wasm32-unknown-unknown
wasm-gc target/wasm32-unknown-unknown/release/hello.wasm hello_gc.wasm
wasm-opt hello_gc.wasm --output hello_gc_opt.wasm -Oz

Creating the ABI File

In the future ABI files will be automatically generated, but for now they must be typed out manually. Copy this code into a file called hello.abi.json:

{
    "version": "eosio::abi/1.0",
    "structs": [
        {
            "name": "hi",
            "base": "",
            "fields": [
                {
                    "name": "name",
                    "type": "name"
                }
            ]
        }
    ],
    "actions": [
        {
            "name": "hi",
            "type": "hi"
        }
    ]
}

Deploying, First Attempt

At this point we have our WASM and ABI files, so we're ready to deploy our smart contract.

For this you will need to have access to an EOS node. Please see the Installing EOS section for instructions. The code below will assume that you've started nodeos and keosd containers using Docker Compose.

First create an alias for cleos:

alias cleos='docker-compose exec keosd cleos --url http://nodeosd:8888 --wallet-url http://127.0.0.1:8900'

Then create a wallet, import a private key, and create the hello account:

PUBKEY=EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV
PRIVKEY=5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
cleos wallet create --to-console
cleos wallet import --private-key $PRIVKEY
cleos create account eosio hello $PUBKEY $PUBKEY

Deploy the ABI:

cleos set abi hello /mnt/dev/project/hello.abi.json

Deploy the WASM:

cleos set code hello /mnt/dev/project/hello_gc_opt.wasm

...but this will fail with an error!

$ cleos set code hello /mnt/dev/project/hello_gc_opt.wasm
Reading WASM from /mnt/dev/project/hello_gc_opt.wasm...
Setting Code...
Error 3070002: Runtime Error Processing WASM

In the nodeos console log you will see this error message:

error 2018-10-26T03:46:12.176 thread-0  http_plugin.cpp:580           handle_exception     ] FC Exception encountered while processing chain.push_transaction
debug 2018-10-26T03:46:12.176 thread-0  http_plugin.cpp:581           handle_exception     ] Exception Details: 3070002 wasm_execution_error: Runtime Error Processing WASM
Smart contract data segments must lie in first 64KiB
     {"k":64}
     thread-0  wasm_eosio_validation.cpp:30 validate
pending console output:
     {"console":""}
     thread-0  apply_context.cpp:72 exec_one

This is happening because Rust by default reserves 1MB for the stack, but EOS expects data to be within the first 64KB.

Deploying, Second Attempt

We can fix this by telling the Rust compiler to reserve less than 64KB for the stack. Create a new file at .cargo/config with these contents:

[target.wasm32-unknown-unknown]
rustflags = [
  "-C", "link-args=-z stack-size=48000"
]

48KB seems to be a reasonable number, but feel free to experiment.

Now let's try to rebuild and redeploy our contract:

cargo build --release --target=wasm32-unknown-unknown
wasm-gc target/wasm32-unknown-unknown/release/hello.wasm hello_gc.wasm
wasm-opt hello_gc.wasm --output hello_gc_opt.wasm -Oz
cleos set code hello /mnt/dev/project/hello_gc_opt.wasm

Finally, say hello:

cleos push action hello hi '["world"]' -p 'hello@active'

Success!

If all went well you should see Hello, world in the console. Otherwise, if the transaction was sent successfully but you don't see any output, you may need to use the --contract-console option with nodeos.