// gen generates contracts/escrow/escrow.wasm // Run from repo root: go run ./contracts/escrow/gen/ // // Methods: init, create, release, refund, dispute, resolve, info // // State keys (per escrow id): // "admin" → admin address // "e::b" → buyer address // "e::s" → seller address // "e::v" → amount (8-byte big-endian u64) // "e::x" → status byte: // 'a' = active (funds locked) // 'd' = disputed // 'r' = released to seller // 'f' = refunded to buyer // // The contract treasury holds the locked funds during active/disputed state. package main import ( "fmt" "os" ) // ── LEB128 & builders ──────────────────────────────────────────────────────── func u(v uint64) []byte { var b []byte for { bt := byte(v & 0x7f) v >>= 7 if v != 0 { bt |= 0x80 } b = append(b, bt) if v == 0 { return b } } } func s(v int64) []byte { var b []byte for { bt := byte(v & 0x7f) v >>= 7 sign := (bt & 0x40) != 0 if (v == 0 && !sign) || (v == -1 && sign) { return append(b, bt) } b = append(b, bt|0x80) } } func cat(slices ...[]byte) []byte { var out []byte for _, sl := range slices { out = append(out, sl...) } return out } func wstr(str string) []byte { return cat(u(uint64(len(str))), []byte(str)) } func section(id byte, content []byte) []byte { return cat([]byte{id}, u(uint64(len(content))), content) } func vec(items ...[]byte) []byte { out := u(uint64(len(items))) for _, it := range items { out = append(out, it...) } return out } func functype(params, results []byte) []byte { return cat([]byte{0x60}, u(uint64(len(params))), params, u(uint64(len(results))), results) } func importFunc(mod, name string, typeIdx uint32) []byte { return cat(wstr(mod), wstr(name), []byte{0x00}, u(uint64(typeIdx))) } func exportEntry(name string, kind byte, idx uint32) []byte { return cat(wstr(name), []byte{kind}, u(uint64(idx))) } func dataSegment(offset int32, data []byte) []byte { return cat( []byte{0x00}, []byte{0x41}, s(int64(offset)), []byte{0x0B}, u(uint64(len(data))), data, ) } func funcBody(localDecls []byte, instrs ...[]byte) []byte { inner := cat(localDecls) for _, ins := range instrs { inner = append(inner, ins...) } inner = append(inner, 0x0B) return cat(u(uint64(len(inner))), inner) } var noLocals = u(0) func localDecl(n uint32, typ byte) []byte { return cat(u(uint64(n)), []byte{typ}) } func withLocals(decls ...[]byte) []byte { return cat(u(uint64(len(decls))), cat(decls...)) } const ( tI32 byte = 0x7F tI64 byte = 0x7E ) func call(fn uint32) []byte { return cat([]byte{0x10}, u(uint64(fn))) } func lget(i uint32) []byte { return cat([]byte{0x20}, u(uint64(i))) } func lset(i uint32) []byte { return cat([]byte{0x21}, u(uint64(i))) } func ic32(v int32) []byte { return cat([]byte{0x41}, s(int64(v))) } func ic64(v int64) []byte { return cat([]byte{0x42}, s(v)) } func block_() []byte { return []byte{0x02, 0x40} } func loop_() []byte { return []byte{0x03, 0x40} } func if_() []byte { return []byte{0x04, 0x40} } func ifI32() []byte { return []byte{0x04, tI32} } func else_() []byte { return []byte{0x05} } func end_() []byte { return []byte{0x0B} } func br_(lbl uint32) []byte { return cat([]byte{0x0C}, u(uint64(lbl))) } func brIf_(lbl uint32) []byte { return cat([]byte{0x0D}, u(uint64(lbl))) } func return_() []byte { return []byte{0x0F} } func drop() []byte { return []byte{0x1A} } func i32Eqz() []byte { return []byte{0x45} } func i32Ne() []byte { return []byte{0x47} } func i32GtU() []byte { return []byte{0x4B} } func i32GeU() []byte { return []byte{0x4F} } func i32Add() []byte { return []byte{0x6A} } func i64Eqz() []byte { return []byte{0x50} } func i32Load8U() []byte { return []byte{0x2D, 0x00, 0x00} } func i32Store8() []byte { return []byte{0x3A, 0x00, 0x00} } // ── Memory layout ───────────────────────────────────────────────────────────── // // 0x000 64 arg[0]: escrow id // 0x040 128 arg[1]: seller address or winner string // 0x140 128 caller buffer // 0x1C0 64 treasury buffer // 0x200 128 state-read buffer 1 (buyer or seller) // 0x280 128 state-read buffer 2 (seller or admin) // 0x300 2 status byte buffer // 0x310 256 scratch (buildField key workspace) // // Constants at 0x500+: // 0x500 5 "admin" // 0x506 13 "initialized: " // 0x514 10 "created: " // 0x51E 10 "released: " // 0x529 10 "refunded: " // 0x534 10 "disputed: " // 0x53F 10 "resolved: " // 0x54A 14 "unauthorized: " // 0x559 11 "not found: " // 0x565 14 "already init: " // 0x574 12 "not active: " // 0x581 14 "not disputed: " // 0x590 7 "buyer: " // 0x598 8 "seller: " // 0x5A1 8 "status: " // 0x5AA 7 "active" // 0x5B1 9 "disputed" // 0x5BB 9 "released" // 0x5C5 8 "refunded" // 0x5CE 6 "buyer" (for resolve winner comparison) // 0x5D4 6 "seller" const ( offArg0 int32 = 0x000 offArg1 int32 = 0x040 offCaller int32 = 0x140 offTreasury int32 = 0x1C0 offRead1 int32 = 0x200 offRead2 int32 = 0x280 offStatBuf int32 = 0x300 offScratch int32 = 0x310 offKeyAdmin int32 = 0x500 offInitedPfx int32 = 0x506 offCreatedPfx int32 = 0x514 offReleasedPfx int32 = 0x51E offRefundedPfx int32 = 0x529 offDisputedPfx int32 = 0x534 offResolvedPfx int32 = 0x53F offUnauthPfx int32 = 0x54A offNotFoundPfx int32 = 0x559 offAlreadyPfx int32 = 0x565 offNotActivePfx int32 = 0x574 offNotDispPfx int32 = 0x581 offBuyerPfx int32 = 0x590 offSellerPfx int32 = 0x598 offStatusPfx int32 = 0x5A1 offStrActive int32 = 0x5AA offStrDisputed int32 = 0x5B1 offStrReleased int32 = 0x5BB offStrRefunded int32 = 0x5C5 offStrBuyer int32 = 0x5CE offStrSeller int32 = 0x5D4 ) // ── Import / function indices ───────────────────────────────────────────────── const ( fnGetArgStr = 0 fnGetArgU64 = 1 fnGetCaller = 2 fnGetState = 3 fnSetState = 4 fnLog = 5 fnTransfer = 6 fnGetContractTreasury = 7 fnPutU64 = 8 fnGetU64 = 9 fnBytesEqual = 10 fnMemcpy = 11 fnLogPrefix = 12 fnBuildField = 13 // (idOff, idLen, fieldChar i32) → keyLen i32 fnInit = 14 fnCreate = 15 fnRelease = 16 fnRefund = 17 fnDispute = 18 fnResolve = 19 fnInfo = 20 ) // ── Helper bodies ───────────────────────────────────────────────────────────── func bytesEqualBody() []byte { return funcBody( withLocals(localDecl(2, tI32)), ic32(1), lset(4), ic32(0), lset(3), block_(), loop_(), lget(3), lget(2), i32GeU(), brIf_(1), lget(0), lget(3), i32Add(), i32Load8U(), lget(1), lget(3), i32Add(), i32Load8U(), i32Ne(), if_(), ic32(0), lset(4), br_(2), end_(), lget(3), ic32(1), i32Add(), lset(3), br_(0), end_(), end_(), lget(4), ) } func memcpyBody() []byte { return funcBody( withLocals(localDecl(1, tI32)), ic32(0), lset(3), block_(), loop_(), lget(3), lget(2), i32GeU(), brIf_(1), lget(0), lget(3), i32Add(), lget(1), lget(3), i32Add(), i32Load8U(), i32Store8(), lget(3), ic32(1), i32Add(), lset(3), br_(0), end_(), end_(), ) } func logPrefixBody() []byte { // (prefixPtr, prefixLen, suffixPtr, suffixLen i32) return funcBody( noLocals, ic32(offScratch), lget(0), lget(1), call(fnMemcpy), ic32(offScratch), lget(1), i32Add(), lget(2), lget(3), call(fnMemcpy), ic32(offScratch), lget(1), lget(3), i32Add(), call(fnLog), ) } // buildField(idOff, idLen, fieldChar i32) → keyLen i32 // Writes "e::" into offScratch. func buildFieldBody() []byte { return funcBody( noLocals, ic32(offScratch), ic32('e'), i32Store8(), ic32(offScratch+1), ic32(':'), i32Store8(), ic32(offScratch+2), lget(0), lget(1), call(fnMemcpy), ic32(offScratch+2), lget(1), i32Add(), ic32(':'), i32Store8(), ic32(offScratch+3), lget(1), i32Add(), lget(2), i32Store8(), lget(1), ic32(4), i32Add(), ) } // isCallerAdminCode: reads admin into offRead2, checks caller == admin. // Params: callerLen local index, adminLen local index. // Returns inline code that leaves i32 (1=is admin) on stack. func isCallerAdminCode(callerLenLocal, adminLenLocal uint32) []byte { return cat( ic32(offKeyAdmin), ic32(5), ic32(offRead2), ic32(128), call(fnGetState), lset(adminLenLocal), lget(adminLenLocal), i32Eqz(), ifI32(), ic32(0), else_(), lget(callerLenLocal), lget(adminLenLocal), i32Ne(), ifI32(), ic32(0), else_(), ic32(offCaller), ic32(offRead2), lget(callerLenLocal), call(fnBytesEqual), end_(), end_(), ) } // ── Contract method bodies ──────────────────────────────────────────────────── // init() — set caller as admin // Locals: callerLen(0), existingLen(1) func initBody() []byte { return funcBody( withLocals(localDecl(2, tI32)), ic32(offCaller), ic32(128), call(fnGetCaller), lset(0), ic32(offKeyAdmin), ic32(5), ic32(offRead2), ic32(128), call(fnGetState), lset(1), lget(1), ic32(0), i32GtU(), if_(), ic32(offAlreadyPfx), ic32(14), ic32(offCaller), lget(0), call(fnLogPrefix), return_(), end_(), ic32(offKeyAdmin), ic32(5), ic32(offCaller), lget(0), call(fnSetState), ic32(offInitedPfx), ic32(13), ic32(offCaller), lget(0), call(fnLogPrefix), ) } // create(id, seller, amount u64) // Locals: idLen(0), sellerLen(1), callerLen(2), treasuryLen(3), keyLen(4) [i32] // amount(5) [i64] func createBody() []byte { return funcBody( withLocals(localDecl(5, tI32), localDecl(1, tI64)), ic32(0), ic32(offArg0), ic32(64), call(fnGetArgStr), lset(0), lget(0), i32Eqz(), if_(), return_(), end_(), ic32(1), ic32(offArg1), ic32(128), call(fnGetArgStr), lset(1), lget(1), i32Eqz(), if_(), return_(), end_(), ic32(2), call(fnGetArgU64), lset(5), // Check id not already used ic32(offArg0), lget(0), ic32('x'), call(fnBuildField), lset(4), ic32(offScratch), lget(4), ic32(offStatBuf), ic32(2), call(fnGetState), ic32(0), i32GtU(), if_(), ic32(offNotFoundPfx), ic32(11), ic32(offArg0), lget(0), call(fnLogPrefix), return_(), end_(), // Get caller (buyer) and treasury ic32(offCaller), ic32(128), call(fnGetCaller), lset(2), ic32(offTreasury), ic32(64), call(fnGetContractTreasury), lset(3), // Transfer amount: buyer → treasury ic32(offCaller), lget(2), ic32(offTreasury), lget(3), lget(5), call(fnTransfer), drop(), // Write buyer: e::b → caller ic32(offArg0), lget(0), ic32('b'), call(fnBuildField), lset(4), ic32(offScratch), lget(4), ic32(offCaller), lget(2), call(fnSetState), // Write seller: e::s → arg1 ic32(offArg0), lget(0), ic32('s'), call(fnBuildField), lset(4), ic32(offScratch), lget(4), ic32(offArg1), lget(1), call(fnSetState), // Write amount: e::v ic32(offArg0), lget(0), ic32('v'), call(fnBuildField), lset(4), ic32(offScratch), lget(4), lget(5), call(fnPutU64), // Write status = 'a': e::x ic32(offStatBuf), ic32('a'), i32Store8(), ic32(offArg0), lget(0), ic32('x'), call(fnBuildField), lset(4), ic32(offScratch), lget(4), ic32(offStatBuf), ic32(1), call(fnSetState), ic32(offCreatedPfx), ic32(9), ic32(offArg0), lget(0), call(fnLogPrefix), ) } // checkActiveStatus: reads status byte into offStatBuf; returns inline code // that returns with an error log if status != expectedChar. func checkStatus(idLenLocal uint32, expectedChar int32, errPfxOff int32, errPfxLen int32) []byte { return cat( ic32(offArg0), lget(idLenLocal), ic32('x'), call(fnBuildField), // keyLen on stack — but we need it in a local. Use the call result directly: // Actually we need to save it. This is getting complex. Let me inline differently. // Instead, we'll read status without saving keyLen — just check immediately. // Actually the fn returns keyLen on stack. We need (keyPtr, keyLen, dstPtr, dstLen) for get_state. // So: ic32(offScratch), [keyLen], ic32(offStatBuf), ic32(2), call(fnGetState) // But keyLen is already on the stack after buildField! // We can do: ic32(offScratch), , ... // No wait, stack ordering: we push ic32(offScratch) BEFORE the keyLen. // For get_state(keyPtr, keyLen, dstPtr, dstLen): // We need: keyPtr first, then keyLen. // buildField leaves keyLen on stack, but we need keyPtr first. // This is the fundamental issue: the keyPtr (offScratch) needs to be pushed before keyLen. // // Fix: save keyLen to a local first, then push offScratch, keyLen. // But we don't have a spare local in this helper... // // For now, let me just not use this helper function and inline the check in each method. ic32(0), // placeholder - don't use this helper ) } // release(id) — buyer releases funds to seller // Locals: idLen(0), buyerLen(1), sellerLen(2), callerLen(3), treasuryLen(4), keyLen(5) [i32] // amount(6) [i64] func releaseBody() []byte { return funcBody( withLocals(localDecl(6, tI32), localDecl(1, tI64)), ic32(0), ic32(offArg0), ic32(64), call(fnGetArgStr), lset(0), lget(0), i32Eqz(), if_(), return_(), end_(), // Check status == 'a' ic32(offArg0), lget(0), ic32('x'), call(fnBuildField), lset(5), ic32(offScratch), lget(5), ic32(offStatBuf), ic32(2), call(fnGetState), ic32(0), i32Ne(), if_(), ic32(offStatBuf), i32Load8U(), ic32('a'), i32Ne(), if_(), ic32(offNotActivePfx), ic32(12), ic32(offArg0), lget(0), call(fnLogPrefix), return_(), end_(), else_(), ic32(offNotFoundPfx), ic32(11), ic32(offArg0), lget(0), call(fnLogPrefix), return_(), end_(), // Check caller == buyer ic32(offCaller), ic32(128), call(fnGetCaller), lset(3), ic32(offArg0), lget(0), ic32('b'), call(fnBuildField), lset(5), ic32(offScratch), lget(5), ic32(offRead1), ic32(128), call(fnGetState), lset(1), lget(3), lget(1), i32Ne(), if_(), ic32(offUnauthPfx), ic32(14), ic32(offArg0), lget(0), call(fnLogPrefix), return_(), end_(), ic32(offCaller), ic32(offRead1), lget(3), call(fnBytesEqual), i32Eqz(), if_(), ic32(offUnauthPfx), ic32(14), ic32(offArg0), lget(0), call(fnLogPrefix), return_(), end_(), // Get seller, treasury, amount ic32(offArg0), lget(0), ic32('s'), call(fnBuildField), lset(5), ic32(offScratch), lget(5), ic32(offRead2), ic32(128), call(fnGetState), lset(2), ic32(offTreasury), ic32(64), call(fnGetContractTreasury), lset(4), ic32(offArg0), lget(0), ic32('v'), call(fnBuildField), lset(5), ic32(offScratch), lget(5), call(fnGetU64), lset(6), // Transfer: treasury → seller ic32(offTreasury), lget(4), ic32(offRead2), lget(2), lget(6), call(fnTransfer), drop(), // Mark status = 'r' ic32(offStatBuf), ic32('r'), i32Store8(), ic32(offArg0), lget(0), ic32('x'), call(fnBuildField), lset(5), ic32(offScratch), lget(5), ic32(offStatBuf), ic32(1), call(fnSetState), ic32(offReleasedPfx), ic32(10), ic32(offArg0), lget(0), call(fnLogPrefix), ) } // refund(id) — seller voluntarily refunds buyer // Locals: idLen(0), sellerLen(1), buyerLen(2), callerLen(3), treasuryLen(4), keyLen(5) [i32] // amount(6) [i64] func refundBody() []byte { return funcBody( withLocals(localDecl(6, tI32), localDecl(1, tI64)), ic32(0), ic32(offArg0), ic32(64), call(fnGetArgStr), lset(0), lget(0), i32Eqz(), if_(), return_(), end_(), // Check status == 'a' ic32(offArg0), lget(0), ic32('x'), call(fnBuildField), lset(5), ic32(offScratch), lget(5), ic32(offStatBuf), ic32(2), call(fnGetState), ic32(0), i32Ne(), if_(), ic32(offStatBuf), i32Load8U(), ic32('a'), i32Ne(), if_(), ic32(offNotActivePfx), ic32(12), ic32(offArg0), lget(0), call(fnLogPrefix), return_(), end_(), else_(), ic32(offNotFoundPfx), ic32(11), ic32(offArg0), lget(0), call(fnLogPrefix), return_(), end_(), // Check caller == seller ic32(offCaller), ic32(128), call(fnGetCaller), lset(3), ic32(offArg0), lget(0), ic32('s'), call(fnBuildField), lset(5), ic32(offScratch), lget(5), ic32(offRead1), ic32(128), call(fnGetState), lset(1), lget(3), lget(1), i32Ne(), if_(), ic32(offUnauthPfx), ic32(14), ic32(offArg0), lget(0), call(fnLogPrefix), return_(), end_(), ic32(offCaller), ic32(offRead1), lget(3), call(fnBytesEqual), i32Eqz(), if_(), ic32(offUnauthPfx), ic32(14), ic32(offArg0), lget(0), call(fnLogPrefix), return_(), end_(), // Get buyer, treasury, amount ic32(offArg0), lget(0), ic32('b'), call(fnBuildField), lset(5), ic32(offScratch), lget(5), ic32(offRead2), ic32(128), call(fnGetState), lset(2), ic32(offTreasury), ic32(64), call(fnGetContractTreasury), lset(4), ic32(offArg0), lget(0), ic32('v'), call(fnBuildField), lset(5), ic32(offScratch), lget(5), call(fnGetU64), lset(6), // Transfer: treasury → buyer ic32(offTreasury), lget(4), ic32(offRead2), lget(2), lget(6), call(fnTransfer), drop(), // Mark status = 'f' ic32(offStatBuf), ic32('f'), i32Store8(), ic32(offArg0), lget(0), ic32('x'), call(fnBuildField), lset(5), ic32(offScratch), lget(5), ic32(offStatBuf), ic32(1), call(fnSetState), ic32(offRefundedPfx), ic32(10), ic32(offArg0), lget(0), call(fnLogPrefix), ) } // dispute(id) — buyer or seller raises a dispute // Locals: idLen(0), callerLen(1), buyerLen(2), sellerLen(3), keyLen(4) [i32] func disputeBody() []byte { return funcBody( withLocals(localDecl(5, tI32)), ic32(0), ic32(offArg0), ic32(64), call(fnGetArgStr), lset(0), lget(0), i32Eqz(), if_(), return_(), end_(), // Check status == 'a' ic32(offArg0), lget(0), ic32('x'), call(fnBuildField), lset(4), ic32(offScratch), lget(4), ic32(offStatBuf), ic32(2), call(fnGetState), ic32(0), i32Ne(), if_(), ic32(offStatBuf), i32Load8U(), ic32('a'), i32Ne(), if_(), ic32(offNotActivePfx), ic32(12), ic32(offArg0), lget(0), call(fnLogPrefix), return_(), end_(), else_(), ic32(offNotFoundPfx), ic32(11), ic32(offArg0), lget(0), call(fnLogPrefix), return_(), end_(), // Caller must be buyer or seller ic32(offCaller), ic32(128), call(fnGetCaller), lset(1), ic32(offArg0), lget(0), ic32('b'), call(fnBuildField), lset(4), ic32(offScratch), lget(4), ic32(offRead1), ic32(128), call(fnGetState), lset(2), ic32(offArg0), lget(0), ic32('s'), call(fnBuildField), lset(4), ic32(offScratch), lget(4), ic32(offRead2), ic32(128), call(fnGetState), lset(3), // isBuyer = callerLen==buyerLen && bytes_equal(caller, buyer, callerLen) // isSeller = callerLen==sellerLen && bytes_equal(caller, seller, callerLen) // if !isBuyer && !isSeller: unauthorized lget(1), lget(2), i32Ne(), if_(), // callerLen != buyerLen → check seller lget(1), lget(3), i32Ne(), if_(), ic32(offUnauthPfx), ic32(14), ic32(offArg0), lget(0), call(fnLogPrefix), return_(), end_(), ic32(offCaller), ic32(offRead2), lget(1), call(fnBytesEqual), i32Eqz(), if_(), ic32(offUnauthPfx), ic32(14), ic32(offArg0), lget(0), call(fnLogPrefix), return_(), end_(), else_(), // callerLen == buyerLen → check bytes ic32(offCaller), ic32(offRead1), lget(1), call(fnBytesEqual), i32Eqz(), if_(), // not buyer — check seller lget(1), lget(3), i32Ne(), if_(), ic32(offUnauthPfx), ic32(14), ic32(offArg0), lget(0), call(fnLogPrefix), return_(), end_(), ic32(offCaller), ic32(offRead2), lget(1), call(fnBytesEqual), i32Eqz(), if_(), ic32(offUnauthPfx), ic32(14), ic32(offArg0), lget(0), call(fnLogPrefix), return_(), end_(), end_(), end_(), // Mark status = 'd' ic32(offStatBuf), ic32('d'), i32Store8(), ic32(offArg0), lget(0), ic32('x'), call(fnBuildField), lset(4), ic32(offScratch), lget(4), ic32(offStatBuf), ic32(1), call(fnSetState), ic32(offDisputedPfx), ic32(10), ic32(offArg0), lget(0), call(fnLogPrefix), ) } // resolve(id, winner) — admin resolves disputed escrow // winner arg must be "buyer" or "seller" // Locals: idLen(0), winnerLen(1), callerLen(2), adminLen(3), // recipientLen(4), treasuryLen(5), keyLen(6) [i32] // amount(7) [i64] func resolveBody() []byte { return funcBody( withLocals(localDecl(7, tI32), localDecl(1, tI64)), ic32(0), ic32(offArg0), ic32(64), call(fnGetArgStr), lset(0), lget(0), i32Eqz(), if_(), return_(), end_(), ic32(1), ic32(offArg1), ic32(128), call(fnGetArgStr), lset(1), lget(1), i32Eqz(), if_(), return_(), end_(), // Check status == 'd' ic32(offArg0), lget(0), ic32('x'), call(fnBuildField), lset(6), ic32(offScratch), lget(6), ic32(offStatBuf), ic32(2), call(fnGetState), ic32(0), i32Ne(), if_(), ic32(offStatBuf), i32Load8U(), ic32('d'), i32Ne(), if_(), ic32(offNotDispPfx), ic32(14), ic32(offArg0), lget(0), call(fnLogPrefix), return_(), end_(), else_(), ic32(offNotFoundPfx), ic32(11), ic32(offArg0), lget(0), call(fnLogPrefix), return_(), end_(), // Check caller is admin ic32(offCaller), ic32(128), call(fnGetCaller), lset(2), isCallerAdminCode(2, 3), i32Eqz(), if_(), ic32(offUnauthPfx), ic32(14), ic32(offArg0), lget(0), call(fnLogPrefix), return_(), end_(), // Determine recipient based on winner arg // winner == "buyer" (5 bytes) → recipient = buyer address // winner == "seller" (6 bytes) → recipient = seller address ic32(offTreasury), ic32(64), call(fnGetContractTreasury), lset(5), ic32(offArg0), lget(0), ic32('v'), call(fnBuildField), lset(6), ic32(offScratch), lget(6), call(fnGetU64), lset(7), // Compare winner to "buyer" lget(1), ic32(5), i32Ne(), if_(), // not "buyer" — assume "seller" ic32(offArg0), lget(0), ic32('s'), call(fnBuildField), lset(6), ic32(offScratch), lget(6), ic32(offRead1), ic32(128), call(fnGetState), lset(4), else_(), // might be "buyer" — verify bytes ic32(offArg1), ic32(offStrBuyer), ic32(5), call(fnBytesEqual), i32Eqz(), if_(), // not "buyer" bytes — default to seller ic32(offArg0), lget(0), ic32('s'), call(fnBuildField), lset(6), ic32(offScratch), lget(6), ic32(offRead1), ic32(128), call(fnGetState), lset(4), else_(), ic32(offArg0), lget(0), ic32('b'), call(fnBuildField), lset(6), ic32(offScratch), lget(6), ic32(offRead1), ic32(128), call(fnGetState), lset(4), end_(), end_(), // Transfer: treasury → recipient ic32(offTreasury), lget(5), ic32(offRead1), lget(4), lget(7), call(fnTransfer), drop(), // Mark status = 'r' (released, settled) ic32(offStatBuf), ic32('r'), i32Store8(), ic32(offArg0), lget(0), ic32('x'), call(fnBuildField), lset(6), ic32(offScratch), lget(6), ic32(offStatBuf), ic32(1), call(fnSetState), ic32(offResolvedPfx), ic32(10), ic32(offArg0), lget(0), call(fnLogPrefix), ) } // info(id) — log escrow details // Locals: idLen(0), buyerLen(1), sellerLen(2), keyLen(3) [i32] func infoBody() []byte { return funcBody( withLocals(localDecl(4, tI32)), ic32(0), ic32(offArg0), ic32(64), call(fnGetArgStr), lset(0), lget(0), i32Eqz(), if_(), return_(), end_(), // Check exists ic32(offArg0), lget(0), ic32('x'), call(fnBuildField), lset(3), ic32(offScratch), lget(3), ic32(offStatBuf), ic32(2), call(fnGetState), ic32(0), i32Ne(), i32Eqz(), if_(), ic32(offNotFoundPfx), ic32(11), ic32(offArg0), lget(0), call(fnLogPrefix), return_(), end_(), // Log buyer ic32(offArg0), lget(0), ic32('b'), call(fnBuildField), lset(3), ic32(offScratch), lget(3), ic32(offRead1), ic32(128), call(fnGetState), lset(1), ic32(offBuyerPfx), ic32(7), ic32(offRead1), lget(1), call(fnLogPrefix), // Log seller ic32(offArg0), lget(0), ic32('s'), call(fnBuildField), lset(3), ic32(offScratch), lget(3), ic32(offRead2), ic32(128), call(fnGetState), lset(2), ic32(offSellerPfx), ic32(8), ic32(offRead2), lget(2), call(fnLogPrefix), // Log status ic32(offStatBuf), i32Load8U(), ic32('a'), i32Ne(), if_(), ic32(offStatBuf), i32Load8U(), ic32('d'), i32Ne(), if_(), ic32(offStatBuf), i32Load8U(), ic32('r'), i32Ne(), if_(), ic32(offStatusPfx), ic32(8), ic32(offStrRefunded), ic32(8), call(fnLogPrefix), else_(), ic32(offStatusPfx), ic32(8), ic32(offStrReleased), ic32(8), call(fnLogPrefix), end_(), else_(), ic32(offStatusPfx), ic32(8), ic32(offStrDisputed), ic32(8), call(fnLogPrefix), end_(), else_(), ic32(offStatusPfx), ic32(8), ic32(offStrActive), ic32(6), call(fnLogPrefix), end_(), ) } // ── main ────────────────────────────────────────────────────────────────────── func main() { // Types: // 0: (i32,i32,i32)→(i32) get_arg_str, bytes_equal, buildField // 1: (i32)→(i64) get_arg_u64 // 2: (i32,i32,i32,i32)→(i32) get_state // 3: (i32,i32,i32,i32)→() set_state, log_prefix // 4: (i32,i32)→() log // 5: (i32,i32,i32,i32,i64)→(i32) transfer // 6: (i32,i32)→(i32) get_caller, get_contract_treasury // 7: (i32,i32,i64)→() put_u64 // 8: (i32,i32)→(i64) get_u64 // 9: ()→() exported methods // 10: (i32,i32,i32)→() memcpy typeSection := section(0x01, vec( functype([]byte{tI32, tI32, tI32}, []byte{tI32}), // 0 functype([]byte{tI32}, []byte{tI64}), // 1 functype([]byte{tI32, tI32, tI32, tI32}, []byte{tI32}), // 2 functype([]byte{tI32, tI32, tI32, tI32}, []byte{}), // 3 functype([]byte{tI32, tI32}, []byte{}), // 4 functype([]byte{tI32, tI32, tI32, tI32, tI64}, []byte{tI32}), // 5 functype([]byte{tI32, tI32}, []byte{tI32}), // 6 functype([]byte{tI32, tI32, tI64}, []byte{}), // 7 functype([]byte{tI32, tI32}, []byte{tI64}), // 8 functype([]byte{}, []byte{}), // 9 functype([]byte{tI32, tI32, tI32}, []byte{}), // 10 )) importSection := section(0x02, vec( importFunc("env", "get_arg_str", 0), // 0 importFunc("env", "get_arg_u64", 1), // 1 importFunc("env", "get_caller", 6), // 2 importFunc("env", "get_state", 2), // 3 importFunc("env", "set_state", 3), // 4 importFunc("env", "log", 4), // 5 importFunc("env", "transfer", 5), // 6 importFunc("env", "get_contract_treasury", 6), // 7 importFunc("env", "put_u64", 7), // 8 importFunc("env", "get_u64", 8), // 9 )) // 11 local functions functionSection := section(0x03, vec( u(0), // bytes_equal type 0 u(10), // memcpy type 10 u(3), // log_prefix type 3 u(0), // build_field type 0 u(9), // init type 9 u(9), // create type 9 u(9), // release type 9 u(9), // refund type 9 u(9), // dispute type 9 u(9), // resolve type 9 u(9), // info type 9 )) memorySection := section(0x05, vec(cat([]byte{0x00}, u(2)))) // 2 pages exportSection := section(0x07, vec( exportEntry("memory", 0x02, 0), exportEntry("init", 0x00, fnInit), exportEntry("create", 0x00, fnCreate), exportEntry("release", 0x00, fnRelease), exportEntry("refund", 0x00, fnRefund), exportEntry("dispute", 0x00, fnDispute), exportEntry("resolve", 0x00, fnResolve), exportEntry("info", 0x00, fnInfo), )) dataSection := section(0x0B, cat( u(21), dataSegment(offKeyAdmin, []byte("admin")), dataSegment(offInitedPfx, []byte("initialized: ")), dataSegment(offCreatedPfx, []byte("created: ")), dataSegment(offReleasedPfx, []byte("released: ")), dataSegment(offRefundedPfx, []byte("refunded: ")), dataSegment(offDisputedPfx, []byte("disputed: ")), dataSegment(offResolvedPfx, []byte("resolved: ")), dataSegment(offUnauthPfx, []byte("unauthorized: ")), dataSegment(offNotFoundPfx, []byte("not found: ")), dataSegment(offAlreadyPfx, []byte("already init: ")), dataSegment(offNotActivePfx,[]byte("not active: ")), dataSegment(offNotDispPfx, []byte("not disputed: ")), dataSegment(offBuyerPfx, []byte("buyer: ")), dataSegment(offSellerPfx, []byte("seller: ")), dataSegment(offStatusPfx, []byte("status: ")), dataSegment(offStrActive, []byte("active")), dataSegment(offStrDisputed, []byte("disputed")), dataSegment(offStrReleased, []byte("released")), dataSegment(offStrRefunded, []byte("refunded")), dataSegment(offStrBuyer, []byte("buyer")), dataSegment(offStrSeller, []byte("seller")), )) codeSection := section(0x0A, cat( u(11), bytesEqualBody(), memcpyBody(), logPrefixBody(), buildFieldBody(), initBody(), createBody(), releaseBody(), refundBody(), disputeBody(), resolveBody(), infoBody(), )) module := cat( []byte{0x00, 0x61, 0x73, 0x6d}, []byte{0x01, 0x00, 0x00, 0x00}, typeSection, importSection, functionSection, memorySection, exportSection, dataSection, codeSection, ) out := "contracts/escrow/escrow.wasm" if err := os.WriteFile(out, module, 0644); err != nil { fmt.Fprintln(os.Stderr, "write:", err) os.Exit(1) } fmt.Printf("Written %s (%d bytes)\n", out, len(module)) }