Rust

This section explains step by step how to write a new smart contract on Casper. Start with main.rs from the previous section.

A Basic Smart Contract in Rust

The Casper VM executes a smart contract by calling the call function specified in the contract. If the function is missing, the smart contract is not valid. The simplest possible example is an empty call function.

#[no_mangle]
    pub extern "C" fn call() {}
    

The #[no_mangle] attribute prevents the compiler from changing (mangling) the function name when converting to the binary format of Wasm. Without it, the VM exits with the error message: Module doesn't have export call.

Arguments

It's possible to pass arguments to smart contracts. To leverage this feature, use runtime::get_named_arg.

use casperlabs_contract::contract_api::runtime;
    
    #[no_mangle]
    pub extern "C" fn call() {
        let value: String = runtime::get_named_arg("value");
    }
    

Storage

Saving and reading values to and from the blockchain is a manual process in Casper. It requires more code to be written, but also provides a lot of flexibility. The storage system works similarly to a file system in an operating system. Let's say we have a string "Hello Casper!" that needs to be saved. To do this, use the text editor, create a new file, paste the string in and save it under a name in some directory. The pattern is similar on the Casper blockchain. First you have to save your value to the memory using storage::new_uref. This returns a reference to the memory object that holds the "Hello Casper!" value. You could use this reference to update the value to something else. It's like a file. Secondly you have to save the reference under a human-readable string using runtime::put_key. It's like giving a name to the file. The following function implements this scenario:

const KEY: &str = "special_value";
    
    fn store(value: String) {
        // Store `value` under a new unforgeable reference.
        let value_ref = storage::new_uref(value);
    
        // Wrap the unforgeable reference in a `Key`.
        let value_key: Key = value_ref.into();
    
        // Store this key under the name "special_value" in context-local storage.
        runtime::put_key(KEY, value_key);
    }
    

After this function is executed, the context (Account or Smart Contract) will have the content of the value stored under KEY in its named keys space. The named keys space is a key-value storage that every context has. It's like a home directory.

Final Smart Contract

The code below is the simple contract generated by cargo-casper (found in contract/src/main.rs of a project created by the tool). It reads an argument and stores it in the memory under a key named "special_value".

#![cfg_attr(
        not(target_arch = "wasm32"),
        crate_type = "target arch should be wasm32"
    )]
    #![no_main]
    
    use casperlabs_contract::{
        contract_api::{runtime, storage},
    };
    use casperlabs_types::{Key, URef};
    
    const KEY: &str = "special_value";
    const ARG_MESSAGE: &str = "message";
    
    fn store(value: String) {
        // Store `value` under a new unforgeable reference.
        let value_ref: URef = storage::new_uref(value);
    
        // Wrap the unforgeable reference in a value of type `Key`.
        let value_key: Key = value_ref.into();
    
        // Store this key under the name "special_value" in context-local storage.
        runtime::put_key(KEY, value_key);
    }
    
    // All session code must have a `call` entrypoint.
    #[no_mangle]
    pub extern "C" fn call() {
        // Get the optional first argument supplied to the argument.
        let value: String = runtime::get_named_arg(ARG_MESSAGE);
        store(value);
    }
    

AssemblyScript

CasperLabs maintains the casper-contract to allow developers to create smart contracts using AssemblyScript. The package source is hosted in the main Casper Network repository.

Installation

For each smart contract, it is necessary to create a project directory and initialize it.

The npm init process prompts for various details about the project. Answer as you see fit, but you may safely default everything except name, which needs to be specified. In this guide, we will refer to the contract name as your-contract-name.

mkdir project
    cd project
    npm init
    

Then install AssemblyScript and this package in the project directory.

npm install --save-dev assemblyscript@0.9.1
    npm install --save casper-contract
    

Contract API Documentation

The Assemblyscript contract API documentation can be found at https://www.npmjs.com/package/casper-contract.

Usage

Add script entries for AssemblyScript to your project's package.json. Note that your contract name is used for the name of the WASM file. Replace your-contract-name with the name of your contract.

{
      "name": "your-contract-name",
      ...
      "scripts": {
        "asbuild:optimized": "asc assembly/index.ts -b dist/your-contract-name.wasm --validate --optimize --use abort=",
        "asbuild": "npm run asbuild:optimized",
        ...
      },
      ...
    }
    

In the project root, create an index.js file with the following contents. Replace your-contract-name with the name of your contract.

const fs = require("fs");
    
    const compiled = new WebAssembly.Module(fs.readFileSync(__dirname + "/dist/your-contract-name.wasm"));
    
    const imports = {
        env: {
            abort(_msg, _file, line, column) {
                console.error("abort called at index.ts:" + line + ":" + column);
            },
        },
    };
    
    Object.defineProperty(module, "exports", {
        get: () => new WebAssembly.Instance(compiled, imports).exports,
    });
    

Next, create a directory called assembly, and in that directory, create a file called tsconfig.json in the following way:

{
        "extends": "../node_modules/assemblyscript/std/assembly.json",
        "include": ["./**/*.ts"]
    }
    

Sample smart contract

In the assembly directory, also create an index.ts file, where the code for the contract needs to go.

You can use the following sample snippet, which demonstrates a simple smart contract that immediately returns an error and writes a message to a block when executed on the Casper Network.

//@ts-nocheck
    import { Error, ErrorCode } from "casper-contract/error";
    
    // simplest possible feedback loop
    export function call(): void {
        Error.fromErrorCode(ErrorCode.None).revert(); // ErrorCode: 1
    }
    

Deploy the Contract

Deploy is done by executing the previously created execute_request instance.

builder.exec(execute_request).expect_success().commit();

Post-Assertion to Confirm Deploy

This will query the post-deploy value and assert for its change.

  let result_of_query = builder
            .query(None, Key::Account(account_addr), &[KEY.to_string()])
            .expect("should be stored value.")
            .as_cl_value()
            .expect("should be cl value.")
            .clone()
            .into_t::<String>()
            .expect("should be string.");

If you prefer a more complicated first contract, you can look at example contracts on the Casper Ecosystem GitHub repository for inspiration.