Use the Schema Tool
Creating smart contracts is simplified using the Schema Tool. This guide outlines the initial steps to set up a new smart contract from scratch.
Step 1: Establish a Central Folder
Select a central folder to house all your smart contracts. You'll create a separate subfolder for each contract within this central repository.
Next, choose a descriptive, capitalized camel case name for your contract, like MySmartContract
.
Step 2: Create a Subfolder
After naming your smart contract, you should create a corresponding subfolder. Open your terminal, navigate to the central folder, and initialize your project with the Schema Tool using the following command:
schema -init MySmartContract
This command will create a subfolder named mysmartcontract
and generate an initial YAML
schema definition file inside this subfolder. Note that the generated subfolder name is
all lower-case. This is to conform to best practices for package names in most languages.
The generated schema definition file looks like this:
- YAML
name: MySmartContract
description: MySmartContract description
events: {}
structs: {}
typedefs: {}
state:
owner: AgentID // current owner of this smart contract
funcs:
init:
params:
owner: AgentID? // optional owner of this smart contract
setOwner:
access: owner // current owner of this smart contract
params:
owner: AgentID // new owner of this smart contract
views:
getOwner:
results:
owner: AgentID // current owner of this smart contract
After initializing your project with the Schema Tool,
a pre-populated schema definition file will be generated in the mysmartcontract
subfolder.
This file contains the necessary sections and functions to manage your smart contract's ownership.
Naming Conventions
Ensure to follow the camel case naming convention in the schema definition file. Here is how to use it:
- Function and variable names: start with a lowercase letter (e.g.,
myFunction
) - Type names: start with an uppercase letter (e.g.,
MyType
)
Customizing Fields
Begin by updating the description
field with a relevant description of your smart contract.
It is the perfect time to add any known definitions to the necessary sections.
Generating Initial Code
Navigate to your mysmartcontract
subfolder to generate the initial code for your preferred programming language using
the Schema Tool.
- Go
- Rust
- Typescript
If you want to generate Go code, you should run the schema tool with the -go
option like
this:
schema -go
If you want to generate Rust code, you should run the schema tool with the -rs
option
like this:
schema -rs
If you want to generate TypeScript code, you should run the schema tool with the -ts
option like this:
schema -ts
If you want to generate more than one language your can simply specify multiple options. For example, to generate both Rust and Go code you would specify both options like this:
schema -rs -go
The schema tool will generate a complete set of source files for the desired language(s), that will compile successfully into a Wasm code file. You compile these as follows:
- Go
- Rust
- Typescript
tinygo build -target wasm go/main.go
This will use the Go source files in the go/mysmartcontract subfolder. The only file in
this folder that you should edit manually is mysmartcontract.go
. All other source files
will be regenerated and overwritten whenever the schema tool is run again.
See the TinyGo documentation for more build options.
After generating the Rust code, you should first modify the Cargo.toml file to your liking, and potentially add the new project to a Rust workspace. Cargo.toml will not be regenerated once it already exists. Then build the code as follows:
wasm-pack build
This will use Rust source files in the src
subfolder. The only file in this folder that
you should edit manually is mysmartcontract.rs
. All other source files will be
regenerated and overwritten whenever the schema tool is run again.
See the wasm-pack documentation for more build options.
npx asc ts/mysmartcontract/lib.ts --outFile mysmartcontract_ts.wasm --lib path/to/node_modules
This will use the TypeScript source files in the ts/mysmartcontract subfolder. The only
file in this folder that you should edit manually is mysmartcontract.ts
. All other
source files will be regenerated and overwritten whenever the schema tool is run again.
See the AssemblyScript documentation for more build options.
The generated code is essentially identical for each language, barring some language idiosyncrasy differences. Just view different language files with the same name next to, each other, and you will see what we mean.
Here is an example of the initially generated code, mysmartcontract.xx
looks like this
before you even start modifying it:
- Go
- Rust
- Typescript
package mysmartcontract
import "github.com/iotaledger/wasp/packages/wasmvm/wasmlib/go/wasmlib"
func funcInit(ctx wasmlib.ScFuncContext, f *InitContext) {
if f.Params.Owner().Exists() {
f.State.Owner().SetValue(f.Params.Owner().Value())
return
}
f.State.Owner().SetValue(ctx.RequestSender())
}
func funcSetOwner(ctx wasmlib.ScFuncContext, f *SetOwnerContext) {
f.State.Owner().SetValue(f.Params.Owner().Value())
}
func viewGetOwner(ctx wasmlib.ScViewContext, f *GetOwnerContext) {
f.Results.Owner().SetValue(f.State.Owner().Value())
}
use wasmlib::*;
use crate::*;
pub fn func_init(ctx: &ScFuncContext, f: &InitContext) {
if f.params.owner().exists() {
f.state.owner().set_value(&f.params.owner().value());
return;
}
f.state.owner().set_value(&ctx.request_sender());
}
pub fn func_set_owner(_ctx: &ScFuncContext, f: &SetOwnerContext) {
f.state.owner().set_value(&f.params.owner().value());
}
pub fn view_get_owner(_ctx: &ScViewContext, f: &GetOwnerContext) {
f.results.owner().set_value(&f.state.owner().value());
}
import * as wasmlib from 'wasmlib';
import * as wasmtypes from 'wasmlib/wasmtypes';
import * as sc from './index';
export function funcInit(ctx: wasmlib.ScFuncContext, f: sc.InitContext): void {
if (f.params.owner().exists()) {
f.state.owner().setValue(f.params.owner().value());
return;
}
f.state.owner().setValue(ctx.requestSender());
}
export function funcSetOwner(
ctx: wasmlib.ScFuncContext,
f: sc.SetOwnerContext,
): void {
f.state.owner().setValue(f.params.owner().value());
}
export function viewGetOwner(
ctx: wasmlib.ScViewContext,
f: sc.GetOwnerContext,
): void {
f.results.owner().setValue(f.state.owner().value());
}
The schema tool automatically generates an initial working version of the functions to maintain the smart contract owner, catering to most use cases.
To streamline the building process, configure a build rule in your environment.
This rule should trigger the schema tool with the necessary parameters
whenever there are changes in the schema definition file.
This setup ensures automatic file regeneration,
eliminating the need to run the schema tool manually after each modification.
The tool regenerates code only if it detects alterations since its last operation.
To override this and force regeneration, include the -force
flag in the command line parameter.