Skip to main content

Initialize a Smart Contract

Smart contracts begin with a blank slate. You can define an initial state through the init() function. This function is vital for setting configurations at the time of deployment.

Features of the init() Function

One-time Call

Triggered automatically post-deployment, it can only be used once to set the initial configurations.

Security

The ISC ensures the init() function is inaccessible after its initial execution, safeguarding it from unauthorized accesses.

Necessity For a Separate Configuration Function

To facilitate reconfiguration in the future, develop a distinct configuration function with proper access controls.

Usage Example

To show how creating a smart contract with WasmLib works, we will slowly start fleshing out the smart contract functions of the dividend example in this tutorial. Here is the first part of the code that implements it, which contains the init() function:

// This example implements 'dividend', a simple smart contract that will
// automatically disperse iota tokens which are sent to the contract to a group
// of member accounts according to predefined division factors. The intent is
// to showcase basic functionality of WasmLib through a minimal implementation
// and not to come up with a complete robust real-world solution.
// Note that we have drawn sometimes out constructs that could have been done
// in a single line over multiple statements to be able to properly document
// step by step what is happening in the code. We also unnecessarily annotate
// all 'var' statements with their assignment type to improve understanding.

//nolint:revive,goimports
package dividend

import (
"github.com/iotaledger/wasp/packages/wasmvm/wasmlib/go/wasmlib"
"github.com/iotaledger/wasp/packages/wasmvm/wasmlib/go/wasmlib/wasmtypes"
)

// 'init' is used as a way to initialize a smart contract. It is an optional
// function that will automatically be called upon contract deployment. In this
// case we use it to initialize the 'owner' state variable so that we can later
// use this information to prevent non-owners from calling certain functions.
// The 'init' function takes a single optional parameter:
// - 'owner', which is the agent id of the entity owning the contract.
// When this parameter is omitted the owner will default to the contract creator.
func funcInit(ctx wasmlib.ScFuncContext, f *InitContext) {
// The schema tool has already created a proper InitContext for this function that
// allows us to access call parameters and state storage in a type-safe manner.

// First we set up a default value for the owner in case the optional 'owner'
// parameter was omitted. We use the agent that sent the deploy request.
var owner wasmtypes.ScAgentID = ctx.RequestSender()

// Now we check if the optional 'owner' parameter is present in the params map.
if f.Params.Owner().Exists() {
// Yes, it was present, so now we overwrite the default owner with
// the one specified by the 'owner' parameter.
owner = f.Params.Owner().Value()
}

// Now that we have sorted out which agent will be the owner of this contract
// we will save this value in the 'owner' variable in state storage on the host.
// Read the documentation on schema.yaml to understand why this state variable is
// supported at compile-time by code generated from schema.yaml by the schema tool.
f.State.Owner().SetValue(owner)
}

We define an owner variable and allow it to be something other than the default value of the contract creator. It is always a good idea to be flexible enough to transfer ownership to another entity if necessary. Remember that once a smart contract has been deployed it is no longer possible to change it. Therefore, it is good practice to think through situations that could require a change in advance and allow the contract itself to handle such changes through its state by providing a proper function interface:

// 'setOwner' is used to change the owner of the smart contract.
// It updates the 'owner' state variable with the provided agent id.
// The 'setOwner' function takes a single mandatory parameter:
// - 'owner', which is the agent id of the entity that will own the contract.
// Only the current owner can change the owner.
func funcSetOwner(_ wasmlib.ScFuncContext, f *SetOwnerContext) {
// Note that the schema tool has already dealt with making sure that this function
// can only be called by the owner and that the required parameter is present.
// So once we get to this point in the code we can take that as a given.

// Save the new owner parameter value in the 'owner' variable in state storage.
f.State.Owner().SetValue(f.Params.Owner().Value())
}

Note that we only define a single owner here. A proper fall-back could require multiple owners in case the owner entity could disappear, which would allow others to take over instead of the contract becoming immutable about the “owner functionality”. We cannot stress enough how important it is to think through every aspect of a smart contract before deployment.