(module ;; Name Registry smart contract ;; ;; Maps human-readable names → owner public keys on-chain. ;; Each name can only be registered once; only the current owner can ;; transfer or release it. ;; ;; Methods (all void, no WASM return values): ;; register(name string) — claim a name for the caller ;; resolve(name string) — log the current owner pubkey ;; transfer(name string, new_owner string) — give name to another pubkey ;; release(name string) — delete name registration ;; ;; State keys: the raw name bytes → owner pubkey bytes. ;; All args come in via env.get_arg_str / env.get_arg_u64. ;; ── imports ────────────────────────────────────────────────────────────── (import "env" "get_arg_str" (func $get_arg_str (param i32 i32 i32) (result i32))) (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 ─────────────────────────────────────────────────────────────── ;; Offset Size Purpose ;; 0x000 64 arg[0] buffer — name (max 64 bytes) ;; 0x040 128 arg[1] buffer — new_owner pubkey (max 128 bytes) ;; 0x0C0 128 caller pubkey buffer ;; 0x140 128 state-read buffer (existing owner) ;; 0x200 ~128 verbose log prefix strings ;; 0x300 256 scratch buffer — build "prefix: name" log messages (memory (export "memory") 1) ;; ── verbose log prefix strings ─────────────────────────────────────────── ;; Each entry is a human-readable prefix ending with ": " so that the ;; log message becomes "prefix: " — readable in the explorer. ;; ;; "registered: " 12 bytes @ 0x200 ;; "name taken: " 12 bytes @ 0x20C ;; "not found: " 11 bytes @ 0x218 ;; "owner: " 7 bytes @ 0x224 ;; "transferred: " 13 bytes @ 0x22C ;; "unauthorized: " 14 bytes @ 0x23A ;; "released: " 10 bytes @ 0x249 (data (i32.const 0x200) "registered: ") (data (i32.const 0x20C) "name taken: ") (data (i32.const 0x218) "not found: ") (data (i32.const 0x224) "owner: ") (data (i32.const 0x22C) "transferred: ") (data (i32.const 0x23A) "unauthorized: ") (data (i32.const 0x249) "released: ") ;; ── helpers ─────────────────────────────────────────────────────────────── ;; $memcpy: copy len bytes from src to dst (func $memcpy (param $dst i32) (param $src i32) (param $len i32) (local $i i32) (local.set $i (i32.const 0)) (block $break (loop $loop (br_if $break (i32.ge_u (local.get $i) (local.get $len))) (i32.store8 (i32.add (local.get $dst) (local.get $i)) (i32.load8_u (i32.add (local.get $src) (local.get $i)))) (local.set $i (i32.add (local.get $i) (i32.const 1))) (br $loop) ) ) ) ;; $log_prefix_name: build "" in scratch buf 0x300 and log it. ;; prefixPtr / prefixLen — the prefix string (e.g. "registered: ", 12 bytes) ;; suffixPtr / suffixLen — the name / pubkey to append (func $log_prefix_name (param $prefixPtr i32) (param $prefixLen i32) (param $suffixPtr i32) (param $suffixLen i32) ;; copy prefix → scratch[0] (call $memcpy (i32.const 0x300) (local.get $prefixPtr) (local.get $prefixLen)) ;; copy suffix → scratch[prefixLen] (call $memcpy (i32.add (i32.const 0x300) (local.get $prefixLen)) (local.get $suffixPtr) (local.get $suffixLen)) ;; log scratch[0 .. prefixLen+suffixLen) (call $log (i32.const 0x300) (i32.add (local.get $prefixLen) (local.get $suffixLen))) ) ;; $bytes_equal: compare mem[aPtr..aPtr+len) with mem[bPtr..bPtr+len) ;; Params: aPtr(0) bPtr(1) len(2) ;; Result: i32 1 = equal, 0 = not equal (func $bytes_equal (param i32 i32 i32) (result i32) (local $i i32) (local $same i32) (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 2))) (if (i32.ne (i32.load8_u (i32.add (local.get 0) (local.get $i))) (i32.load8_u (i32.add (local.get 1) (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) ) ) (local.get $same) ) ;; ── register(name) ──────────────────────────────────────────────────────── ;; Claims `name` for the caller. ;; Logs "registered: " on success, "name taken: " on conflict. (func (export "register") (local $nameLen i32) (local $callerLen i32) (local $existingLen i32) ;; Read name into 0x000, max 64 bytes (local.set $nameLen (call $get_arg_str (i32.const 0) (i32.const 0x000) (i32.const 64))) (if (i32.eqz (local.get $nameLen)) (then return)) ;; Check if name is already taken (local.set $existingLen (call $get_state (i32.const 0x000) (local.get $nameLen) (i32.const 0x140) (i32.const 128))) (if (i32.gt_u (local.get $existingLen) (i32.const 0)) (then (call $log_prefix_name (i32.const 0x20C) (i32.const 12) ;; "name taken: " (i32.const 0x000) (local.get $nameLen)) return ) ) ;; Store: state[name] = caller_pubkey (local.set $callerLen (call $get_caller (i32.const 0x0C0) (i32.const 128))) (call $set_state (i32.const 0x000) (local.get $nameLen) (i32.const 0x0C0) (local.get $callerLen)) (call $log_prefix_name (i32.const 0x200) (i32.const 12) ;; "registered: " (i32.const 0x000) (local.get $nameLen)) ) ;; ── resolve(name) ───────────────────────────────────────────────────────── ;; Logs "owner: " for the registered name, or "not found: ". (func (export "resolve") (local $nameLen i32) (local $ownerLen i32) (local.set $nameLen (call $get_arg_str (i32.const 0) (i32.const 0x000) (i32.const 64))) (if (i32.eqz (local.get $nameLen)) (then return)) (local.set $ownerLen (call $get_state (i32.const 0x000) (local.get $nameLen) (i32.const 0x140) (i32.const 128))) (if (i32.eqz (local.get $ownerLen)) (then (call $log_prefix_name (i32.const 0x218) (i32.const 11) ;; "not found: " (i32.const 0x000) (local.get $nameLen)) return ) ) ;; Log "owner: " ;; The pubkey stored in state is the raw caller pubkey bytes that the ;; host wrote via get_caller — these are the hex-encoded public key ;; string bytes, so the log will show the readable hex address. (call $log_prefix_name (i32.const 0x224) (i32.const 7) ;; "owner: " (i32.const 0x140) (local.get $ownerLen)) ) ;; ── transfer(name, new_owner) ───────────────────────────────────────────── ;; Transfers ownership of `name` to `new_owner`. ;; Only the current owner may call this (or anyone if name is unregistered). ;; Logs "transferred: " on success, "unauthorized: " otherwise. (func (export "transfer") (local $nameLen i32) (local $newOwnerLen i32) (local $callerLen i32) (local $existingLen i32) (local.set $nameLen (call $get_arg_str (i32.const 0) (i32.const 0x000) (i32.const 64))) (if (i32.eqz (local.get $nameLen)) (then return)) (local.set $newOwnerLen (call $get_arg_str (i32.const 1) (i32.const 0x040) (i32.const 128))) (if (i32.eqz (local.get $newOwnerLen)) (then return)) ;; Read existing owner into 0x140 (local.set $existingLen (call $get_state (i32.const 0x000) (local.get $nameLen) (i32.const 0x140) (i32.const 128))) ;; If not registered, anyone can claim → register directly for new_owner (if (i32.eqz (local.get $existingLen)) (then (call $set_state (i32.const 0x000) (local.get $nameLen) (i32.const 0x040) (local.get $newOwnerLen)) (call $log_prefix_name (i32.const 0x22C) (i32.const 13) ;; "transferred: " (i32.const 0x000) (local.get $nameLen)) return ) ) ;; Verify caller == existing owner (local.set $callerLen (call $get_caller (i32.const 0x0C0) (i32.const 128))) (if (i32.eqz (call $bytes_equal (i32.const 0x0C0) (i32.const 0x140) (if (result i32) (i32.ne (local.get $callerLen) (local.get $existingLen)) (then (i32.const 0)) ;; length mismatch → not equal (else (local.get $callerLen))))) (then (call $log_prefix_name (i32.const 0x23A) (i32.const 14) ;; "unauthorized: " (i32.const 0x000) (local.get $nameLen)) return ) ) ;; Authorized — update owner (call $set_state (i32.const 0x000) (local.get $nameLen) (i32.const 0x040) (local.get $newOwnerLen)) (call $log_prefix_name (i32.const 0x22C) (i32.const 13) ;; "transferred: " (i32.const 0x000) (local.get $nameLen)) ) ;; ── release(name) ───────────────────────────────────────────────────────── ;; Removes a name registration. Only the current owner may call this. ;; Logs "released: " on success. (func (export "release") (local $nameLen i32) (local $callerLen i32) (local $existingLen i32) (local.set $nameLen (call $get_arg_str (i32.const 0) (i32.const 0x000) (i32.const 64))) (if (i32.eqz (local.get $nameLen)) (then return)) (local.set $existingLen (call $get_state (i32.const 0x000) (local.get $nameLen) (i32.const 0x140) (i32.const 128))) (if (i32.eqz (local.get $existingLen)) (then (call $log_prefix_name (i32.const 0x218) (i32.const 11) ;; "not found: " (i32.const 0x000) (local.get $nameLen)) return ) ) ;; Verify caller == owner (local.set $callerLen (call $get_caller (i32.const 0x0C0) (i32.const 128))) (if (i32.eqz (call $bytes_equal (i32.const 0x0C0) (i32.const 0x140) (if (result i32) (i32.ne (local.get $callerLen) (local.get $existingLen)) (then (i32.const 0)) (else (local.get $callerLen))))) (then (call $log_prefix_name (i32.const 0x23A) (i32.const 14) ;; "unauthorized: " (i32.const 0x000) (local.get $nameLen)) return ) ) ;; Store empty bytes → effectively deletes the record (call $set_state (i32.const 0x000) (local.get $nameLen) (i32.const 0x000) (i32.const 0)) (call $log_prefix_name (i32.const 0x249) (i32.const 10) ;; "released: " (i32.const 0x000) (local.get $nameLen)) ) )