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.

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 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.