Skip to main content

Smart Contract State

The smart contract state storage on the host consists of a single key/value map. Both key and value are raw data bytes. As long as you access the data in the same way that you used to store it, you will always get valid data back. The schema tool will create a type-safe access layer to make sure that data storage and retrieval always uses the expected data type.

The state section in the schema definition file contains a number of field definitions that together define the variables that are stored in the state storage. Each field definition uses a YAML key/value pair to define the name and data type of the field. The YAML key defines the field name. The YAML value (a string) defines the field's data type, and can be followed by an optional comment that describes the field.

The schema tool will use this information to generate the specific code that accesses the state variables in a type-safe way. Let's examine the state section of the dividend example in more detail:

state:
memberList: Address[] # array with all the recipients of this dividend

# factors per member

members: map[Address]Uint64 # map with all the recipient factors of this dividend
owner: AgentID # owner of contract, the only one who can call 'member' func
totalFactor: Uint64 # sum of all recipient factors

Let's start with the simplest state variables. totalFactor is an Uint64, and owner is an AgentID. Both are predefined WasmLib value types.

Then you have the memberList variable. The empty brackets [] indicate that this is an array. The brackets immediately follow the homogenous type of the elements in the array, which in this case is the predefined Address value type.

Finally, you have the members variable. The map[] indicates that this is a map. Between the brackets is the homogenous type of the keys, which in this case are of the predefined Address type. The brackets are immediately followed by the homogenous type of the values in the map, which in this case are of the predefined Uint64 type.

Here is part of the corresponding code in state.xx that the schema tool generates. The MutableDividendState struct defines a type-safe interface to access each of the state variables through mutable proxies:

type MutableDividendState struct {
proxy wasmtypes.Proxy
}

func (s MutableDividendState) AsImmutable() ImmutableDividendState {
return ImmutableDividendState(s)
}

// array with all the recipients of this dividend
func (s MutableDividendState) MemberList() ArrayOfMutableAddress {
return ArrayOfMutableAddress{proxy: s.proxy.Root(StateMemberList)}
}

// map with all the recipient factors of this dividend
func (s MutableDividendState) Members() MapAddressToMutableUint64 {
return MapAddressToMutableUint64{proxy: s.proxy.Root(StateMembers)}
}

// owner of contract, the only one who can call 'member' func
func (s MutableDividendState) Owner() wasmtypes.ScMutableAgentID {
return wasmtypes.NewScMutableAgentID(s.proxy.Root(StateOwner))
}

// sum of all recipient factors
func (s MutableDividendState) TotalFactor() wasmtypes.ScMutableUint64 {
return wasmtypes.NewScMutableUint64(s.proxy.Root(StateTotalFactor))
}

As you can see, the schema tool has generated a proxy interface for the mutable dividend state, called MutableDividendState. It has a 1-to-1 correspondence to the state section in the schema definition file. Each member function accesses a type-safe proxy object for the corresponding variable. In addition, the schema tool generates any necessary intermediate map and array proxy types that force the usage of their respective homogenous types. In the above example both ArrayOfMutableAddress and MapAddressToMutableUint64 are examples of such automatically generated proxy types. See the full state.xx for more details.

In the next section we will explore how the schema tool helps to simplify triggering events.