(module ;; Counter smart contract — methods: increment, get, reset ;; State key "counter" stores uint64 as 8-byte big-endian. ;; State key "owner" stores the deployer pub key set on first reset() call. ;; ── imports ────────────────────────────────────────────────────────────── (import "env" "put_u64" (func $put_u64 (param i32 i32 i64))) (import "env" "get_u64" (func $get_u64 (param i32 i32) (result i64))) (import "env" "get_caller" (func $get_caller (param i32 i32) (result i32))) (import "env" "get_state" (func $get_state (param i32 i32 i32 i32) (result i32))) (import "env" "set_state" (func $set_state (param i32 i32 i32 i32))) (import "env" "log" (func $log (param i32 i32))) ;; ── memory ─────────────────────────────────────────────────────────────── ;; Memory layout: ;; offset 0 : "counter" (7 bytes) — state key for the counter value ;; offset 16 : "owner" (5 bytes) — state key for the owner pub key ;; offset 32 : caller buffer (128 bytes) ;; offset 160 : owner buffer (128 bytes) ;; offset 288 : log messages (memory (export "memory") 1) (data (i32.const 0) "counter") (data (i32.const 16) "owner") (data (i32.const 288) "incremented") (data (i32.const 300) "get called") (data (i32.const 310) "reset") (data (i32.const 316) "unauthorized") (data (i32.const 329) "reset ok") ;; ── increment() ────────────────────────────────────────────────────────── (func (export "increment") (local $val i64) (local.set $val (call $get_u64 (i32.const 0) (i32.const 7))) (local.set $val (i64.add (local.get $val) (i64.const 1))) (call $put_u64 (i32.const 0) (i32.const 7) (local.get $val)) (call $log (i32.const 288) (i32.const 11)) ) ;; ── get() ───────────────────────────────────────────────────────────────── (func (export "get") (call $log (i32.const 300) (i32.const 10)) ) ;; ── reset() ─────────────────────────────────────────────────────────────── ;; Resets counter to 0. Only callable by the deployer (first caller sets ownership). (func (export "reset") (local $callerLen i32) (local $ownerLen i32) (local $i i32) (local $same i32) (local.set $callerLen (call $get_caller (i32.const 32) (i32.const 128))) (local.set $ownerLen (call $get_state (i32.const 16) (i32.const 5) (i32.const 160) (i32.const 128))) ;; No owner yet — first caller becomes owner and resets to 0 (if (i32.eqz (local.get $ownerLen)) (then (call $set_state (i32.const 16) (i32.const 5) (i32.const 32) (local.get $callerLen)) (call $put_u64 (i32.const 0) (i32.const 7) (i64.const 0)) (call $log (i32.const 329) (i32.const 8)) return ) ) ;; Length mismatch → unauthorized (if (i32.ne (local.get $callerLen) (local.get $ownerLen)) (then (call $log (i32.const 316) (i32.const 12)) return) ) ;; Byte-by-byte comparison (local.set $same (i32.const 1)) (local.set $i (i32.const 0)) (block $break (loop $loop (br_if $break (i32.ge_u (local.get $i) (local.get $callerLen))) (if (i32.ne (i32.load8_u (i32.add (i32.const 32) (local.get $i))) (i32.load8_u (i32.add (i32.const 160) (local.get $i)))) (then (local.set $same (i32.const 0)) (br $break)) ) (local.set $i (i32.add (local.get $i) (i32.const 1))) (br $loop) ) ) (if (i32.eqz (local.get $same)) (then (call $log (i32.const 316) (i32.const 12)) return) ) ;; Authorized — reset (call $put_u64 (i32.const 0) (i32.const 7) (i64.const 0)) (call $log (i32.const 329) (i32.const 8)) ) )