// Counter smart contract — compiles to WASM with GOOS=wasip1 GOARCH=wasm. // // Methods (exported via //go:export): // - increment — adds 1 to the stored counter // - get — logs the current value (readable via /api/contracts/{id}/state/counter) // - reset — resets counter to 0; only the first caller (owner) is allowed // // Host imports from the "env" module (see vm/host.go): // - put_u64(keyPtr, keyLen, val) — stores uint64 as 8-byte big-endian // - get_u64(keyPtr, keyLen) uint64 — reads 8-byte big-endian uint64 // - get_caller(buf, bufLen) int32 — writes caller pub key hex into buf // - get_state(kPtr,kLen,dPtr,dLen) int32 — reads raw state bytes // - set_state(kPtr,kLen,vPtr,vLen) — writes raw state bytes // - log(msgPtr, msgLen) — emits message to node log // //go:build wasip1 package main import ( "unsafe" ) // ── host function imports ───────────────────────────────────────────────────── //go:wasmimport env put_u64 func hostPutU64(keyPtr unsafe.Pointer, keyLen int32, val uint64) //go:wasmimport env get_u64 func hostGetU64(keyPtr unsafe.Pointer, keyLen int32) uint64 //go:wasmimport env get_caller func hostGetCaller(buf unsafe.Pointer, bufLen int32) int32 //go:wasmimport env get_state func hostGetState(keyPtr unsafe.Pointer, keyLen int32, dstPtr unsafe.Pointer, dstLen int32) int32 //go:wasmimport env set_state func hostSetState(keyPtr unsafe.Pointer, keyLen int32, valPtr unsafe.Pointer, valLen int32) //go:wasmimport env log func hostLog(msgPtr unsafe.Pointer, msgLen int32) // ── helpers ─────────────────────────────────────────────────────────────────── func logMsg(s string) { if len(s) == 0 { return } b := []byte(s) hostLog(unsafe.Pointer(&b[0]), int32(len(b))) } func putU64(key string, val uint64) { b := []byte(key) hostPutU64(unsafe.Pointer(&b[0]), int32(len(b)), val) } func getU64(key string) uint64 { b := []byte(key) return hostGetU64(unsafe.Pointer(&b[0]), int32(len(b))) } func getState(key string, dst []byte) int32 { kb := []byte(key) return hostGetState(unsafe.Pointer(&kb[0]), int32(len(kb)), unsafe.Pointer(&dst[0]), int32(len(dst))) } func setState(key string, val []byte) { kb := []byte(key) hostSetState(unsafe.Pointer(&kb[0]), int32(len(kb)), unsafe.Pointer(&val[0]), int32(len(val))) } func getCaller() string { buf := make([]byte, 128) n := hostGetCaller(unsafe.Pointer(&buf[0]), int32(len(buf))) if n <= 0 { return "" } return string(buf[:n]) } // ── contract state keys ─────────────────────────────────────────────────────── const ( keyCounter = "counter" keyOwner = "owner" ) // ── exported contract methods ───────────────────────────────────────────────── //go:export increment func increment() { val := getU64(keyCounter) val++ putU64(keyCounter, val) logMsg("incremented") } //go:export get func get() { logMsg("get called") } //go:export reset func reset() { caller := getCaller() if caller == "" { logMsg("reset: no caller") return } ownerBuf := make([]byte, 128) ownerLen := getState(keyOwner, ownerBuf) if ownerLen == 0 { // No owner set yet — first caller becomes the owner. setState(keyOwner, []byte(caller)) putU64(keyCounter, 0) logMsg("reset ok (owner set)") return } owner := string(ownerBuf[:ownerLen]) if caller != owner { logMsg("reset: unauthorized") return } putU64(keyCounter, 0) logMsg("reset ok") } // main is required by the Go runtime for wasip1 programs. func main() {}