Skip to content

Rust Smart Contracts FAQ

What is the full list of APIs for contracts to interact with the Oasis network?

A full list can be found in the owasm-ethereum docs.

My contract isn't deploying, what do I do?

Rust contracts can grow large quickly depending on how many third-party dependencies you pull in. It should be possible to deploy contracts that are ~500 KB already, compared to Parity's 20 KB limit. We are working on supporting even larger contracts.

To reduce the contract size, you can try:

  • Removing unused dependencies
  • Using wee-alloc will give you ~10 KB savings over the default allocator
  • Compiling your contract with the no-std flag and use the owasm-std crate, which is smaller than the default std crate.

Storing values in Rust contracts

Whether we want to store a String, u64, or complex data structure, the storage interface is the same. We can read and write bytes to and from storage using the owasm_ethereum::get_bytes and owasm_ethereum::set_bytes APIs.

For example, suppose we have an instance of the following struct we wish to save into storage.

struct PlayerInfo {
    address: [u8; 20],
    id: u16,
    is_bot: bool
}
let info = PlayerInfo { .. };

First, we’ll need to convert the struct into a Vec<u8> byte vector. The Serde crate makes this simple.

To do this, first add these dependencies to your Cargo.toml file:

serde = { version = "^1.0", features = ["derive"] }
serde_json = "^1.0"

Then in your contract, you can import these libraries and make sure your structs are serializable by adding derives before the struct declaration.

extern crate serde;
extern crate serde_json;
use serde::{Deserialize, Serialize};
...

#[derive(Deserialize, Serialize)]
struct PlayerInfo {
...

Then, we can generate the serialized data:

let serialized = serde_json::to_vec(&info).expect("Could not serialize storable.");

Once we have serialized our struct, we create an H256 key to save it under.

let key = keccak(format!(“{}”, info.id));

Then write it straight into storage.

owasm_ethereum::set_bytes(&key, &serialized);

It's that easy! Note how we converted the Vec<u8>into the more general &[u8] slice.

Reading values in Rust contracts

Continuing with the above example, say we want to load the struct for player 16, which has been stored in a previous transaction. Given the ID 16, we’ll first reconstruct our key and then read the serialized value from storage.

let key = keccak(“16”);
let serialized = owasm_ethereum::read_bytes(&key);

Once we have our serialized value, we can use Serde to perform the deserialization into our desired struct:

let player_info: PlayerInfo = serde_json::from_slice(serialized.as_slice())
                    .expect("Could not deserialize storable.");

We have successfully written to and read from storage our PlayerInfo struct.

How does the programming model differ from Parity's?

In order to further experiment with new features including new opcodes and gas models, we have forked some Parity repositories. Here are a list of some changes that we have made:

  • owasm-abi: changed the allowable method signatures in contract ABIs. The Parity crate only allows for pwasm types (e.g. U256, Address, ...) while the Oasis fork lets you use u64 directly.
  • owasm-abi-derive: added a contract attribute macro which auto-generates contract call/deploy boilerplate
  • owasm-ethereum: added bulk storage APIs (get_bytes and set_bytes)
  • owasm-std: allow the std feature so contracts can benefit from Rust std

What if my contract fails to deploy using Truffle?

A common issue you'll encounter when writing Rust contracts is that Truffle will fail on deployment, even if your contract compiles fine:

Error:  *** Deployment Failed ***

"Contract" -- The contract code couldn't be stored, please check your gas limit..

The error message is most likely related to insufficiently allocating memory. See the FAQ section below.

What do I do if I run out of memory?

If you run contracts that use a lot of memory you may run into the following error statement in the local Parity chain logs:

exception=Wasm("Wasm runtime error: Memory(\"trying to access region [39204..157198] in memory [0..65536]\")

This indicates that the contract has run out of memory.To solve this, there are two additional configurations to the contract that can be set. The first is in <contract_root_dir>/.cargo/config. Here, the amount of bytes allocated to the stack can be specified like so:

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

This example allows 4096 bytes to be allocated to the stack of the contract.

The second configuration is the --max-mem flag in wasm-build. If you're using the suggested oasis-box truffle workflow, the easiest way to configure this is to add it to your contract's Cargo.toml as follows:

[package.metadata.oasis]
max-mem = 131072

This flag specifies the total amount of memory that the contract can allocate, in bytes. Since this is the total amount of memory allocated to the contract, it must be strictly larger or equal to the stack size specified above. Also, this number must be divisible by 65536 as that is the size of a page. If not, then the amount allocated will round down. By default, this number is exactly a page, or 65536 bytes.

If you're manually compiling and not using a truffle workflow, then this would look like the following:

wasm-build --target wasm32-unknown-unknown --max-mem 131072 counter-contract

You should increase these values if you need. However, be careful to not set these values to be too high otherwise the contract will take too much gas to deploy. We have tried values up to 2097152.