Skip to content

Develop a smart contract

This is a brief overview of Magi smart contract development with Go.

Terminal window
git clone https://github.com/vsc-eco/go-vsc-node
cd go-vsc-node
go mod download
make contract-deployer

This will generate an executable named contract-deployer in the build folder. You may move it to your PATH directory (i.e. /usr/bin for Linux or /usr/local/bin for macOS).


Clone the Go contract template:

Terminal window
git clone https://github.com/vsc-eco/go-contract-template
  • Directorycontract your contract code goes here
    • main.go
  • Directoryruntime
    • gc_leaking_exported.go
  • Directorysdk
    • address.go
    • env.go
    • sdk.go
  • Directorytest
    • contract_test.go
  • .gitignore
  • go.mod
  • go.sum

The below contract demonstrates calling SDK methods to interact with the contract database and defining WASM exports to mark a function as callable by Magi accounts or other contracts.

Find out more about the Go contract SDK methods here.

main.go
package main
import "contract-template/sdk"
//go:wasmexport hello_world
func HelloWorld(a *string) *string {
ret := "Hello world"
return &ret
}
//go:wasmexport setString
func SetString(a *string) *string {
sdk.StateSetObject("myString", *a)
return a
}
//go:wasmexport getString
func GetString(a *string) *string {
return sdk.StateGetObject("myString")
}

To compile your contract:

Terminal window
tinygo build -gc=custom -scheduler=none -panic=trap -no-debug -target=wasm-unknown -o artifacts/main.wasm ./contract

To remove metadata from the output WASM binary to reduce file size:

Terminal window
wasm-strip -o artifacts/main-striped.wasm artifacts/main.wasm

To inspect the output WASM assembly:

Terminal window
wasm2wat artifacts/main.wasm

We provide a contract test environment for you to execute contract calls on a state engine that resembles the live network. Your contract test code will look something like this:

package contract_test
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/assert"
"github.com/vsc-eco/go-vsc-node/lib/test_utils"
"github.com/vsc-eco/go-vsc-node/modules/db/vsc/contracts"
ledgerDb "github.com/vsc-eco/go-vsc-node/modules/db/vsc/ledger"
stateEngine "github.com/vsc-eco/go-vsc-node/modules/state-processing"
)
//go:embed artifacts/main.wasm
var ContractWasm []byte
func TestContract(t *testing.T) {
ct := test_utils.NewContractTest()
ct.RegisterContract("vsccontractid", ContractWasm)
ct.Deposit("hive:someone", 1000, ledgerDb.AssetHive) // deposit 1 HIVE
ct.Deposit("hive:someone", 1000, ledgerDb.AssetHbd) // deposit 1 HBD
result := ct.Call(stateEngine.TxVscCallContract{
Self: stateEngine.TxSelf{
TxId: "sometxid",
BlockId: "abcdef",
Index: 69,
OpIndex: 0,
Timestamp: "2025-09-03T00:00:00",
RequiredAuths: []string{"hive:someone"},
RequiredPostingAuths: []string{},
},
ContractId: contractId,
Action: "yourMethodName",
Payload: json.RawMessage([]byte("1000")),
RcLimit: 1000,
Intents: []contracts.Intent{{
Type: "transfer.allow",
Args: map[string]string{
"limit": "1.000",
"token": "hive",
},
}},
})
assert.True(t, result.Success) // assert contract execution success
assert.LessOrEqual(t, result.GasUsed, uint(10000000)) // assert this call uses no more than 10M WASM gas
assert.GreaterOrEqual(t, len(result.Logs), 1) // assert at least 1 log emitted
}

Find out more about methods provided by the contract test utils here.


Firstly, initialize the deployer configuration:

Terminal window
vsc-contract-deploy -init

This will generate some config files in data/config folder in your current directory. Insert your Hive username and active key of the deployer account in identityConfig.json:

identityConfig.json
{
"BlsPrivKeySeed": "9e264692ced37...",
"HiveActiveKey": "ADD_YOUR_PRIVATE_WIF",
"HiveUsername": "ADD_YOUR_USERNAME",
"Libp2pPrivKey": "125cd98aed75d..."
}

Then deploy your contract to Magi:

Terminal window
vsc-contract-deploy -wasmPath artifacts/main-striped.wasm -name "my first contract"

You should see an output similar to this:

WASM_CODE: 1130 [0 97 115 109 1 0 0 0 1 9] ...
peer ID: 12D3KooWS7N7zmrkMHxGX9ibNXbKk4byfkx8ckEhfgXM8eQcgBtK
NAT Status {Private}
12D3KooWS7N7zmrkMHxGX9ibNXbKk4byfkx8ckEhfgXM8eQcgBtK pubsub /vsc/mainnet/data-availability/v1 peers: 20
pubsub handling error: message did not add any signatures
pubsub handling error: message did not add any signatures
pubsub handling error: message did not add any signatures
pubsub handling error: message did not add any signatures
pubsub handling error: message did not add any signatures
pubsub handling error: message did not add any signatures
pubsub handling error: message did not add any signatures
pubsub handling error: message did not add any signatures
{bafkreibwwj3fypek5uz6l3scacy47yw3b2adgbxmfab2ybmkarljaggbey {i_TLUMJVJb6n6X-5_lKvBibiLvP0RUVz2tn20KJTw5Q_lQ-tcVt9jrZr4s1WVga1CR7q0Ldnrc-EjNpqgaY7GpnhE2d3pC3nfNhUKkmN1za6h9SwCEUuo3CByNnmnqYE __f9}}
{"__v":"0.1","code":"bafkreibwwj3fypek5uz6l3scacy47yw3b2adgbxmfab2ybmkarljaggbey","description":"","name":"go test","net_id":"vsc-mainnet","owner":"techcoderx.vsc","runtime":"go","storage_proof":{"hash":"bafkreibwwj3fypek5uz6l3scacy47yw3b2adgbxmfab2ybmkarljaggbey","signature":{"sig":"i_TLUMJVJb6n6X-5_lKvBibiLvP0RUVz2tn20KJTw5Q_lQ-tcVt9jrZr4s1WVga1CR7q0Ldnrc-EjNpqgaY7GpnhE2d3pC3nfNhUKkmN1za6h9SwCEUuo3CByNnmnqYE","bv":"__f9"}}}
pubsub handling error: message did not add any signatures
pubsub handling error: message did not add any signatures
tx id: bef70add6d21cd812cf68da2caee72da05de48b4
contract id: vsc1Bem8RnoLgGPP7E2MBN52ekrdVqy2LNpSqF
Error in subscription: subscription cancelled

Contracts may be called from L1 using Hive accounts or on Magi using offchain accounts.

To call your contract, navigate to the contract page on Magi Blocks by pasting your contract ID in the search bar, then click on the Call Contract tab to connect your wallet and fill up the contract call details.