Skip to main content

Writing Session Code

This section explains how to write session code. To review the definition of session code and the differences between session code and contract code, see Comparing Session Code and Contract Code. Session code can be written in any programming language that compiles to Wasm. However, the examples in this topic use Rust.

Creating the Directory Structure

For writing session code, we use the same project structure used for writing contracts, described here.

Example 1: Writing Session Code

The following steps illustrate the process of writing session code using an example repository containing sample session code for configuring an account: https://github.com/casper-ecosystem/two-party-multi-sig/. The sample code adds an associated key to the account and updates the action thresholds. Remember that an Account on a Casper network can add associated accounts and set up a multi-signature scheme for deploys. To follow along, clone the repository.

git clone https://github.com/casper-ecosystem/two-party-multi-sig/
note

Before executing session code, ensure that you know exactly what the session code is doing. If you don't know what it is meant for, it could be doing something malicious.

Dependencies in Cargo.toml

The Cargo.toml file includes the dependencies and versions the session code requires. At a minimum, you need to import the latest versions of the casper-contract and casper-types crates. The following dependencies and version numbers are only examples and must be adjusted based on your requirements.

  • casper-contract = "1.4.4" - Provides the SDK for the execution engine (EE). The latest version of the crate is published here.
  • casper-types = "1.5.0" - Includes types shared by many Casper crates for use on a Casper network. This crate is necessary for the EE to understand and interpret the session code. The latest version of the crate is published here.

Updating the main.rs File

Open the contract/src/main.rs file that contains the sample session code. Notice these directives at the top of the file:

  • #![no_std] - Specifies not to import the standard library.
  • #![no_main] - Indicates the main function is not required since the session code has only one entry point as the call function.

Next, review the imported crates and other required libraries.

#![no_std]
#![no_main]

use casper_contract::contract_api::{account, runtime};
use casper_contract::unwrap_or_revert::UnwrapOrRevert;
use casper_types::account::{AccountHash, ActionType, Weight};

After the imported libraries, we usually find the constants.

const ASSOCIATED_ACCOUNT: &str = "deployment-account";

Next, we see the call function, the only entry point in this example session code. The #[no_mangle] flag ensures that the function name is retained as a string in the Wasm binary. For session code, this flag retains the call string and marks the entry point for the execution engine. Explore the call function details by opening the cloned project.

#[no_mangle]
pub extern "C" fn call() {
// Open the repository for details
}

When compiled, the call function could be used from another library. For example, a C library could link to the resulting Wasm.

Example 2: Calling a Contract with Session Code

Another example of session code is the counter-call/src/main.rs file, in the counter repository. This example shows how we commonly use session code to invoke logic stored within a smart contract. To follow along, clone the repository.

git clone https://github.com/casper-ecosystem/counter/

Observe how the project is set up and review the dependencies in the counter/counter-call/Cargo.toml file. Then, open the counter/counter-call/src/main.rs file containing the session code. Notice the directives at the top of the file, the required dependencies, and the declared constants.

The call function interacts with the contract's counter_inc and counter_get entry points. This is how the session's call entry point triggers the logic stored inside the counter contract.

    // Call the counter to get the current value.
let current_counter_value: u32 =
runtime::call_contract(contract_hash, COUNTER_GET, RuntimeArgs::new());

// Call the counter to increment the value.
let _: () = runtime::call_contract(contract_hash, COUNTER_INC, RuntimeArgs::new());

Example 3: Transfers using Session Code

In this example, we use session code to perform a transfer using the transfer_from_purse_to_purse system function. The entire session code is available in GitHub, but this is the call function:

#[no_mangle]
pub extern "C" fn call() {
let target_purse: URef = runtime::get_named_arg(ARG_TARGET_PURSE);
let amount: U512 = runtime::get_named_arg(ARG_AMOUNT);

let source_purse = account::get_main_purse();

system::transfer_from_purse_to_purse(source_purse, target_purse, amount, None)
.unwrap_or_revert();
}

Another system function is transfer_to_public_key. The full session code example is on GitHub.

#[no_mangle]
pub extern "C" fn call() {
let account_hash: PublicKey = runtime::get_named_arg(ARG_TARGET);
let transfer_amount: U512 = runtime::get_named_arg(ARG_AMOUNT);
system::transfer_to_public_key(account_hash, transfer_amount, None).unwrap_or_revert();
}

Other transfer functions are available here:

Compiling Session Code

Before running session code to interact with a contract or other entities on the network, you must compile it to Wasm. Run the following command in the directory hosting the Cargo.toml file and src folder.

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

For the examples above, you may use the Makefiles provided:

make build-contract

Executing Session Code

Before running session code on a live Casper network, test it as described here. You can also set up a local network using NCTL for additional tests.

Session code can execute on a Casper network via a Deploy. All deploys can be broadly categorized as some unit of work that, when executed and committed, affects change to the network's global state.

The Casper command-line client and its put-deploy command provide one way to execute session code.

casper-client put-deploy \
--node-address <HOST:PORT> \
--chain-name <NETWORK-NAME> \
--secret-key <PATH> \
--payment-amount <PAYMENT-AMOUNT> \
--session-path <SESSION-PATH> \
--session-arg <"NAME:TYPE='VALUE'" OR "NAME:TYPE=null">
  • node-address - An IP address of a peer on the network. The default port for JSON-RPC servers on Mainnet and Testnet is 7777.
  • secret-key - The file name containing the secret key of the account paying for the deploy.
  • chain-name - The network where the deploy should be sent. For Mainnet, use casper. For Testnet, use casper-test.
  • payment-amount - Payment for the deploy in motes. The payment amount varies based on the deploy and network chainspec.
  • session-path - Path to the contract Wasm, pointing to the compiled contract.
  • session-arg - A named and typed argument passed to the Wasm code.

Use the --help option to view an updated list of supported arguments.

casper-client put-deploy --help

Video Walkthrough

The following brief video describes sample session code for configuring an account.

What's Next?