Skip to main content

State

Smart contracts store data in their account's state, which is public on the chain. The storage starts empty until a contract is deployed and the state is initialized.

Defining

The state is defined by the struct which implements the WIDL generated trait and whose trait impl block is annotated with #[smart_contract]. In the Counter Applet example, the state is defined by the CounterContractState struct. The attributes of this struct define the data that will be stored.

use serde::{Deserialize, Serialize};
use weil_macros::{constructor, mutate, query, smart_contract, WeilType};

pub trait Counter {
fn new() -> Result<Self, String>
where
Self: Sized;
async fn get_count(&self) -> usize;
async fn increment(&mut self);
}

#[derive(Serialize, Deserialize, WeilType)]
pub struct CounterContractState {
// define your contract state here!
}

#[smart_contract]
impl Counter for CounterContractState {
#[constructor]
fn new() -> Result<Self, String>
where
Self: Sized,
{... }

#[query]
async fn get_count(&self) -> usize {...}

#[mutate]
async fn increment(&mut self) {...}
}

Key Aspects

  • Serializable: The state must be serializable.
  • Ordered: The state must be ordered on serialization.
  • Persistent: The state is serialized and stored inside the persistent storage. This means that the state persists across function calls.

To statically ensure these properties, the Contract SDK defines WeilType and only instances of WeilType may be used inside the state. For example, in Rust a HashMap is not ordered but a BTreeMap is, so HashMap cannot be a WeilType, but BTreeMap can. WeilType is detailed in the following section.

WeilType

We call a WeilType those data types that may be used as state by an Applet. In Rust, for example, WeilType is an SDK provided trait which may be derived by the state types.
A type can derive WeilType if all of its fields (in case of structs) or variant (in case of enums) implements WeilType.

In almost all cases you would not implement this derivation manually. In Rust, for example, you will always use the derive macro WeilType to define your state type, as in the previous example.

The following table lists the basic WIDL types, which all implement WeilType, and their equivalent types in the supported languages.

WIDLRustGoAssemblyScript
u8u8uint8u8
u16u16uint16u16
u32u32uint32u32
u64u64`uint64u64
usizeusizeuint32u32 or u64
i8i8int8i8
i16i16int16i16
i32i32int32i32
i64i64int64i64
isizeisizeint32i32 or i64
f32float32f32
f64float64f64
boolboolboolean
charN/A
stringStringstringstring

Some basic collections are also WeilType, and listed in the following table.

WIDLRustGoAssemblyScript
Vec<_>`WeilVec
BTreeMap<_, _>Map<_, _>
BTreeSet<_>Set<_>

Some language specific types are also WeilType, but these may not have equivalent types in all languages and should not be used as parameter or return values.

WIDLRustGoAssemblyScript
Box<T>
Option<T>
Result<T,E>
() (unity)
tuple<T,U,...>(T,U,...)

Whenever an exported method is called, the complete serialized state is loaded into WASM memory and deserialized to form the in-memory state object on which the method is then called. This means that a contract containing large amounts of data might not fit in memory. This is where Weil-Collections comes into the picture.

Weil-Collections are light-weight lazy loaded counterparts of the standard collections like Vec and HashMap in Rust.

WeilVec<T>, WeilMap<K, V>, WeilSet<T>, WeilTrieMap<T>, WeilMemory

More details on these collections is given in Section Collections.