Status: complete, 2026-08-06. Confirms a Go service can build, sign, and submit Stellar transactions against the K01 contract end to end — using the Stellar Go SDK directly (github.com/stellar/go), no soroban-cli, no shelling out to another process. This is what services/core-go will actually do in Phase 1.
Deploys a fresh instance of K01's already-compiled wasm (../k01-soroban-escrow/target/wasm32v1-none/release/k01_soroban_escrow.wasm, reused as-is, not rebuilt) and drives it through the same lifecycle K01 validated, entirely from Go: upload wasm → create contract → initialize → fund → approve → NEG-1 → release → NEG-2.
Result contract: CC76XEKP5AI6AL7TX7O2XKXBD5A5HOUE4C6L36OVNO7XOKCSGWLDTCE6.
| # | Check | Result |
|---|---|---|
| 1 | Go can upload contract wasm, hand-building the InvokeHostFunction XDR operation itself |
✅ PASS |
| 2 | Go can create a fresh contract instance (CreateContract, address-based ContractIdPreimage + random salt) and recover the resulting contract address from the simulation's return value |
✅ PASS |
| 3 | Go can drive the full lifecycle — initialize, fund, approve, release — including the simulate → attach footprint/fee/auth → sign → submit → poll cycle for each call |
✅ PASS |
| 4 | Go-signed unauthorized calls are rejected the same way CLI-signed ones are | ✅ PASS — see NEG-1 |
| NEG-1 | Organizer attempts release directly, signed and submitted by Go |
✅ rejected on-chain (transaction included in the ledger but failed require_auth) — a stronger check than K01's CLI-level rejection, since this one actually reached the ledger's own auth enforcement, not just a local "missing signing key" short-circuit |
| NEG-2 | Release the same escrow twice, via Go | ✅ rejected — simulation itself failed with HostError: Error(Contract, #7) = Error::AlreadyReleased, same contract-level guarantee K01 confirmed |
- The Go SDK has no high-level "invoke a contract" helper —
txnbuild.InvokeHostFunctionexists, but buildingHostFunction/CreateContractArgs/ScValvalues is manual, low-level XDR construction (github.com/stellar/go/xdr). Budget for this in E02 (Go EscrowClient) — it's not a thin wrapper over a convenience API, it's real integration work. - The simulate → sign → submit → poll pattern has to be hand-rolled too.
clients/rpcclient(package nameclient, despite the directory beingrpcclient) exposesSimulateTransaction/SendTransaction/GetTransaction/LoadAccountas raw JSON-RPC calls — there's no "preflight and assemble" helper. The pattern that worked: build an unsigned tx with emptyAuth/Ext, simulate it, parseTransactionDataXDRintoxdr.SorobanTransactionDataand eachResults[0].AuthXDRentry intoxdr.SorobanAuthorizationEntry, setSorobanData.ResourceFee = MinResourceFee, rebuild the tx with those attached (txnbuild.NewTransactionauto-sums the classic + resource fee onceExt.SorobanDatais set), sign, submit, pollGetTransactionforSUCCESS/FAILED. Addressarguments need to handle both G- and C- prefixed strkeys. Soroban'sAddresstype is polymorphic over accounts and contracts; naively assuming every address argument is aG...account address broke on thetokenargument toinitialize(aC...contract address) — seeaddressArg(), which triesAddressToAccountIdfirst and falls back to a contract address.- A contract's
CreateContractreturn value is recoverable from the simulation response (Results[0].ReturnValueXDR, anScValof type Address) — no need to hand-derive the CAP-46 contract-id-from-preimage hash to know what address was just created.
Requirements: Go 1.26+, the K01 wasm already built (cd ../k01-soroban-escrow && cargo build --target wasm32v1-none --release).
# from spikes/k02-go-soroban/
go mod tidy
go run .No soroban-cli/stellar-cli needed for this spike — the whole point is that Go can do it independently.
K02 is done. Per server-build-plan.md's own sequencing, K03 (wallet compatibility check) and K04 (ADRs from K01–K03 findings) are next; S01 (monorepo scaffold) shouldn't start before K04 folds these findings in.